# HG changeset patch # User Mike Ruprecht # Date 1233834460 0 # Node ID f06eb6e7d907d2adacbb891a1c948b350227f911 # Parent dcff28a0415c835be274504b9212768fae450e0d# Parent 23038e1a175429bbd9068fa7f5d30c91ed50d1ee merge of '1cd039d2f04c877820b4097e462f26831c2d828e' and 'fd3a51d3135cc4ef80591280934a987837bd3a2e' diff -r 23038e1a1754 -r f06eb6e7d907 COPYRIGHT --- a/COPYRIGHT Thu Feb 05 11:47:36 2009 +0000 +++ b/COPYRIGHT Thu Feb 05 11:47:40 2009 +0000 @@ -314,6 +314,7 @@ Ted Percival Eduardo Pérez Matt Perry +Luke Petre Diego Petten Nathan Peterson Sebastián E. Peyrott diff -r 23038e1a1754 -r f06eb6e7d907 ChangeLog --- a/ChangeLog Thu Feb 05 11:47:36 2009 +0000 +++ b/ChangeLog Thu Feb 05 11:47:40 2009 +0000 @@ -2,17 +2,29 @@ version 2.5.5 (??/??/????): libpurple: + * Fix a crash when removing an account with an unknown protocol id. + * Beta support for SSL connections for AIM and ICQ accounts. To + enable, check the "Use SSL" option from the Advanced tab when + editing your AIM or ICQ account. (Paul Aurich) + * Fix a memory leak in SILC. (Luke Petre) + + ICQ: + * Fix retrieval of status messages from users of ICQ 6.x, Miranda, and + other libpurple clients. (Daniel Ljungborg) + * Change client ID to match ICQ Basic 14.34.3096. This fixes publishing + of buddy icons and available messages. + * Properly publish status messages for statuses other than Available. + ICQ 6.x users can now see these status messages. (Daniel Ljungborg) + + MSN: * Fix transfer of buddy icons, custom smileys, and files from the latest Windows Live Messenger 9 official client. (Thomas Gibson-Robinson) - * Fix a crash when removing an account with an unknown protocol id. - * Large (multi-part) messages on MSN are now correctly re-combined. - * Beta support for SSL connections for AIM and ICQ accounts. To - enable, check the "Use SSL" option from the Advanced tab when - editing your AIM or ICQ account. (Paul Aurich) - * Fix retrieval of ICQ status messages from users of ICQ 6.x, Miranda, - and other libpurple clients (fixes with libpurple users only on - statuses other than Available). (Daniel Ljungborg) + * Large (multi-part) messages are now correctly re-combined. + * Federated/Yahoo! buddies should now stop creating sync issues at + every signin. You may need to remove duplicates in the Address + Book. See the FAQ for more information. + * Messages from Yahoo! buddies are no longer silently dropped. Finch: * Allow rebinding keys to change the focused widget (details in the diff -r 23038e1a1754 -r f06eb6e7d907 ChangeLog.API --- a/ChangeLog.API Thu Feb 05 11:47:36 2009 +0000 +++ b/ChangeLog.API Thu Feb 05 11:47:40 2009 +0000 @@ -12,6 +12,8 @@ * purple_status_type_new now defaults "saveable" to TRUE. This was necessary in order to maintain the current behavior while fixing non-saveable statuses not to be saved. + * xmlnode_get_prefix, xmlnode_to_str and xmlnode_to_formatted_str + now all take a const xmlnode* instead of an xmlnode* version 2.5.4 (01/12/2009): perl: diff -r 23038e1a1754 -r f06eb6e7d907 ChangeLog.win32 --- a/ChangeLog.win32 Thu Feb 05 11:47:36 2009 +0000 +++ b/ChangeLog.win32 Thu Feb 05 11:47:40 2009 +0000 @@ -1,3 +1,8 @@ +version 2.5.5 (??/??/2009): + * Remove the "Flash window when chat messages are received" pref from + the Windows Pidgin Options plugin - the Message Notification plugin + does this (and much more). + version 2.5.4 (01/12/2009): * Fix the "Hang on Exit" issue that a number of users encountered. * Updated GTK+ to 2.14.6 diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/dbus-analyze-functions.py --- a/libpurple/dbus-analyze-functions.py Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/dbus-analyze-functions.py Thu Feb 05 11:47:40 2009 +0000 @@ -497,7 +497,7 @@ # This is a total hack, but self.call is set up before the parameters # are processed, so we can't tell it to pass a parameter by reference. self.call = "%s(%s)" % (self.function.name, - ", ".join(param.name if param.name != "len" else "&len" for param in self.params)) + ", ".join([(param.name, "&len")[param.name == "len"] for param in self.params])) self.cdecls.append("\tgconstpointer %s;" % name) self.ccode.append("\t%s = %s;" % (name, self.call)) diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/media.c --- a/libpurple/media.c Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/media.c Thu Feb 05 11:47:40 2009 +0000 @@ -75,8 +75,8 @@ gboolean candidates_prepared; - FsCandidate *local_candidate; - FsCandidate *remote_candidate; + GList *active_local_candidates; + GList *active_remote_candidates; gulong window_id; }; @@ -250,10 +250,10 @@ if (stream->remote_candidates) fs_candidate_list_destroy(stream->remote_candidates); - if (stream->local_candidate) - fs_candidate_destroy(stream->local_candidate); - if (stream->remote_candidate) - fs_candidate_destroy(stream->remote_candidate); + if (stream->active_local_candidates) + fs_candidate_list_destroy(stream->active_local_candidates); + if (stream->active_remote_candidates) + fs_candidate_list_destroy(stream->active_remote_candidates); g_free(stream); } @@ -1253,10 +1253,39 @@ } else if (gst_structure_has_name(msg->structure, "farsight-component-state-changed")) { - + FsStreamState fsstate = g_value_get_enum(gst_structure_get_value(msg->structure, "state")); + guint component = g_value_get_uint(gst_structure_get_value(msg->structure, "component")); + const gchar *state; + switch (fsstate) { + case FS_STREAM_STATE_FAILED: + state = "FAILED"; + break; + case FS_STREAM_STATE_DISCONNECTED: + state = "DISCONNECTED"; + break; + case FS_STREAM_STATE_GATHERING: + state = "GATHERING"; + break; + case FS_STREAM_STATE_CONNECTING: + state = "CONNECTING"; + break; + case FS_STREAM_STATE_CONNECTED: + state = "CONNECTED"; + break; + case FS_STREAM_STATE_READY: + state = "READY"; + break; + default: + state = "UNKNOWN"; + break; + } + purple_debug_info("media", "farsight-component-state-changed: component: %u state: %s\n", component, state); } else if (gst_structure_has_name(msg->structure, "farsight-send-codec-changed")) { - + FsCodec *codec = g_value_get_boxed(gst_structure_get_value(msg->structure, "codec")); + gchar *codec_str = fs_codec_to_string(codec); + purple_debug_info("media", "farsight-send-codec-changed: codec: %s\n", codec_str); + g_free(codec_str); } else if (gst_structure_has_name(msg->structure, "farsight-codecs-changed")) { GList *sessions = g_hash_table_get_values(PURPLE_MEDIA(media)->priv->sessions); @@ -1641,6 +1670,7 @@ gchar *name; FsParticipant *participant; PurpleMediaStream *stream; + GList *iter; g_return_if_fail(FS_IS_STREAM(fsstream)); g_return_if_fail(session != NULL); @@ -1651,8 +1681,41 @@ stream = purple_media_get_stream(session->media, session->id, name); - stream->local_candidate = fs_candidate_copy(native_candidate); - stream->remote_candidate = fs_candidate_copy(remote_candidate); + iter = stream->active_local_candidates; + for(; iter; iter = g_list_next(iter)) { + FsCandidate *c = iter->data; + if (native_candidate->component_id == c->component_id) { + fs_candidate_destroy(c); + stream->active_local_candidates = + g_list_delete_link(iter, iter); + stream->active_local_candidates = g_list_prepend( + stream->active_local_candidates, + fs_candidate_copy(native_candidate)); + break; + } + } + if (iter == NULL) + stream->active_local_candidates = g_list_prepend( + stream->active_local_candidates, + fs_candidate_copy(native_candidate)); + + iter = stream->active_remote_candidates; + for(; iter; iter = g_list_next(iter)) { + FsCandidate *c = iter->data; + if (native_candidate->component_id == c->component_id) { + fs_candidate_destroy(c); + stream->active_remote_candidates = + g_list_delete_link(iter, iter); + stream->active_remote_candidates = g_list_prepend( + stream->active_remote_candidates, + fs_candidate_copy(remote_candidate)); + break; + } + } + if (iter == NULL) + stream->active_remote_candidates = g_list_prepend( + stream->active_remote_candidates, + fs_candidate_copy(remote_candidate)); purple_debug_info("media", "candidate pair established\n"); } @@ -1739,7 +1802,14 @@ g_free(filename); if (err != NULL) { - purple_debug_error("media", "Error reading codec configuration file: %s\n", err->message); + if (err->code == 4) + purple_debug_info("media", "Couldn't read " + "fs-codec.conf: %s\n", + err->message); + else + purple_debug_error("media", "Error reading " + "fs-codec.conf: %s\n", + err->message); g_error_free(err); } @@ -1987,22 +2057,26 @@ } } -PurpleMediaCandidate * -purple_media_get_local_candidate(PurpleMedia *media, const gchar *sess_id, const gchar *name) +GList * +purple_media_get_active_local_candidates(PurpleMedia *media, + const gchar *sess_id, const gchar *name) { PurpleMediaStream *stream; g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); stream = purple_media_get_stream(media, sess_id, name); - return purple_media_candidate_from_fs(stream->local_candidate); + return purple_media_candidate_list_from_fs( + stream->active_local_candidates); } -PurpleMediaCandidate * -purple_media_get_remote_candidate(PurpleMedia *media, const gchar *sess_id, const gchar *name) +GList * +purple_media_get_active_remote_candidates(PurpleMedia *media, + const gchar *sess_id, const gchar *name) { PurpleMediaStream *stream; g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); stream = purple_media_get_stream(media, sess_id, name); - return purple_media_candidate_from_fs(stream->remote_candidate); + return purple_media_candidate_list_from_fs( + stream->active_remote_candidates); } gboolean @@ -2044,8 +2118,14 @@ for (; sessions; sessions = sessions->next) { const gchar *session = sessions->data; - if (!purple_media_get_local_candidate(media, session, name) || - !purple_media_get_remote_candidate(media, session, name)) + GList *local = purple_media_get_active_local_candidates( + media, session, name); + GList *remote = purple_media_get_active_remote_candidates( + media, session, name); + gboolean result = (local == NULL || remote == NULL); + purple_media_candidate_list_free(local); + purple_media_candidate_list_free(remote); + if (!result) return FALSE; } diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/media.h --- a/libpurple/media.h Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/media.h Thu Feb 05 11:47:40 2009 +0000 @@ -533,27 +533,27 @@ const gchar *name); /** - * Gets the active local candidate for the stream. + * Gets the active local candidates for the stream. * * @param media The media object to find the session in. * @param sess_id The session id of the session to find the stream in. * @param name The name of the remote user to get the active candidate from. * - * @return The active candidate retrieved. + * @return The active candidates retrieved. */ -PurpleMediaCandidate *purple_media_get_local_candidate(PurpleMedia *media, +GList *purple_media_get_active_local_candidates(PurpleMedia *media, const gchar *sess_id, const gchar *name); /** - * Gets the active remote candidate for the stream. + * Gets the active remote candidates for the stream. * * @param media The media object to find the session in. * @param sess_id The session id of the session to find the stream in. * @param name The name of the remote user to get the remote candidate from. * - * @return The remote candidate retrieved. + * @return The remote candidates retrieved. */ -PurpleMediaCandidate *purple_media_get_remote_candidate(PurpleMedia *media, +GList *purple_media_get_active_remote_candidates(PurpleMedia *media, const gchar *sess_id, const gchar *name); /** diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/mediamanager.c --- a/libpurple/mediamanager.c Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/mediamanager.c Thu Feb 05 11:47:40 2009 +0000 @@ -39,6 +39,12 @@ struct _PurpleMediaManagerPrivate { GList *medias; + GList *elements; + + PurpleMediaElementInfo *video_src; + PurpleMediaElementInfo *video_sink; + PurpleMediaElementInfo *audio_src; + PurpleMediaElementInfo *audio_sink; }; #define PURPLE_MEDIA_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_MANAGER, PurpleMediaManagerPrivate)) @@ -123,6 +129,8 @@ g_list_delete_link(priv->medias, priv->medias)) { g_object_unref(priv->medias->data); } + for (; priv->elements; priv->elements = + g_list_delete_link(priv->elements, priv->elements)); parent_class->finalize(media); } @@ -205,19 +213,22 @@ PurpleMediaSessionType type) { GstElement *ret = NULL; - GstElement *level = NULL; /* TODO: If src, retrieve current src */ /* TODO: Send a signal here to allow for overriding the source/sink */ - if (type & PURPLE_MEDIA_SEND_AUDIO) - purple_media_audio_init_src(&ret, &level); - else if (type & PURPLE_MEDIA_RECV_AUDIO) - purple_media_audio_init_recv(&ret, &level); - else if (type & PURPLE_MEDIA_SEND_VIDEO) - purple_media_video_init_src(&ret); - else if (type & PURPLE_MEDIA_RECV_VIDEO) - purple_media_video_init_recv(&ret); + if (type & PURPLE_MEDIA_SEND_AUDIO + && manager->priv->audio_src != NULL) + ret = manager->priv->audio_src->create(); + else if (type & PURPLE_MEDIA_RECV_AUDIO + && manager->priv->audio_sink != NULL) + ret = manager->priv->audio_sink->create(); + else if (type & PURPLE_MEDIA_SEND_VIDEO + && manager->priv->video_src != NULL) + ret = manager->priv->video_src->create(); + else if (type & PURPLE_MEDIA_RECV_VIDEO + && manager->priv->video_sink != NULL) + ret = manager->priv->video_sink->create(); if (ret == NULL) purple_debug_error("media", "Error creating source or sink\n"); @@ -225,4 +236,122 @@ return ret; } +PurpleMediaElementInfo * +purple_media_manager_get_element_info(PurpleMediaManager *manager, + const gchar *id) +{ + GList *iter; + + g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), NULL); + + iter = manager->priv->elements; + + for (; iter; iter = g_list_next(iter)) { + PurpleMediaElementInfo *info = iter->data; + if (!strcmp(info->id, id)) + return info; + } + + return NULL; +} + +gboolean +purple_media_manager_register_element(PurpleMediaManager *manager, + PurpleMediaElementInfo *info) +{ + g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE); + g_return_val_if_fail(info != NULL, FALSE); + + if (purple_media_manager_get_element_info(manager, info->id) != NULL) + return FALSE; + + manager->priv->elements = + g_list_prepend(manager->priv->elements, info); + return TRUE; +} + +gboolean +purple_media_manager_unregister_element(PurpleMediaManager *manager, + const gchar *id) +{ + PurpleMediaElementInfo *info; + + g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE); + + info = purple_media_manager_get_element_info(manager, id); + + if (info == NULL) + return FALSE; + + if (manager->priv->audio_src == info) + manager->priv->audio_src = NULL; + if (manager->priv->audio_sink == info) + manager->priv->audio_sink = NULL; + if (manager->priv->video_src == info) + manager->priv->video_src = NULL; + if (manager->priv->video_sink == info) + manager->priv->video_sink = NULL; + + manager->priv->elements = g_list_remove( + manager->priv->elements, info); + return TRUE; +} + +gboolean +purple_media_manager_set_active_element(PurpleMediaManager *manager, + PurpleMediaElementInfo *info) +{ + gboolean ret = FALSE; + + g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE); + g_return_val_if_fail(info != NULL, FALSE); + + if (purple_media_manager_get_element_info(manager, info->id) == NULL) + purple_media_manager_register_element(manager, info); + + if (info->type & PURPLE_MEDIA_ELEMENT_SRC) { + if (info->type & PURPLE_MEDIA_ELEMENT_AUDIO) { + manager->priv->audio_src = info; + ret = TRUE; + } + if (info->type & PURPLE_MEDIA_ELEMENT_VIDEO) { + manager->priv->video_src = info; + ret = TRUE; + } + } + if (info->type & PURPLE_MEDIA_ELEMENT_SINK) { + if (info->type & PURPLE_MEDIA_ELEMENT_AUDIO) { + manager->priv->audio_sink = info; + ret = TRUE; + } + if (info->type & PURPLE_MEDIA_ELEMENT_VIDEO) { + manager->priv->video_sink = info; + ret = TRUE; + } + } + + return ret; +} + +PurpleMediaElementInfo * +purple_media_manager_get_active_element(PurpleMediaManager *manager, + PurpleMediaElementType type) +{ + g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), NULL); + + if (type & PURPLE_MEDIA_ELEMENT_SRC) { + if (type & PURPLE_MEDIA_ELEMENT_AUDIO) + return manager->priv->audio_src; + else if (type & PURPLE_MEDIA_ELEMENT_VIDEO) + return manager->priv->video_src; + } else if (type & PURPLE_MEDIA_ELEMENT_SINK) { + if (type & PURPLE_MEDIA_ELEMENT_AUDIO) + return manager->priv->audio_sink; + else if (type & PURPLE_MEDIA_ELEMENT_VIDEO) + return manager->priv->video_sink; + } + + return NULL; +} + #endif /* USE_VV */ diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/mediamanager.h --- a/libpurple/mediamanager.h Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/mediamanager.h Thu Feb 05 11:47:40 2009 +0000 @@ -50,6 +50,8 @@ typedef struct _PurpleMediaManagerClass PurpleMediaManagerClass; /** @copydoc _PurpleMediaManagerPrivate */ typedef struct _PurpleMediaManagerPrivate PurpleMediaManagerPrivate; +/** @copydoc _PurpleMediaElementInfo */ +typedef struct _PurpleMediaElementInfo PurpleMediaElementInfo; /** The media manager class. */ struct _PurpleMediaManagerClass @@ -64,6 +66,37 @@ PurpleMediaManagerPrivate *priv; /**< Private data for the manager. */ }; +typedef enum { + PURPLE_MEDIA_ELEMENT_AUDIO = 1, /** supports audio */ + PURPLE_MEDIA_ELEMENT_VIDEO = 1 << 1, /** supports video */ + PURPLE_MEDIA_ELEMENT_AUDIO_VIDEO = PURPLE_MEDIA_ELEMENT_AUDIO + | PURPLE_MEDIA_ELEMENT_VIDEO, /** supports audio and video */ + + PURPLE_MEDIA_ELEMENT_NO_SRCS = 0, /** has no src pads */ + PURPLE_MEDIA_ELEMENT_ONE_SRC = 1 << 2, /** has one src pad */ + PURPLE_MEDIA_ELEMENT_MULTI_SRC = 1 << 3, /** has multiple src pads */ + PURPLE_MEDIA_ELEMENT_REQUEST_SRC = 1 << 4, /** src pads must be requested */ + + PURPLE_MEDIA_ELEMENT_NO_SINKS = 0, /** has no sink pads */ + PURPLE_MEDIA_ELEMENT_ONE_SINK = 1 << 5, /** has one sink pad */ + PURPLE_MEDIA_ELEMENT_MULTI_SINK = 1 << 6, /** has multiple sink pads */ + PURPLE_MEDIA_ELEMENT_REQUEST_SINK = 1 << 7, /** sink pads must be requested */ + + PURPLE_MEDIA_ELEMENT_UNIQUE = 1 << 8, /** This element is unique and + only one instance of it should + be created at a time */ + + PURPLE_MEDIA_ELEMENT_SRC = 1 << 9, /** can be set as an active src */ + PURPLE_MEDIA_ELEMENT_SINK = 1 << 10, /** can be set as an active sink */ +} PurpleMediaElementType; + +struct _PurpleMediaElementInfo +{ + const gchar *id; + PurpleMediaElementType type; + GstElement *(*create)(void); +}; + #ifdef __cplusplus extern "C" { #endif @@ -131,6 +164,16 @@ GstElement *purple_media_manager_get_element(PurpleMediaManager *manager, PurpleMediaSessionType type); +PurpleMediaElementInfo *purple_media_manager_get_element_info( + PurpleMediaManager *manager, const gchar *name); +gboolean purple_media_manager_register_element(PurpleMediaManager *manager, + PurpleMediaElementInfo *info); +gboolean purple_media_manager_unregister_element(PurpleMediaManager *manager, + const gchar *name); +gboolean purple_media_manager_set_active_element(PurpleMediaManager *manager, + PurpleMediaElementInfo *info); +PurpleMediaElementInfo *purple_media_manager_get_active_element( + PurpleMediaManager *manager, PurpleMediaElementType type); /*}@*/ #ifdef __cplusplus diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/network.c --- a/libpurple/network.c Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/network.c Thu Feb 05 11:47:40 2009 +0000 @@ -75,8 +75,12 @@ /* Mutex for the other global vars */ static GStaticMutex mutex = G_STATIC_MUTEX_INIT; -static gboolean network_initialized; -static HANDLE network_change_handle; +static gboolean network_initialized = FALSE; +static HANDLE network_change_handle = NULL; +static int (WSAAPI *MyWSANSPIoctl) ( + HANDLE hLookup, DWORD dwControlCode, LPVOID lpvInBuffer, + DWORD cbInBuffer, LPVOID lpvOutBuffer, DWORD cbOutBuffer, + LPDWORD lpcbBytesReturned, LPWSACOMPLETION lpCompletion) = NULL; #endif struct _PurpleNetworkListenData { @@ -543,27 +547,28 @@ return FALSE; } +static gboolean _print_debug_msg(gpointer data) { + gchar *msg = data; + purple_debug_warning("network", msg); + g_free(msg); + return FALSE; +} + static gpointer wpurple_network_change_thread(gpointer data) { WSAQUERYSET qs; WSAEVENT *nla_event; - time_t last_trigger = time(NULL); - - int (WSAAPI *MyWSANSPIoctl) ( - HANDLE hLookup, DWORD dwControlCode, LPVOID lpvInBuffer, - DWORD cbInBuffer, LPVOID lpvOutBuffer, DWORD cbOutBuffer, - LPDWORD lpcbBytesReturned, LPWSACOMPLETION lpCompletion) = NULL; - - if (!(MyWSANSPIoctl = (void*) wpurple_find_and_loadproc("ws2_32.dll", "WSANSPIoctl"))) { - g_thread_exit(NULL); - return NULL; - } + time_t last_trigger = time(NULL) - 31; + char buf[4096]; + WSAQUERYSET *res = (LPWSAQUERYSET) buf; + DWORD size; if ((nla_event = WSACreateEvent()) == WSA_INVALID_EVENT) { int errorid = WSAGetLastError(); gchar *msg = g_win32_error_message(errorid); - purple_debug_warning("network", "Couldn't create WSA event. " - "Message: %s (%d).\n", msg, errorid); + purple_timeout_add(0, _print_debug_msg, + g_strdup_printf("Couldn't create WSA event. " + "Message: %s (%d).\n", msg, errorid)); g_free(msg); g_thread_exit(NULL); return NULL; @@ -584,30 +589,26 @@ return NULL; } - memset(&qs, 0, sizeof(WSAQUERYSET)); - qs.dwSize = sizeof(WSAQUERYSET); - qs.dwNameSpace = NS_NLA; - if (WSALookupServiceBegin(&qs, 0, &network_change_handle) == SOCKET_ERROR) { - int errorid = WSAGetLastError(); - gchar *msg = g_win32_error_message(errorid); - purple_debug_warning("network", "Couldn't retrieve NLA SP lookup handle. " - "NLA service is probably not running. Message: %s (%d).\n", - msg, errorid); - g_free(msg); - WSACloseEvent(nla_event); - g_static_mutex_unlock(&mutex); - g_thread_exit(NULL); - return NULL; + if (network_change_handle == NULL) { + memset(&qs, 0, sizeof(WSAQUERYSET)); + qs.dwSize = sizeof(WSAQUERYSET); + qs.dwNameSpace = NS_NLA; + if (WSALookupServiceBegin(&qs, 0, &network_change_handle) == SOCKET_ERROR) { + int errorid = WSAGetLastError(); + gchar *msg = g_win32_error_message(errorid); + purple_timeout_add(0, _print_debug_msg, + g_strdup_printf("Couldn't retrieve NLA SP lookup handle. " + "NLA service is probably not running. Message: %s (%d).\n", + msg, errorid)); + g_free(msg); + WSACloseEvent(nla_event); + g_static_mutex_unlock(&mutex); + g_thread_exit(NULL); + return NULL; + } } g_static_mutex_unlock(&mutex); - /* Make sure at least 30 seconds have elapsed since the last - * notification so we don't peg the cpu if this keeps changing. */ - if ((time(NULL) - last_trigger) < 30) - Sleep(30000); - - last_trigger = time(NULL); - memset(&completion, 0, sizeof(WSACOMPLETION)); completion.Type = NSP_NOTIFY_EVENT; overlapped.hEvent = nla_event; @@ -615,18 +616,34 @@ if (MyWSANSPIoctl(network_change_handle, SIO_NSP_NOTIFY_CHANGE, NULL, 0, NULL, 0, &retLen, &completion) == SOCKET_ERROR) { int errorid = WSAGetLastError(); + if (errorid == WSA_INVALID_HANDLE) { + purple_timeout_add(0, _print_debug_msg, + g_strdup("Invalid NLA handle; resetting.\n")); + g_static_mutex_lock(&mutex); + retval = WSALookupServiceEnd(network_change_handle); + network_change_handle = NULL; + g_static_mutex_unlock(&mutex); + continue; /* WSA_IO_PENDING indicates successful async notification will happen */ - if (errorid != WSA_IO_PENDING) { + } else if (errorid != WSA_IO_PENDING) { gchar *msg = g_win32_error_message(errorid); - purple_debug_warning("network", "Unable to wait for changes. Message: %s (%d).\n", - msg, errorid); + purple_timeout_add(0, _print_debug_msg, + g_strdup_printf("Unable to wait for changes. Message: %s (%d).\n", + msg, errorid)); g_free(msg); } } + /* Make sure at least 30 seconds have elapsed since the last + * notification so we don't peg the cpu if this keeps changing. */ + if ((time(NULL) - last_trigger) < 30) + Sleep(30000); + /* This will block until NLA notifies us */ retval = WaitForSingleObjectEx(nla_event, WSA_INFINITE, TRUE); + last_trigger = time(NULL); + g_static_mutex_lock(&mutex); if (network_initialized == FALSE) { /* Time to die */ @@ -636,8 +653,14 @@ return NULL; } - retval = WSALookupServiceEnd(network_change_handle); - network_change_handle = NULL; + size = sizeof(buf); + while ((retval = WSALookupServiceNext(network_change_handle, 0, &size, res)) == ERROR_SUCCESS) { + /*purple_timeout_add(0, _print_debug_msg, + g_strdup_printf("thread found network '%s'\n", + res->lpszServiceInstanceName ? res->lpszServiceInstanceName : "(NULL)"));*/ + size = sizeof(buf); + } + WSAResetEvent(nla_event); g_static_mutex_unlock(&mutex); @@ -863,11 +886,12 @@ if (cnt < 0) /* Assume there is a network */ current_network_count = 1; /* Don't listen for network changes if we can't tell anyway */ - else - { + else { current_network_count = cnt; - if (!g_thread_create(wpurple_network_change_thread, NULL, FALSE, &err)) - purple_debug_error("network", "Couldn't create Network Monitor thread: %s\n", err ? err->message : ""); + if ((MyWSANSPIoctl = (void*) wpurple_find_and_loadproc("ws2_32.dll", "WSANSPIoctl"))) { + if (!g_thread_create(wpurple_network_change_thread, NULL, FALSE, &err)) + purple_debug_error("network", "Couldn't create Network Monitor thread: %s\n", err ? err->message : ""); + } } #endif @@ -953,12 +977,14 @@ msg, errorid); g_free(msg); } + network_change_handle = NULL; + } g_static_mutex_unlock(&mutex); #endif purple_signal_unregister(purple_network_get_handle(), - "network-configuration-changed"); + "network-configuration-changed"); if (stun_ip) g_free(stun_ip); diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/protocols/jabber/disco.c --- a/libpurple/protocols/jabber/disco.c Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/protocols/jabber/disco.c Thu Feb 05 11:47:40 2009 +0000 @@ -452,7 +452,12 @@ if (!strcmp(name, "Google Talk")) { purple_debug_info("jabber", "Google Talk!\n"); js->googletalk = TRUE; - } + + /* autodiscover stun and relays */ + jabber_google_send_jingle_info(js); + } else { + /* TODO: add external service discovery here... */ + } } for (child = xmlnode_get_child(query, "feature"); child; diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/protocols/jabber/google.c --- a/libpurple/protocols/jabber/google.c Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/protocols/jabber/google.c Thu Feb 05 11:47:40 2009 +0000 @@ -23,6 +23,8 @@ #include "mediamanager.h" #include "util.h" #include "privacy.h" +#include "dnsquery.h" +#include "network.h" #include "buddy.h" #include "google.h" @@ -30,6 +32,8 @@ #include "presence.h" #include "iq.h" +#include "jingle/jingle.h" + #ifdef USE_VV typedef struct { @@ -124,7 +128,7 @@ sess = google_session_create_xmlnode(session, "candidates"); xmlnode_insert_child(iq->node, sess); xmlnode_set_attrib(iq->node, "to", session->remote_jid); - + for (;candidates;candidates = candidates->next) { char port[8]; char pref[8]; @@ -132,7 +136,7 @@ if (!strcmp(transport->ip, "127.0.0.1")) continue; - + candidate = xmlnode_new("candidate"); g_snprintf(port, sizeof(port), "%d", transport->port); @@ -162,7 +166,6 @@ xmlnode_set_attrib(candidate, "generation", "0"); xmlnode_set_attrib(candidate, "network", "0"); xmlnode_insert_child(sess, candidate); - } jabber_iq_send(iq); } @@ -246,6 +249,26 @@ } } +static GParameter * +jabber_google_session_get_params(JabberStream *js, guint *num) +{ + guint num_params; + GParameter *params = jingle_get_params(js, &num_params); + GParameter *new_params = g_new0(GParameter, num_params + 1); + + memcpy(new_params, params, sizeof(GParameter) * num_params); + + purple_debug_info("jabber", "setting Google jingle compatibility param\n"); + new_params[num_params].name = "compatibility-mode"; + g_value_init(&new_params[num_params].value, G_TYPE_UINT); + g_value_set_uint(&new_params[num_params].value, 1); /* NICE_COMPATIBILITY_GOOGLE */ + + g_free(params); + *num = num_params + 1; + return new_params; +} + + PurpleMedia* jabber_google_session_initiate(JabberStream *js, const gchar *who, PurpleMediaSessionType type) { @@ -253,7 +276,8 @@ JabberBuddy *jb; JabberBuddyResource *jbr; gchar *jid; - GParameter param; + GParameter *params; + guint num_params; /* construct JID to send to */ jb = jabber_buddy_find(js, who, FALSE); @@ -286,18 +310,15 @@ purple_media_manager_get(), js->gc, "fsrtpconference", session->remote_jid, TRUE); - /* GTalk requires the NICE_COMPATIBILITY_GOOGLE param */ - param.name = "compatibility-mode"; - memset(¶m.value, 0, sizeof(GValue)); - g_value_init(¶m.value, G_TYPE_UINT); - g_value_set_uint(¶m.value, 1); /* NICE_COMPATIBILITY_GOOGLE */ + params = jabber_google_session_get_params(js, &num_params); if (purple_media_add_stream(session->media, "google-voice", session->remote_jid, PURPLE_MEDIA_AUDIO, - "nice", 1, ¶m) == FALSE) { + "nice", num_params, params) == FALSE) { purple_media_error(session->media, "Error adding stream."); purple_media_hangup(session->media); google_session_destroy(session); + g_free(params); return NULL; } @@ -310,6 +331,7 @@ sessions = g_hash_table_new(google_session_id_hash, google_session_id_equal); g_hash_table_insert(sessions, &(session->id), session); + g_free(params); return session->media; } @@ -322,8 +344,9 @@ xmlnode *desc_element, *codec_element; PurpleMediaCodec *codec; const char *id, *encoding_name, *clock_rate; - GParameter param; - + GParameter *params; + guint num_params; + if (session->state != UNINIT) { purple_debug_error("jabber", "Received initiate for active session.\n"); return; @@ -332,22 +355,21 @@ session->media = purple_media_manager_create_media(purple_media_manager_get(), js->gc, "fsrtpconference", session->remote_jid, FALSE); - /* GTalk requires the NICE_COMPATIBILITY_GOOGLE param */ - param.name = "compatibility-mode"; - memset(¶m.value, 0, sizeof(GValue)); - g_value_init(¶m.value, G_TYPE_UINT); - g_value_set_uint(¶m.value, 1); /* NICE_COMPATIBILITY_GOOGLE */ + params = jabber_google_session_get_params(js, &num_params); if (purple_media_add_stream(session->media, "google-voice", session->remote_jid, - PURPLE_MEDIA_AUDIO, "nice", 1, ¶m) == FALSE) { + PURPLE_MEDIA_AUDIO, "nice", num_params, params) == FALSE) { purple_media_error(session->media, "Error adding stream."); purple_media_hangup(session->media); google_session_send_terminate(session); + g_free(params); return; } + g_free(params); + desc_element = xmlnode_get_child(sess, "description"); - + for (codec_element = xmlnode_get_child(desc_element, "payload-type"); codec_element; codec_element = xmlnode_get_next_twin(codec_element)) { @@ -368,7 +390,7 @@ G_CALLBACK(google_session_state_changed_cb), session); purple_media_codec_list_free(codecs); - + result = jabber_iq_new(js, JABBER_IQ_RESULT); jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id")); xmlnode_set_attrib(result->node, "to", session->remote_jid); @@ -1025,3 +1047,105 @@ const char *attr = purple_status_get_attr_string(tune, PURPLE_TUNE_TITLE); return attr ? g_strdup_printf("♫ %s", attr) : g_strdup(""); } + +static void +jabber_google_stun_lookup_cb(GSList *hosts, gpointer data, + const char *error_message) +{ + JabberStream *js = (JabberStream *) data; + + if (error_message) { + purple_debug_error("jabber", "Google STUN lookup failed: %s\n", + error_message); + g_slist_free(hosts); + return; + } + + if (hosts && g_slist_next(hosts)) { + struct sockaddr *addr = g_slist_next(hosts)->data; + char dst[INET6_ADDRSTRLEN]; + int port; + + if (addr->sa_family == AF_INET6) { + inet_ntop(addr->sa_family, &((struct sockaddr_in6 *) addr)->sin6_addr, + dst, sizeof(dst)); + port = ntohs(((struct sockaddr_in6 *) addr)->sin6_port); + } else { + inet_ntop(addr->sa_family, &((struct sockaddr_in *) addr)->sin_addr, + dst, sizeof(dst)); + port = ntohs(((struct sockaddr_in *) addr)->sin_port); + } + + if (js) { + if (js->stun_ip) { + g_free(js->stun_ip); + } + js->stun_ip = g_strdup(dst); + purple_debug_info("jabber", "set Google STUN IP address: %s\n", dst); + js->stun_port = port; + purple_debug_info("jabber", "set Google STUN port: %d\n", port); + purple_debug_info("jabber", "set Google STUN port: %d\n", port); + /* unmark ongoing query */ + js->stun_query = NULL; + } + } + + g_slist_free(hosts); +} + +static void +jabber_google_jingle_info_cb(JabberStream *js, xmlnode *result, + gpointer nullus) +{ + if (result) { + const xmlnode *query = + xmlnode_get_child_with_namespace(result, "query", + GOOGLE_JINGLE_INFO_NAMESPACE); + + if (query) { + const xmlnode *stun = xmlnode_get_child(query, "stun"); + + purple_debug_info("jabber", "got google:jingleinfo\n"); + + if (stun) { + xmlnode *server = xmlnode_get_child(stun, "server"); + + if (server) { + const gchar *host = xmlnode_get_attrib(server, "host"); + const gchar *udp = xmlnode_get_attrib(server, "udp"); + + if (host && udp) { + int port = atoi(udp); + /* if there, would already be an ongoing query, + cancel it */ + if (js->stun_query) + purple_dnsquery_destroy(js->stun_query); + + js->stun_query = purple_dnsquery_a(host, port, + jabber_google_stun_lookup_cb, js); + } + } + } + /* should perhaps handle relays later on, or maybe wait until + Google supports a common standard... */ + } + } +} + +void +jabber_google_handle_jingle_info(JabberStream *js, xmlnode *packet) +{ + jabber_google_jingle_info_cb(js, packet, NULL); +} + +void +jabber_google_send_jingle_info(JabberStream *js) +{ + JabberIq *jingle_info = + jabber_iq_new_query(js, JABBER_IQ_GET, GOOGLE_JINGLE_INFO_NAMESPACE); + + jabber_iq_set_callback(jingle_info, jabber_google_jingle_info_cb, + NULL); + purple_debug_info("jabber", "sending google:jingleinfo query\n"); + jabber_iq_send(jingle_info); +} diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/protocols/jabber/google.h --- a/libpurple/protocols/jabber/google.h Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/protocols/jabber/google.h Thu Feb 05 11:47:40 2009 +0000 @@ -27,6 +27,8 @@ #include "jabber.h" #include "media.h" +#define GOOGLE_JINGLE_INFO_NAMESPACE "google:jingleinfo" + void jabber_gmail_init(JabberStream *js); void jabber_gmail_poke(JabberStream *js, xmlnode *node); @@ -49,5 +51,7 @@ PurpleMedia *jabber_google_session_initiate(JabberStream *js, const gchar *who, PurpleMediaSessionType type); void jabber_google_session_parse(JabberStream *js, xmlnode *node); +void jabber_google_handle_jingle_info(JabberStream *js, xmlnode *packet); +void jabber_google_send_jingle_info(JabberStream *js); #endif /* _PURPLE_GOOGLE_H_ */ diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/protocols/jabber/iq.c --- a/libpurple/protocols/jabber/iq.c Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/protocols/jabber/iq.c Thu Feb 05 11:47:40 2009 +0000 @@ -448,6 +448,9 @@ #ifdef USE_VV jabber_iq_register_handler(JINGLE, jingle_parse); #endif + /* handle Google jingleinfo */ + jabber_iq_register_handler(GOOGLE_JINGLE_INFO_NAMESPACE, + jabber_google_handle_jingle_info); } void jabber_iq_uninit(void) diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/protocols/jabber/jabber.c --- a/libpurple/protocols/jabber/jabber.c Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/protocols/jabber/jabber.c Thu Feb 05 11:47:40 2009 +0000 @@ -735,20 +735,24 @@ js->sessions = NULL; #endif + js->stun_ip = NULL; + js->stun_port = 0; + js->stun_query = NULL; + if(!js->user) { purple_connection_error_reason (gc, PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, _("Invalid XMPP ID")); return; } - + if (!js->user->domain || *(js->user->domain) == '\0') { purple_connection_error_reason (gc, PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, _("Invalid XMPP ID. Domain must be set.")); return; } - + if((my_jb = jabber_buddy_find(js, purple_account_get_username(account), TRUE))) my_jb->subscription |= JABBER_SUB_BOTH; @@ -1222,6 +1226,10 @@ server = connect_server[0] ? connect_server : js->user->domain; js->certificate_CN = g_strdup(server); + js->stun_ip = NULL; + js->stun_port = 0; + js->stun_query = NULL; + jabber_stream_set_state(js, JABBER_STREAM_CONNECTING); if(purple_account_get_bool(account, "old_ssl", FALSE)) { @@ -1425,6 +1433,15 @@ g_free(js->srv_rec); js->srv_rec = NULL; + g_free(js->stun_ip); + js->stun_ip = NULL; + + /* cancel DNS query for STUN, if one is ongoing */ + if (js->stun_query) { + purple_dnsquery_destroy(js->stun_query); + js->stun_query = NULL; + } + g_free(js); gc->proto_data = NULL; @@ -1881,7 +1898,7 @@ type = purple_status_type_new_with_attrs(PURPLE_STATUS_OFFLINE, jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_UNAVAILABLE), - NULL, FALSE, TRUE, FALSE, + NULL, TRUE, TRUE, FALSE, "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), NULL); types = g_list_append(types, type); diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/protocols/jabber/jabber.h --- a/libpurple/protocols/jabber/jabber.h Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/protocols/jabber/jabber.h Thu Feb 05 11:47:40 2009 +0000 @@ -58,6 +58,7 @@ #include "mediamanager.h" #include "roomlist.h" #include "sslconn.h" +#include "dnsquery.h" #include "jutil.h" #include "xmlnode.h" @@ -250,6 +251,12 @@ #ifdef USE_VV GHashTable *medias; #endif + + /* maybe this should only be present when USE_VV? */ + gchar *stun_ip; + int stun_port; + PurpleDnsQueryData *stun_query; + /* later add stuff to handle TURN relays... */ }; typedef gboolean (JabberFeatureEnabled)(JabberStream *js, const gchar *shortname, const gchar *namespace); diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/protocols/jabber/jingle/iceudp.c --- a/libpurple/protocols/jabber/jingle/iceudp.c Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/protocols/jabber/jingle/iceudp.c Thu Feb 05 11:47:40 2009 +0000 @@ -57,6 +57,7 @@ new_candidate->component = candidate->component; new_candidate->foundation = g_strdup(candidate->foundation); new_candidate->generation = candidate->generation; + new_candidate->id = g_strdup(candidate->id); new_candidate->ip = g_strdup(candidate->ip); new_candidate->network = candidate->network; new_candidate->port = candidate->port; @@ -74,6 +75,7 @@ jingle_iceudp_candidate_free(JingleIceUdpCandidate *candidate) { g_free(candidate->foundation); + g_free(candidate->id); g_free(candidate->ip); g_free(candidate->protocol); g_free(candidate->type); @@ -97,14 +99,16 @@ JingleIceUdpCandidate * jingle_iceudp_candidate_new(guint component, const gchar *foundation, - guint generation, const gchar *ip, guint network, - guint port, guint priority, const gchar *protocol, - const gchar *type, const gchar *username, const gchar *password) + guint generation, const gchar *id, const gchar *ip, + guint network, guint port, guint priority, + const gchar *protocol, const gchar *type, + const gchar *username, const gchar *password) { JingleIceUdpCandidate *candidate = g_new0(JingleIceUdpCandidate, 1); candidate->component = component; candidate->foundation = g_strdup(foundation); candidate->generation = generation; + candidate->id = g_strdup(id); candidate->ip = g_strdup(ip); candidate->network = network; candidate->port = port; @@ -233,8 +237,7 @@ for (; iter; iter = g_list_next(iter)) { JingleIceUdpCandidate *c = iter->data; - if ((c->component == candidate->component) && - !strcmp(c->foundation, candidate->foundation)) { + if (!strcmp(c->id, candidate->id)) { guint generation = c->generation + 1; g_boxed_free(JINGLE_TYPE_ICEUDP_CANDIDATE, c); @@ -261,13 +264,12 @@ static JingleIceUdpCandidate * jingle_iceudp_get_remote_candidate_by_id(JingleIceUdp *iceudp, - guint component, const gchar *foundation) + const gchar *id) { GList *iter = iceudp->priv->remote_candidates; for (; iter; iter = g_list_next(iter)) { JingleIceUdpCandidate *candidate = iter->data; - if ((candidate->component == component) && - !strcmp(candidate->foundation, foundation)) { + if (!strcmp(candidate->id, id)) { return candidate; } } @@ -280,7 +282,7 @@ JingleIceUdpPrivate *priv = JINGLE_ICEUDP_GET_PRIVATE(iceudp); JingleIceUdpCandidate *iceudp_candidate = jingle_iceudp_get_remote_candidate_by_id(iceudp, - candidate->component, candidate->foundation); + candidate->id); if (iceudp_candidate != NULL) { priv->remote_candidates = g_list_remove( priv->remote_candidates, iceudp_candidate); @@ -304,6 +306,7 @@ atoi(xmlnode_get_attrib(candidate, "component")), xmlnode_get_attrib(candidate, "foundation"), atoi(xmlnode_get_attrib(candidate, "generation")), + xmlnode_get_attrib(candidate, "id"), xmlnode_get_attrib(candidate, "ip"), atoi(xmlnode_get_attrib(candidate, "network")), atoi(xmlnode_get_attrib(candidate, "port")), @@ -347,6 +350,7 @@ xmlnode_set_attrib(xmltransport, "component", component); xmlnode_set_attrib(xmltransport, "foundation", candidate->foundation); xmlnode_set_attrib(xmltransport, "generation", generation); + xmlnode_set_attrib(xmltransport, "id", candidate->id); xmlnode_set_attrib(xmltransport, "ip", candidate->ip); xmlnode_set_attrib(xmltransport, "network", network); xmlnode_set_attrib(xmltransport, "port", port); diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/protocols/jabber/jingle/iceudp.h --- a/libpurple/protocols/jabber/jingle/iceudp.h Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/protocols/jabber/jingle/iceudp.h Thu Feb 05 11:47:40 2009 +0000 @@ -66,6 +66,7 @@ guint component; gchar *foundation; guint generation; + gchar *id; gchar *ip; guint network; guint port; @@ -91,9 +92,10 @@ GType jingle_iceudp_get_type(void); JingleIceUdpCandidate *jingle_iceudp_candidate_new(guint component, - const gchar *foundation, guint generation, const gchar *ip, - guint network, guint port, guint priority, const gchar *protocol, - const gchar *type, const gchar *username, const gchar *password); + const gchar *foundation, guint generation, const gchar *id, + const gchar *ip, guint network, guint port, guint priority, + const gchar *protocol, const gchar *type, + const gchar *username, const gchar *password); void jingle_iceudp_add_local_candidate(JingleIceUdp *iceudp, JingleIceUdpCandidate *candidate); GList *jingle_iceudp_get_remote_candidates(JingleIceUdp *iceudp); diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/protocols/jabber/jingle/jingle.c --- a/libpurple/protocols/jabber/jingle/jingle.c Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/protocols/jabber/jingle/jingle.c Thu Feb 05 11:47:40 2009 +0000 @@ -20,6 +20,7 @@ */ #include "internal.h" +#include "network.h" #include "content.h" #include "debug.h" @@ -40,8 +41,12 @@ return "content-add"; case JINGLE_CONTENT_MODIFY: return "content-modify"; + case JINGLE_CONTENT_REJECT: + return "content-reject"; case JINGLE_CONTENT_REMOVE: return "content-remove"; + case JINGLE_DESCRIPTION_INFO: + return "description-info"; case JINGLE_SESSION_ACCEPT: return "session-accept"; case JINGLE_SESSION_INFO: @@ -54,6 +59,8 @@ return "transport-accept"; case JINGLE_TRANSPORT_INFO: return "transport-info"; + case JINGLE_TRANSPORT_REJECT: + return "transport-reject"; case JINGLE_TRANSPORT_REPLACE: return "transport-replace"; default: @@ -70,8 +77,12 @@ return JINGLE_CONTENT_ADD; else if (!strcmp(action, "content-modify")) return JINGLE_CONTENT_MODIFY; + else if (!strcmp(action, "content-reject")) + return JINGLE_CONTENT_REJECT; else if (!strcmp(action, "content-remove")) return JINGLE_CONTENT_REMOVE; + else if (!strcmp(action, "description-info")) + return JINGLE_DESCRIPTION_INFO; else if (!strcmp(action, "session-accept")) return JINGLE_SESSION_ACCEPT; else if (!strcmp(action, "session-info")) @@ -84,6 +95,8 @@ return JINGLE_TRANSPORT_ACCEPT; else if (!strcmp(action, "transport-info")) return JINGLE_TRANSPORT_INFO; + else if (!strcmp(action, "transport-reject")) + return JINGLE_TRANSPORT_REJECT; else if (!strcmp(action, "transport-replace")) return JINGLE_TRANSPORT_REPLACE; else @@ -206,6 +219,30 @@ } static void +jingle_handle_description_info(JingleSession *session, xmlnode *jingle) +{ + xmlnode *content = xmlnode_get_child(jingle, "content"); + + jabber_iq_send(jingle_session_create_ack(session, jingle)); + + jingle_session_accept_session(session); + + for (; content; content = xmlnode_get_next_twin(content)) { + const gchar *name = xmlnode_get_attrib(content, "name"); + const gchar *creator = xmlnode_get_attrib(content, "creator"); + JingleContent *parsed_content = + jingle_session_find_content(session, name, creator); + if (parsed_content == NULL) { + purple_debug_error("jingle", "Error parsing content\n"); + /* XXX: send error */ + } else { + jingle_content_handle_action(parsed_content, content, + JINGLE_DESCRIPTION_INFO); + } + } +} + +static void jingle_handle_session_accept(JingleSession *session, xmlnode *jingle) { xmlnode *content = xmlnode_get_child(jingle, "content"); @@ -407,6 +444,8 @@ jingle_handle_content_reject(session, jingle); } else if (!strcmp(action, "content-remove")) { jingle_handle_content_remove(session, jingle); + } else if (!strcmp(action, "description-info")) { + jingle_handle_description_info(session, jingle); } else if (!strcmp(action, "session-accept")) { jingle_handle_session_accept(session, jingle); } else if (!strcmp(action, "session-info")) { @@ -438,3 +477,32 @@ jingle_terminate_sessions_gh, NULL); } +GParameter * +jingle_get_params(JabberStream *js, guint *num) +{ + /* don't set a STUN server if one is set globally in prefs, in that case + this will be handled in media.c */ + gboolean has_account_stun = js->stun_ip && !purple_network_get_stun_ip(); + guint num_params = has_account_stun ? 2 : 0; + GParameter *params = NULL; + + if (num_params > 0) { + params = g_new0(GParameter, num_params); + + purple_debug_info("jabber", + "setting param stun-ip for stream using Google auto-config: %s\n", + js->stun_ip); + params[0].name = "stun-ip"; + g_value_init(¶ms[0].value, G_TYPE_STRING); + g_value_set_string(¶ms[0].value, js->stun_ip); + purple_debug_info("jabber", + "setting param stun-port for stream using Google auto-config: %d\n", + js->stun_port); + params[1].name = "stun-port"; + g_value_init(¶ms[1].value, G_TYPE_UINT); + g_value_set_uint(¶ms[1].value, js->stun_port); + } + + *num = num_params; + return params; +} diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/protocols/jabber/jingle/jingle.h --- a/libpurple/protocols/jabber/jingle/jingle.h Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/protocols/jabber/jingle/jingle.h Thu Feb 05 11:47:40 2009 +0000 @@ -33,9 +33,9 @@ #define JINGLE "urn:xmpp:jingle:0" #define JINGLE_ERROR "urn:xmpp:jingle:errors:0" #define JINGLE_APP_FT "urn:xmpp:jingle:apps:file-transfer:0" -#define JINGLE_APP_RTP "urn:xmpp:jingle:apps:rtp:0" -#define JINGLE_APP_RTP_ERROR "urn:xmpp:jingle:apps:rtp:errors:0" -#define JINGLE_APP_RTP_INFO "urn:xmpp:jingle:apps:rtp:info:0" +#define JINGLE_APP_RTP "urn:xmpp:jingle:apps:rtp:1" +#define JINGLE_APP_RTP_ERROR "urn:xmpp:jingle:apps:rtp:errors:1" +#define JINGLE_APP_RTP_INFO "urn:xmpp:jingle:apps:rtp:info:1" #define JINGLE_APP_RTP_SUPPORT_AUDIO "urn:xmpp:jingle:apps:rtp:audio" #define JINGLE_APP_RTP_SUPPORT_VIDEO "urn:xmpp:jingle:apps:rtp:video" #define JINGLE_APP_XML "urn:xmpp:tmp:jingle:apps:xmlstream" @@ -43,8 +43,7 @@ #define JINGLE_TRANSPORT_SOCKS "urn:xmpp:jingle:transports:bytestreams:0" #define JINGLE_TRANSPORT_IBB "urn:xmpp:jingle:transports:ibb:0" #define JINGLE_TRANSPORT_ICEUDP "urn:xmpp:jingle:transports:ice-udp:0" -#define JINGLE_TRANSPORT_RAWUDP "urn:xmpp:jingle:transports:raw-udp:0" -#define JINGLE_TRANSPORT_RAWUDP_INFO "urn:xmpp:jingle:transports:raw-udp:info:0" +#define JINGLE_TRANSPORT_RAWUDP "urn:xmpp:jingle:transports:raw-udp:1" typedef enum { JINGLE_UNKNOWN_TYPE, @@ -53,6 +52,7 @@ JINGLE_CONTENT_MODIFY, JINGLE_CONTENT_REJECT, JINGLE_CONTENT_REMOVE, + JINGLE_DESCRIPTION_INFO, JINGLE_SESSION_ACCEPT, JINGLE_SESSION_INFO, JINGLE_SESSION_INITIATE, @@ -72,6 +72,10 @@ void jingle_terminate_sessions(JabberStream *js); +/* create a GParam array given autoconfigured STUN (and later perhaps TURN). + if google_talk is TRUE, set compatability mode to GOOGLE_TALK */ +GParameter *jingle_get_params(JabberStream *js, guint *num_params); + #ifdef __cplusplus } #endif diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/protocols/jabber/jingle/rtp.c --- a/libpurple/protocols/jabber/jingle/rtp.c Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/protocols/jabber/jingle/rtp.c Thu Feb 05 11:47:40 2009 +0000 @@ -189,26 +189,28 @@ jingle_rtp_candidates_to_transport(JingleSession *session, GType type, guint generation, GList *candidates) { if (type == JINGLE_TYPE_RAWUDP) { - gchar *id = jabber_get_next_id(jingle_session_get_js(session)); JingleTransport *transport = jingle_transport_create(JINGLE_TRANSPORT_RAWUDP); JingleRawUdpCandidate *rawudp_candidate; for (; candidates; candidates = g_list_next(candidates)) { PurpleMediaCandidate *candidate = candidates->data; - id = jabber_get_next_id(jingle_session_get_js(session)); + gchar *id = jabber_get_next_id( + jingle_session_get_js(session)); rawudp_candidate = jingle_rawudp_candidate_new(id, generation, candidate->component_id, candidate->ip, candidate->port); jingle_rawudp_add_local_candidate(JINGLE_RAWUDP(transport), rawudp_candidate); + g_free(id); } - g_free(id); return transport; } else if (type == JINGLE_TYPE_ICEUDP) { JingleTransport *transport = jingle_transport_create(JINGLE_TRANSPORT_ICEUDP); JingleIceUdpCandidate *iceudp_candidate; for (; candidates; candidates = g_list_next(candidates)) { PurpleMediaCandidate *candidate = candidates->data; + gchar *id = jabber_get_next_id( + jingle_session_get_js(session)); iceudp_candidate = jingle_iceudp_candidate_new(candidate->component_id, - candidate->foundation, generation, candidate->ip, + candidate->foundation, generation, id, candidate->ip, 0, candidate->port, candidate->priority, "udp", candidate->type == PURPLE_MEDIA_CANDIDATE_TYPE_HOST ? "host" : candidate->type == PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX ? "srflx" : @@ -216,6 +218,7 @@ candidate->type == PURPLE_MEDIA_CANDIDATE_TYPE_RELAY ? "relay" : "", candidate->username, candidate->password); jingle_iceudp_add_local_candidate(JINGLE_ICEUDP(transport), iceudp_candidate); + g_free(id); } return transport; } else { @@ -402,6 +405,8 @@ gboolean is_audio; PurpleMediaSessionType type; JingleTransport *transport; + GParameter *params = NULL; + guint num_params; /* maybe this create ought to just be in initiate and handle initiate */ if (media == NULL) @@ -436,13 +441,16 @@ type = is_audio == TRUE ? PURPLE_MEDIA_RECV_AUDIO : PURPLE_MEDIA_RECV_VIDEO; + params = + jingle_get_params(jingle_session_get_js(session), &num_params); purple_media_add_stream(media, name, remote_jid, - type, transmitter, 0, NULL); + type, transmitter, num_params, params); g_free(name); g_free(media_type); g_free(remote_jid); g_free(senders); + g_free(params); g_object_unref(session); return TRUE; diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/protocols/msn/contact.c --- a/libpurple/protocols/msn/contact.c Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/protocols/msn/contact.c Thu Feb 05 11:47:40 2009 +0000 @@ -158,6 +158,113 @@ state->action |= action; } +/*************************************************************** + * General SOAP handling + ***************************************************************/ + +static const char * +msn_contact_operation_str(MsnCallbackAction action) +{ + /* Make sure this is large enough when adding more */ + static char buf[BUF_LEN]; + buf[0] = '\0'; + + if (action & MSN_ADD_BUDDY) + strcat(buf, "Adding Buddy,"); + if (action & MSN_MOVE_BUDDY) + strcat(buf, "Moving Buddy,"); + if (action & MSN_ACCEPTED_BUDDY) + strcat(buf, "Accepted Buddy,"); + if (action & MSN_DENIED_BUDDY) + strcat(buf, "Denied Buddy,"); + if (action & MSN_ADD_GROUP) + strcat(buf, "Adding Group,"); + if (action & MSN_DEL_GROUP) + strcat(buf, "Deleting Group,"); + if (action & MSN_RENAME_GROUP) + strcat(buf, "Renaming Group,"); + if (action & MSN_UPDATE_INFO) + strcat(buf, "Updating Contact Info,"); + + return buf; +} + +static gboolean msn_contact_request(MsnCallbackState *state); + +static void +msn_contact_request_cb(MsnSoapMessage *req, MsnSoapMessage *resp, + gpointer data) +{ + MsnCallbackState *state = data; + xmlnode *fault; + char *faultcode_str; + + if (resp == NULL) { + purple_debug_error("msn", + "Operation {%s} failed. No response received from server.\n", + msn_contact_operation_str(state->action)); + return; + } + + fault = xmlnode_get_child(resp->xml, "Body/Fault"); + + if (fault == NULL) { + /* No errors */ + if (state->cb) + ((MsnSoapCallback)state->cb)(req, resp, data); + msn_callback_state_free(state); + return; + } + + faultcode_str = xmlnode_get_data(xmlnode_get_child(fault, "faultcode")); + + if (faultcode_str && g_str_equal(faultcode_str, "q0:BadContextToken")) { + purple_debug_info("msn", + "Contact Operation {%s} failed because of bad token." + " Updating token now and retrying operation.\n", + msn_contact_operation_str(state->action)); + /* Token has expired, so renew it, and try again later */ + msn_nexus_update_token(state->session->nexus, MSN_AUTH_CONTACTS, + (GSourceFunc)msn_contact_request, data); + } + else + { + if (state->cb) { + ((MsnSoapCallback)state->cb)(req, resp, data); + } else { + /* We don't know how to respond to this faultcode, so log it */ + char *str = xmlnode_to_str(fault, NULL); + purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n", + msn_contact_operation_str(state->action), str); + g_free(str); + } + msn_callback_state_free(state); + } + + g_free(faultcode_str); +} + +static gboolean +msn_contact_request(MsnCallbackState *state) +{ + if (state->token == NULL) + state->token = xmlnode_get_child(state->body, + "Header/ABAuthHeader/TicketToken"); + /* delete old & replace with new token */ + xmlnode_free(state->token->child); + xmlnode_insert_data(state->token, + msn_nexus_get_token_str(state->session->nexus, MSN_AUTH_CONTACTS), -1); + msn_soap_message_send(state->session, + msn_soap_message_new(state->post_action, xmlnode_copy(state->body)), + MSN_CONTACT_SERVER, state->post_url, FALSE, + msn_contact_request_cb, state); + return FALSE; +} + +/*************************************************************** + * Address Book and Membership List Operations + ***************************************************************/ + /*get MSN member role utility*/ static MsnListId msn_get_memberrole(const char *role) @@ -180,9 +287,10 @@ static void msn_create_address_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) { + MsnCallbackState *state = data; if (resp && xmlnode_get_child(resp->xml, "Body/Fault") == NULL) { purple_debug_info("msn", "Address Book successfully created!\n"); - msn_get_address_book((MsnSession *)data, MSN_PS_INITIAL, NULL, NULL); + msn_get_address_book(state->session, MSN_PS_INITIAL, NULL, NULL); } else { purple_debug_info("msn", "Address Book creation failed!\n"); } @@ -192,7 +300,7 @@ msn_create_address_book(MsnSession *session) { gchar *body; - gchar *token_str; + MsnCallbackState *state; g_return_if_fail(session != NULL); g_return_if_fail(session->user != NULL); @@ -200,17 +308,15 @@ purple_debug_info("msn", "Creating an Address Book.\n"); - token_str = g_markup_escape_text( - msn_nexus_get_token_str(session->nexus, MSN_AUTH_CONTACTS), -1); body = g_strdup_printf(MSN_ADD_ADDRESSBOOK_TEMPLATE, - token_str, session->user->passport); - g_free(token_str); + session->user->passport); - msn_soap_message_send(session, - msn_soap_message_new(MSN_ADD_ADDRESSBOOK_SOAP_ACTION, - xmlnode_from_str(body, -1)), - MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL, FALSE, - msn_create_address_cb, session); + state = msn_callback_state_new(session); + state->body = xmlnode_from_str(body, -1); + state->post_action = MSN_ADD_ADDRESSBOOK_SOAP_ACTION; + state->post_url = MSN_ADDRESS_BOOK_POST_URL; + state->cb = msn_create_address_cb; + msn_contact_request(state); g_free(body); } @@ -243,7 +349,7 @@ g_free(name); } } - + purple_debug_info("msn", "CL: %s name: %s, Type: %s, MembershipID: %s, NetworkID: %u\n", node, passport, type, member_id == NULL ? "(null)" : member_id, nid); @@ -362,8 +468,8 @@ msn_get_contact_list_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) { - GetContactListCbData *cb_data = data; - MsnSession *session = cb_data->session; + MsnCallbackState *state = data; + MsnSession *session = state->session; g_return_if_fail(session != NULL); @@ -379,7 +485,7 @@ dynamicItemLastChange = purple_account_get_string(session->account, "dynamicItemLastChange", NULL); - if (cb_data->which == MSN_PS_INITIAL) { + if (state->partner_scenario == MSN_PS_INITIAL) { #ifdef MSN_PARTIAL_LISTS /* XXX: this should be enabled when we can correctly do partial syncs with the server. Currently we need to retrieve the whole @@ -390,8 +496,6 @@ #endif } } - - g_free(cb_data); } /*SOAP get contact list*/ @@ -401,8 +505,7 @@ { gchar *body = NULL; gchar *update_str = NULL; - gchar *token_str; - GetContactListCbData cb_data = { session, partner_scenario }; + MsnCallbackState *state; const gchar *partner_scenario_str = MsnSoapPartnerScenarioText[partner_scenario]; purple_debug_misc("msn", "Getting Contact List.\n"); @@ -412,17 +515,16 @@ update_str = g_strdup_printf(MSN_GET_CONTACT_UPDATE_XML, update_time); } - token_str = g_markup_escape_text( - msn_nexus_get_token_str(session->nexus, MSN_AUTH_CONTACTS), -1); body = g_strdup_printf(MSN_GET_CONTACT_TEMPLATE, partner_scenario_str, - token_str, update_str ? update_str : ""); - g_free(token_str); + update_str ? update_str : ""); - msn_soap_message_send(session, - msn_soap_message_new(MSN_GET_CONTACT_SOAP_ACTION, - xmlnode_from_str(body, -1)), - MSN_CONTACT_SERVER, MSN_GET_CONTACT_POST_URL, FALSE, - msn_get_contact_list_cb, g_memdup(&cb_data, sizeof(cb_data))); + state = msn_callback_state_new(session); + state->partner_scenario = partner_scenario; + state->body = xmlnode_from_str(body, -1); + state->post_action = MSN_GET_CONTACT_SOAP_ACTION; + state->post_url = MSN_GET_CONTACT_POST_URL; + state->cb = msn_get_contact_list_cb; + msn_contact_request(state); g_free(update_str); g_free(body); @@ -525,7 +627,7 @@ for(contactNode = xmlnode_get_child(node, "Contact"); contactNode; contactNode = xmlnode_get_next_twin(contactNode)) { - xmlnode *contactId, *contactInfo, *contactType, *passportName, *displayName, *guid, *groupIds, *messenger_user; + xmlnode *contactId, *contactInfo, *contactType, *passportName, *displayName, *guid, *groupIds; xmlnode *annotation; MsnUser *user; @@ -556,58 +658,52 @@ continue; /* Not adding own account as buddy to buddylist */ } - /* ignore non-messenger contacts */ - if ((messenger_user = xmlnode_get_child(contactInfo, "isMessengerUser"))) { - char *is_messenger_user = xmlnode_get_data(messenger_user); - - if(is_messenger_user && !strcmp(is_messenger_user, "false")) { - g_free(is_messenger_user); - continue; - } - - g_free(is_messenger_user); - } - passportName = xmlnode_get_child(contactInfo, "passportName"); if (passportName == NULL) { xmlnode *emailsNode, *contactEmailNode, *emailNode; xmlnode *messengerEnabledNode; char *msnEnabled; - /*TODO: add it to the none-instant Messenger group and recognize as email Membership*/ - /*Yahoo User?*/ + /*TODO: add it to the non-instant Messenger group and recognize as email Membership*/ + /* Yahoo/Federated User? */ emailsNode = xmlnode_get_child(contactInfo, "emails"); if (emailsNode == NULL) { /*TODO: need to support the Mobile type*/ continue; } for (contactEmailNode = xmlnode_get_child(emailsNode, "ContactEmail"); contactEmailNode; - contactEmailNode = xmlnode_get_next_twin(contactEmailNode) ){ - if (!(messengerEnabledNode = xmlnode_get_child(contactEmailNode, "isMessengerEnabled"))) { - /* XXX: Should this be a continue instead of a break? It seems like it'd cause unpredictable results otherwise. */ - break; - } + contactEmailNode = xmlnode_get_next_twin(contactEmailNode)) { + if (!(messengerEnabledNode = xmlnode_get_child(contactEmailNode, "isMessengerEnabled"))) + continue; msnEnabled = xmlnode_get_data(messengerEnabledNode); - if ((emailNode = xmlnode_get_child(contactEmailNode, "email"))) { - g_free(passport); - passport = xmlnode_get_data(emailNode); - } + if (msnEnabled && !strcmp(msnEnabled, "true")) { + if ((emailNode = xmlnode_get_child(contactEmailNode, "email"))) + passport = xmlnode_get_data(emailNode); - if (msnEnabled && !strcmp(msnEnabled, "true")) { /*Messenger enabled, Get the Passport*/ - purple_debug_info("msn", "AB Yahoo User %s\n", passport ? passport : "(null)"); + purple_debug_info("msn", "AB Yahoo/Federated User %s\n", passport ? passport : "(null)"); g_free(msnEnabled); break; - } else { - /*TODO maybe we can just ignore it in Purple?*/ - purple_debug_info("msn", "AB Other type user\n"); } g_free(msnEnabled); } } else { + xmlnode *messenger_user; + /* ignore non-messenger contacts */ + if ((messenger_user = xmlnode_get_child(contactInfo, "isMessengerUser"))) { + char *is_messenger_user = xmlnode_get_data(messenger_user); + + if (is_messenger_user && !strcmp(is_messenger_user, "false")) { + g_free(is_messenger_user); + continue; + } + + g_free(is_messenger_user); + } + passport = xmlnode_get_data(passportName); } @@ -777,10 +873,8 @@ static void msn_get_address_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) { - MsnSession *session = data; - - if (resp == NULL) - return; + MsnCallbackState *state = data; + MsnSession *session = state->session; g_return_if_fail(session != NULL); @@ -809,7 +903,7 @@ const char *dynamicItemLastChange) { char *body, *update_str = NULL; - gchar *token_str; + MsnCallbackState *state; purple_debug_misc("msn", "Getting Address Book\n"); @@ -819,19 +913,16 @@ else if (LastChanged != NULL) update_str = g_strdup_printf(MSN_GET_ADDRESS_UPDATE_XML, LastChanged); - token_str = g_markup_escape_text( - msn_nexus_get_token_str(session->nexus, MSN_AUTH_CONTACTS), -1); body = g_strdup_printf(MSN_GET_ADDRESS_TEMPLATE, MsnSoapPartnerScenarioText[partner_scenario], - token_str, update_str ? update_str : ""); - g_free(token_str); - msn_soap_message_send(session, - msn_soap_message_new(MSN_GET_ADDRESS_SOAP_ACTION, - xmlnode_from_str(body, -1)), - MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL, FALSE, - msn_get_address_cb, session); + state = msn_callback_state_new(session); + state->body = xmlnode_from_str(body, -1); + state->post_action = MSN_GET_ADDRESS_SOAP_ACTION; + state->post_url = MSN_ADDRESS_BOOK_POST_URL; + state->cb = msn_get_address_cb; + msn_contact_request(state); g_free(update_str); g_free(body); @@ -841,105 +932,6 @@ * Contact Operations ***************************************************************/ -static const char * -msn_contact_operation_str(MsnCallbackAction action) -{ - /* Make sure this is large enough when adding more */ - static char buf[BUF_LEN]; - buf[0] = '\0'; - - if (action & MSN_ADD_BUDDY) - strcat(buf, "Adding Buddy,"); - if (action & MSN_MOVE_BUDDY) - strcat(buf, "Moving Buddy,"); - if (action & MSN_ACCEPTED_BUDDY) - strcat(buf, "Accepted Buddy,"); - if (action & MSN_DENIED_BUDDY) - strcat(buf, "Denied Buddy,"); - if (action & MSN_ADD_GROUP) - strcat(buf, "Adding Group,"); - if (action & MSN_DEL_GROUP) - strcat(buf, "Deleting Group,"); - if (action & MSN_RENAME_GROUP) - strcat(buf, "Renaming Group,"); - if (action & MSN_UPDATE_INFO) - strcat(buf, "Updating Contact Info,"); - - return buf; -} - -static gboolean msn_contact_request(MsnCallbackState *state); - -static void -msn_contact_request_cb(MsnSoapMessage *req, MsnSoapMessage *resp, - gpointer data) -{ - MsnCallbackState *state = data; - xmlnode *fault; - char *faultcode_str; - - if (resp == NULL) { - purple_debug_error("msn", - "Operation {%s} failed. No response received from server.\n", - msn_contact_operation_str(state->action)); - return; - } - - fault = xmlnode_get_child(resp->xml, "Body/Fault"); - - if (fault == NULL) { - /* No errors */ - if (state->cb) - ((MsnSoapCallback)state->cb)(req, resp, data); - msn_callback_state_free(state); - return; - } - - faultcode_str = xmlnode_get_data(xmlnode_get_child(fault, "faultcode")); - - if (faultcode_str && g_str_equal(faultcode_str, "q0:BadContextToken")) { - purple_debug_info("msn", - "Contact Operation {%s} failed because of bad token." - " Updating token now and retrying operation.\n", - msn_contact_operation_str(state->action)); - /* Token has expired, so renew it, and try again later */ - msn_nexus_update_token(state->session->nexus, MSN_AUTH_CONTACTS, - (GSourceFunc)msn_contact_request, data); - } - else - { - if (state->cb) { - ((MsnSoapCallback)state->cb)(req, resp, data); - } else { - /* We don't know how to respond to this faultcode, so log it */ - char *str = xmlnode_to_str(fault, NULL); - purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n", - msn_contact_operation_str(state->action), str); - g_free(str); - } - msn_callback_state_free(state); - } - - g_free(faultcode_str); -} - -static gboolean -msn_contact_request(MsnCallbackState *state) -{ - if (state->token == NULL) - state->token = xmlnode_get_child(state->body, - "Header/ABAuthHeader/TicketToken"); - /* delete old & replace with new token */ - xmlnode_free(state->token->child); - xmlnode_insert_data(state->token, - msn_nexus_get_token_str(state->session->nexus, MSN_AUTH_CONTACTS), -1); - msn_soap_message_send(state->session, - msn_soap_message_new(state->post_action, xmlnode_copy(state->body)), - MSN_CONTACT_SERVER, state->post_url, FALSE, - msn_contact_request_cb, state); - return FALSE; -} - static void msn_add_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/protocols/msn/contact.h --- a/libpurple/protocols/msn/contact.h Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/protocols/msn/contact.h Thu Feb 05 11:47:40 2009 +0000 @@ -52,7 +52,7 @@ ""\ ""\ "false"\ - "%s"\ + "EMPTY"\ ""\ ""\ ""\ @@ -94,7 +94,7 @@ ""\ ""\ "false"\ - "%s"\ + "EMPTY"\ ""\ ""\ ""\ @@ -135,7 +135,7 @@ ""\ ""\ "false"\ - "%s"\ + "EMPTY"\ ""\ ""\ ""\ @@ -619,6 +619,15 @@ MSN_UPDATE_INFO = 0x80 } MsnCallbackAction; +typedef enum +{ + MSN_PS_INITIAL, + MSN_PS_SAVE_CONTACT, + MSN_PS_PENDING_LIST, + MSN_PS_CONTACT_API, + MSN_PS_BLOCK_UNBLOCK +} MsnSoapPartnerScenario; + typedef struct _MsnCallbackState MsnCallbackState; struct _MsnCallbackState @@ -636,19 +645,12 @@ const gchar *post_action; const gchar *post_url; MsnSoapCallback cb; + /* For msn_get_contact_list only */ + MsnSoapPartnerScenario partner_scenario; }; typedef enum { - MSN_PS_INITIAL, - MSN_PS_SAVE_CONTACT, - MSN_PS_PENDING_LIST, - MSN_PS_CONTACT_API, - MSN_PS_BLOCK_UNBLOCK -} MsnSoapPartnerScenario; - -typedef enum -{ MSN_UPDATE_DISPLAY, /* Real display name */ MSN_UPDATE_ALIAS, /* Aliased display name */ MSN_UPDATE_COMMENT diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/protocols/msn/msg.c --- a/libpurple/protocols/msn/msg.c Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/protocols/msn/msg.c Thu Feb 05 11:47:40 2009 +0000 @@ -23,6 +23,7 @@ */ #include "msn.h" #include "msg.h" +#include "msnutils.h" MsnMessage * msn_message_new(MsnMsgType type) @@ -804,3 +805,174 @@ g_string_free(str, TRUE); } + +/************************************************************************** + * Message Handlers + **************************************************************************/ +void +msn_plain_msg(MsnCmdProc *cmdproc, MsnMessage *msg) +{ + PurpleConnection *gc; + const char *body; + char *body_str; + char *body_enc; + char *body_final; + size_t body_len; + const char *passport; + const char *value; + + gc = cmdproc->session->account->gc; + + body = msn_message_get_bin_data(msg, &body_len); + body_str = g_strndup(body, body_len); + body_enc = g_markup_escape_text(body_str, -1); + g_free(body_str); + + passport = msg->remote_user; + + if (!strcmp(passport, "messenger@microsoft.com") && + strstr(body, "immediate security update")) + { + return; + } + +#if 0 + if ((value = msn_message_get_attr(msg, "User-Agent")) != NULL) + { + purple_debug_misc("msn", "User-Agent = '%s'\n", value); + } +#endif + + if ((value = msn_message_get_attr(msg, "X-MMS-IM-Format")) != NULL) + { + char *pre, *post; + + msn_parse_format(value, &pre, &post); + + body_final = g_strdup_printf("%s%s%s", pre ? pre : "", + body_enc ? body_enc : "", post ? post : ""); + + g_free(pre); + g_free(post); + g_free(body_enc); + } + else + { + body_final = body_enc; + } + + if (cmdproc->servconn->type == MSN_SERVCONN_SB) { + MsnSwitchBoard *swboard = cmdproc->data; + + swboard->flag |= MSN_SB_FLAG_IM; + + if (swboard->current_users > 1 || + ((swboard->conv != NULL) && + purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT)) + { + /* If current_users is always ok as it should then there is no need to + * check if this is a chat. */ + if (swboard->current_users <= 1) + purple_debug_misc("msn", "plain_msg: current_users(%d)\n", + swboard->current_users); + + serv_got_chat_in(gc, swboard->chat_id, passport, 0, body_final, + time(NULL)); + if (swboard->conv == NULL) + { + swboard->conv = purple_find_chat(gc, swboard->chat_id); + swboard->flag |= MSN_SB_FLAG_IM; + } + } + else + { + serv_got_im(gc, passport, body_final, 0, time(NULL)); + if (swboard->conv == NULL) + { + swboard->conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, + passport, purple_connection_get_account(gc)); + swboard->flag |= MSN_SB_FLAG_IM; + } + } + + } else { + serv_got_im(gc, passport, body_final, 0, time(NULL)); + } + + g_free(body_final); +} + +void +msn_control_msg(MsnCmdProc *cmdproc, MsnMessage *msg) +{ + PurpleConnection *gc; + char *passport; + + gc = cmdproc->session->account->gc; + passport = msg->remote_user; + + if (msn_message_get_attr(msg, "TypingUser") == NULL) + return; + + if (cmdproc->servconn->type == MSN_SERVCONN_SB) { + MsnSwitchBoard *swboard = cmdproc->data; + + if (swboard->current_users == 1) + { + serv_got_typing(gc, passport, MSN_TYPING_RECV_TIMEOUT, + PURPLE_TYPING); + } + + } else { + serv_got_typing(gc, passport, MSN_TYPING_RECV_TIMEOUT, + PURPLE_TYPING); + } +} + +void +msn_datacast_msg(MsnCmdProc *cmdproc, MsnMessage *msg) +{ + GHashTable *body; + const char *id; + body = msn_message_get_hashtable_from_body(msg); + + id = g_hash_table_lookup(body, "ID"); + + if (!strcmp(id, "1")) { + /* Nudge */ + PurpleAccount *account; + const char *user; + + account = cmdproc->session->account; + user = msg->remote_user; + + if (cmdproc->servconn->type == MSN_SERVCONN_SB) { + MsnSwitchBoard *swboard = cmdproc->data; + if (swboard->current_users > 1 || + ((swboard->conv != NULL) && + purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT)) + purple_prpl_got_attention_in_chat(account->gc, swboard->chat_id, user, MSN_NUDGE); + + else + purple_prpl_got_attention(account->gc, user, MSN_NUDGE); + + } else { + purple_prpl_got_attention(account->gc, user, MSN_NUDGE); + } + + } else if (!strcmp(id, "2")) { + /* Wink */ + + } else if (!strcmp(id, "3")) { + /* Voiceclip */ + + } else if (!strcmp(id, "4")) { + /* Action */ + + } else { + purple_debug_warning("msn", "Got unknown datacast with ID %s.\n", id); + } + + g_hash_table_destroy(body); +} + diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/protocols/msn/msg.h --- a/libpurple/protocols/msn/msg.h Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/protocols/msn/msg.h Thu Feb 05 11:47:40 2009 +0000 @@ -339,4 +339,10 @@ char *msn_message_to_string(MsnMessage *msg); +void msn_plain_msg(MsnCmdProc *cmdproc, MsnMessage *msg); + +void msn_control_msg(MsnCmdProc *cmdproc, MsnMessage *msg); + +void msn_datacast_msg(MsnCmdProc *cmdproc, MsnMessage *msg); + #endif /* _MSN_MSG_H_ */ diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/protocols/msn/msn.c --- a/libpurple/protocols/msn/msn.c Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/protocols/msn/msn.c Thu Feb 05 11:47:40 2009 +0000 @@ -853,11 +853,11 @@ types = g_list_append(types, status); status = purple_status_type_new_full(PURPLE_STATUS_INVISIBLE, - NULL, NULL, FALSE, TRUE, FALSE); + NULL, NULL, TRUE, TRUE, FALSE); types = g_list_append(types, status); status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, - NULL, NULL, FALSE, TRUE, FALSE); + NULL, NULL, TRUE, TRUE, FALSE); types = g_list_append(types, status); status = purple_status_type_new_full(PURPLE_STATUS_MOBILE, @@ -1176,6 +1176,7 @@ MsnMessage *msg; char *msgformat; char *msgtext; + size_t msglen; const char *username; purple_debug_info("msn", "send IM {%s} to %s\n", message, who); @@ -1203,13 +1204,23 @@ } msn_import_html(message, &msgformat, &msgtext); + msglen = strlen(msgtext); + if (msglen == 0) { + /* Stuff like
will be ignored. Don't send an empty message + if that's all there is. */ + g_free(msgtext); + g_free(msgformat); + + return 0; + } + if (msn_user_is_online(account, who) || msn_user_is_yahoo(account, who) || swboard != NULL) { /*User online or have a swboard open because it's invisible * and sent us a message,then send Online Instant Message*/ - if (strlen(msgtext) + strlen(msgformat) + strlen(VERSION) > 1564) + if (msglen + strlen(msgformat) + strlen(VERSION) > 1564) { g_free(msgformat); g_free(msgtext); diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/protocols/msn/notification.c --- a/libpurple/protocols/msn/notification.c Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/protocols/msn/notification.c Thu Feb 05 11:47:40 2009 +0000 @@ -2096,6 +2096,13 @@ msn_table_add_msg_type(cbs_table, "application/x-msmsgssystemmessage", system_msg); + /* generic message handlers */ + msn_table_add_msg_type(cbs_table, "text/plain", + msn_plain_msg); + msn_table_add_msg_type(cbs_table, "text/x-msmsgscontrol", + msn_control_msg); + msn_table_add_msg_type(cbs_table, "text/x-msnmsgr-datacast", + msn_datacast_msg); } void diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/protocols/msn/soap.c --- a/libpurple/protocols/msn/soap.c Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/protocols/msn/soap.c Thu Feb 05 11:47:40 2009 +0000 @@ -342,12 +342,14 @@ } if (fault || body) { - MsnSoapRequest *request = conn->current_request; - conn->current_request = NULL; - request->cb(request->message, response, - request->cb_data); + if (conn->current_request) { + MsnSoapRequest *request = conn->current_request; + conn->current_request = NULL; + request->cb(request->message, response, + request->cb_data); + msn_soap_request_destroy(request, FALSE); + } msn_soap_message_destroy(response); - msn_soap_request_destroy(request, FALSE); } return TRUE; diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/protocols/msn/switchboard.c --- a/libpurple/protocols/msn/switchboard.c Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/protocols/msn/switchboard.c Thu Feb 05 11:47:40 2009 +0000 @@ -887,113 +887,6 @@ * Message Handlers **************************************************************************/ static void -plain_msg(MsnCmdProc *cmdproc, MsnMessage *msg) -{ - PurpleConnection *gc; - MsnSwitchBoard *swboard; - const char *body; - char *body_str; - char *body_enc; - char *body_final; - size_t body_len; - const char *passport; - const char *value; - - gc = cmdproc->session->account->gc; - swboard = cmdproc->data; - - body = msn_message_get_bin_data(msg, &body_len); - body_str = g_strndup(body, body_len); - body_enc = g_markup_escape_text(body_str, -1); - g_free(body_str); - - passport = msg->remote_user; - - if (!strcmp(passport, "messenger@microsoft.com") && - strstr(body, "immediate security update")) - { - return; - } - -#if 0 - if ((value = msn_message_get_attr(msg, "User-Agent")) != NULL) - { - purple_debug_misc("msn", "User-Agent = '%s'\n", value); - } -#endif - - if ((value = msn_message_get_attr(msg, "X-MMS-IM-Format")) != NULL) - { - char *pre, *post; - - msn_parse_format(value, &pre, &post); - - body_final = g_strdup_printf("%s%s%s", pre ? pre : "", - body_enc ? body_enc : "", post ? post : ""); - - g_free(pre); - g_free(post); - g_free(body_enc); - } - else - { - body_final = body_enc; - } - - swboard->flag |= MSN_SB_FLAG_IM; - - if (swboard->current_users > 1 || - ((swboard->conv != NULL) && - purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT)) - { - /* If current_users is always ok as it should then there is no need to - * check if this is a chat. */ - if (swboard->current_users <= 1) - purple_debug_misc("msn", "plain_msg: current_users(%d)\n", - swboard->current_users); - - serv_got_chat_in(gc, swboard->chat_id, passport, 0, body_final, - time(NULL)); - if (swboard->conv == NULL) - { - swboard->conv = purple_find_chat(gc, swboard->chat_id); - swboard->flag |= MSN_SB_FLAG_IM; - } - } - else - { - serv_got_im(gc, passport, body_final, 0, time(NULL)); - if (swboard->conv == NULL) - { - swboard->conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, - passport, purple_connection_get_account(gc)); - swboard->flag |= MSN_SB_FLAG_IM; - } - } - - g_free(body_final); -} - -static void -control_msg(MsnCmdProc *cmdproc, MsnMessage *msg) -{ - PurpleConnection *gc; - MsnSwitchBoard *swboard; - char *passport; - - gc = cmdproc->session->account->gc; - swboard = cmdproc->data; - passport = msg->remote_user; - - if (swboard->current_users == 1 && - msn_message_get_attr(msg, "TypingUser") != NULL) - { - serv_got_typing(gc, passport, MSN_TYPING_RECV_TIMEOUT, - PURPLE_TYPING); - } -} - -static void clientcaps_msg(MsnCmdProc *cmdproc, MsnMessage *msg) { #if 0 @@ -1012,49 +905,6 @@ #endif } -static void -datacast_msg(MsnCmdProc *cmdproc, MsnMessage *msg) -{ - GHashTable *body; - const char *id; - body = msn_message_get_hashtable_from_body(msg); - - id = g_hash_table_lookup(body, "ID"); - - if (!strcmp(id, "1")) { - /* Nudge */ - MsnSwitchBoard *swboard; - PurpleAccount *account; - const char *user; - - swboard = cmdproc->data; - account = cmdproc->session->account; - user = msg->remote_user; - - if (swboard->current_users > 1 || - ((swboard->conv != NULL) && - purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT)) - purple_prpl_got_attention_in_chat(account->gc, swboard->chat_id, user, MSN_NUDGE); - - else - purple_prpl_got_attention(account->gc, user, MSN_NUDGE); - - } else if (!strcmp(id, "2")) { - /* Wink */ - - } else if (!strcmp(id, "3")) { - /* Voiceclip */ - - } else if (!strcmp(id, "4")) { - /* Action */ - - } else { - purple_debug_warning("msn", "Got unknown datacast with ID %s.\n", id); - } - - g_hash_table_destroy(body); -} - /************************************************************************** * Connect stuff **************************************************************************/ @@ -1372,9 +1222,9 @@ /* Register the message type callbacks. */ msn_table_add_msg_type(cbs_table, "text/plain", - plain_msg); + msn_plain_msg); msn_table_add_msg_type(cbs_table, "text/x-msmsgscontrol", - control_msg); + msn_control_msg); msn_table_add_msg_type(cbs_table, "text/x-clientcaps", clientcaps_msg); msn_table_add_msg_type(cbs_table, "text/x-clientinfo", @@ -1384,9 +1234,9 @@ msn_table_add_msg_type(cbs_table, "text/x-mms-emoticon", msn_emoticon_msg); msn_table_add_msg_type(cbs_table, "text/x-mms-animemoticon", - msn_emoticon_msg); + msn_emoticon_msg); msn_table_add_msg_type(cbs_table, "text/x-msnmsgr-datacast", - datacast_msg); + msn_datacast_msg); #if 0 msn_table_add_msg_type(cbs_table, "text/x-msmmsginvite", msn_invite_msg); diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/protocols/msn/user.c --- a/libpurple/protocols/msn/user.c Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/protocols/msn/user.c Thu Feb 05 11:47:40 2009 +0000 @@ -180,7 +180,7 @@ gboolean msn_user_set_friendly_name(MsnUser *user, const char *name) { - g_return_if_fail(user != NULL); + g_return_val_if_fail(user != NULL, FALSE); if (user->friendly_name && name && !strcmp(user->friendly_name, name)) return FALSE; diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/protocols/novell/novell.c --- a/libpurple/protocols/novell/novell.c Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/protocols/novell/novell.c Thu Feb 05 11:47:40 2009 +0000 @@ -2976,7 +2976,7 @@ NULL, TRUE, TRUE, FALSE); status_types = g_list_append(status_types, type); - type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, NULL, NULL, FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, NULL, NULL, TRUE, TRUE, FALSE); status_types = g_list_append(status_types, type); return status_types; diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/protocols/oscar/oscar.c --- a/libpurple/protocols/oscar/oscar.c Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/protocols/oscar/oscar.c Thu Feb 05 11:47:40 2009 +0000 @@ -4782,7 +4782,7 @@ if (status_html != NULL) { status_text = purple_markup_strip_html(status_html); - /* If the status_text is longer than 60 character then truncate it */ + /* If the status_text is longer than 251 characters then truncate it */ if (strlen(status_text) > MAXAVAILMSGLEN) { char *tmp = g_utf8_find_prev_char(status_text, &status_text[MAXAVAILMSGLEN - 2]); @@ -4799,9 +4799,28 @@ } else { + char *status_text = NULL; + htmlaway = purple_status_get_attr_string(status, "message"); if ((htmlaway == NULL) || (*htmlaway == '\0')) htmlaway = purple_status_type_get_name(status_type); + + /* ICQ 6.x seems to use an available message for all statuses so set one */ + if (od->icq) + { + status_text = purple_markup_strip_html(htmlaway); + /* If the status_text is longer than 251 characters then truncate it */ + if (strlen(status_text) > MAXAVAILMSGLEN) + { + char *tmp = g_utf8_find_prev_char(status_text, &status_text[MAXAVAILMSGLEN - 2]); + strcpy(tmp, "..."); + } + aim_srv_setextrainfo(od, FALSE, 0, TRUE, status_text, NULL); + } + g_free(status_text); + + /* Set a proper away message for icq too so that they work for old third party clients */ + away = purple_prpl_oscar_convert_to_infotext(htmlaway, &awaylen, &away_encoding); if (awaylen > od->rights.maxawaymsglen) diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/protocols/oscar/oscar.h --- a/libpurple/protocols/oscar/oscar.h Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/protocols/oscar/oscar.h Thu Feb 05 11:47:40 2009 +0000 @@ -286,6 +286,15 @@ "us", "en", \ } +#define CLIENTINFO_ICQBASIC_14_34_3096 { \ + "ICQBasic", \ + 0x010a, \ + 0x0014, 0x0034, \ + 0x0000, 0x0c18, \ + 0x0000043d, \ + "us", "en", \ +} + #define CLIENTINFO_NETSCAPE_7_0_1 { \ "Netscape 2000 an approved user of AOL Instant Messenger (SM)", \ 0x1d0d, \ @@ -312,14 +321,14 @@ #define CLIENTINFO_PURPLE_ICQ { \ "Purple/" VERSION, \ 0x010a, \ - 0x0006, 0x0000, \ - 0x0000, 0x17ab, \ - 0x00007535, \ + 0x0014, 0x0034, \ + 0x0000, 0x0c18, \ + 0x0000043d, \ "us", "en", \ } #define CLIENTINFO_AIM_KNOWNGOOD CLIENTINFO_AIM_5_1_3036 -#define CLIENTINFO_ICQ_KNOWNGOOD CLIENTINFO_ICQBASIC_14_34_3000 +#define CLIENTINFO_ICQ_KNOWNGOOD CLIENTINFO_ICQBASIC_14_34_3096 typedef enum { diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/protocols/qq/qq.c --- a/libpurple/protocols/qq/qq.c Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/protocols/qq/qq.c Thu Feb 05 11:47:40 2009 +0000 @@ -406,15 +406,15 @@ GList *types = NULL; status = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, - "available", _("Available"), FALSE, TRUE, FALSE); + "available", _("Available"), TRUE, TRUE, FALSE); types = g_list_append(types, status); status = purple_status_type_new_full(PURPLE_STATUS_AWAY, - "away", _("Away"), FALSE, TRUE, FALSE); + "away", _("Away"), TRUE, TRUE, FALSE); types = g_list_append(types, status); status = purple_status_type_new_full(PURPLE_STATUS_INVISIBLE, - "invisible", _("Invisible"), FALSE, TRUE, FALSE); + "invisible", _("Invisible"), TRUE, TRUE, FALSE); types = g_list_append(types, status); status = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, @@ -422,7 +422,7 @@ types = g_list_append(types, status); status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, - "offline", _("Offline"), FALSE, TRUE, FALSE); + "offline", _("Offline"), TRUE, TRUE, FALSE); types = g_list_append(types, status); status = purple_status_type_new_full(PURPLE_STATUS_MOBILE, diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/protocols/silc/silc.c --- a/libpurple/protocols/silc/silc.c Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/protocols/silc/silc.c Thu Feb 05 11:47:40 2009 +0000 @@ -48,19 +48,19 @@ PurpleStatusType *type; GList *types = NULL; - type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_AVAILABLE, NULL, FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_AVAILABLE, NULL, TRUE, TRUE, FALSE); types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_HYPER, _("Hyper Active"), FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_HYPER, _("Hyper Active"), TRUE, TRUE, FALSE); types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_AWAY, NULL, FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_AWAY, NULL, TRUE, TRUE, FALSE); types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, SILCPURPLE_STATUS_ID_BUSY, _("Busy"), FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, SILCPURPLE_STATUS_ID_BUSY, _("Busy"), TRUE, TRUE, FALSE); types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_INDISPOSED, _("Indisposed"), FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_INDISPOSED, _("Indisposed"), TRUE, TRUE, FALSE); types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_PAGE, _("Wake Me Up"), FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_PAGE, _("Wake Me Up"), TRUE, TRUE, FALSE); types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, SILCPURPLE_STATUS_ID_OFFLINE, NULL, FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, SILCPURPLE_STATUS_ID_OFFLINE, NULL, TRUE, TRUE, FALSE); types = g_list_append(types, type); return types; @@ -1357,6 +1357,7 @@ char tmp[256]; SilcClientEntry client_entry; SilcDList list; + gboolean free_list = FALSE; convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, im->nick, sg->account); @@ -1373,6 +1374,8 @@ im->nick, FALSE); if (!clients) goto err; + + free_list = TRUE; } silc_dlist_start(clients); @@ -1413,6 +1416,9 @@ purple_conversation_write(convo, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL)); out: + if (free_list) { + silc_client_list_free(client, conn, clients); + } g_free(im->nick); g_free(im->message); silc_free(im); diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/protocols/silc10/silc.c --- a/libpurple/protocols/silc10/silc.c Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/protocols/silc10/silc.c Thu Feb 05 11:47:40 2009 +0000 @@ -39,19 +39,19 @@ PurpleStatusType *type; GList *types = NULL; - type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_AVAILABLE, NULL, FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_AVAILABLE, NULL, TRUE, TRUE, FALSE); types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_HYPER, _("Hyper Active"), FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_HYPER, _("Hyper Active"), TRUE, TRUE, FALSE); types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_AWAY, NULL, FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_AWAY, NULL, TRUE, TRUE, FALSE); types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, SILCPURPLE_STATUS_ID_BUSY, _("Busy"), FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, SILCPURPLE_STATUS_ID_BUSY, _("Busy"), TRUE, TRUE, FALSE); types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_INDISPOSED, _("Indisposed"), FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_INDISPOSED, _("Indisposed"), TRUE, TRUE, FALSE); types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_PAGE, _("Wake Me Up"), FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_PAGE, _("Wake Me Up"), TRUE, TRUE, FALSE); types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, SILCPURPLE_STATUS_ID_OFFLINE, NULL, FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, SILCPURPLE_STATUS_ID_OFFLINE, NULL, TRUE, TRUE, FALSE); types = g_list_append(types, type); return types; diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/xmlnode.c --- a/libpurple/xmlnode.c Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/xmlnode.c Thu Feb 05 11:47:40 2009 +0000 @@ -303,7 +303,7 @@ node->prefix = g_strdup(prefix); } -const char *xmlnode_get_prefix(xmlnode *node) +const char *xmlnode_get_prefix(const xmlnode *node) { g_return_val_if_fail(node != NULL, NULL); return node->prefix; @@ -449,11 +449,11 @@ } static char * -xmlnode_to_str_helper(xmlnode *node, int *len, gboolean formatting, int depth) +xmlnode_to_str_helper(const xmlnode *node, int *len, gboolean formatting, int depth) { GString *text = g_string_new(""); const char *prefix; - xmlnode *c; + const xmlnode *c; char *node_name, *esc, *esc2, *tab = NULL; gboolean need_end = FALSE, pretty = formatting; @@ -543,13 +543,13 @@ } char * -xmlnode_to_str(xmlnode *node, int *len) +xmlnode_to_str(const xmlnode *node, int *len) { return xmlnode_to_str_helper(node, len, FALSE, 0); } char * -xmlnode_to_formatted_str(xmlnode *node, int *len) +xmlnode_to_formatted_str(const xmlnode *node, int *len) { char *xml, *xml_with_declaration; diff -r 23038e1a1754 -r f06eb6e7d907 libpurple/xmlnode.h --- a/libpurple/xmlnode.h Thu Feb 05 11:47:36 2009 +0000 +++ b/libpurple/xmlnode.h Thu Feb 05 11:47:40 2009 +0000 @@ -243,7 +243,7 @@ * @param node The node to get the prefix from * @return The prefix of this node */ -const char *xmlnode_get_prefix(xmlnode *node); +const char *xmlnode_get_prefix(const xmlnode *node); /** * Gets the parent node. @@ -263,7 +263,7 @@ * @return The node represented as a string. You must * g_free this string when finished using it. */ -char *xmlnode_to_str(xmlnode *node, int *len); +char *xmlnode_to_str(const xmlnode *node, int *len); /** * Returns the node in a string of human readable xml. @@ -275,7 +275,7 @@ * tab and new line characters. You must * g_free this string when finished using it. */ -char *xmlnode_to_formatted_str(xmlnode *node, int *len); +char *xmlnode_to_formatted_str(const xmlnode *node, int *len); /** * Creates a node from a string of XML. Calling this on the diff -r 23038e1a1754 -r f06eb6e7d907 pidgin/gtkconv.h diff -r 23038e1a1754 -r f06eb6e7d907 pidgin/gtkdialogs.c --- a/pidgin/gtkdialogs.c Thu Feb 05 11:47:36 2009 +0000 +++ b/pidgin/gtkdialogs.c Thu Feb 05 11:47:40 2009 +0000 @@ -198,7 +198,7 @@ {N_("Macedonian"), "mk", "Arangel Angov ", "arangel@linux.net.mk"}, {N_("Macedonian"), "mk", "Ivana Kirkovska", "ivana.kirkovska@gmail.com"}, {N_("Macedonian"), "mk", "Jovan Naumovski", "jovan@lugola.net"}, - {"Mongolian", "mn", "gooyo", NULL}, + {N_("Mongolian"), "mn", "gooyo", NULL}, {N_("Bokmål Norwegian"), "nb", "Hans Fredrik Nordhaug", "hans@nordhaug.priv.no"}, {N_("Nepali"), "ne", "Shyam Krishna Bal", "shyamkrishna_bal@yahoo.com"}, {N_("Dutch, Flemish"), "nl", "Vincent van Adrighem", "V.vanAdrighem@dirck.mine.nu"}, diff -r 23038e1a1754 -r f06eb6e7d907 pidgin/gtkmedia.c --- a/pidgin/gtkmedia.c Thu Feb 05 11:47:36 2009 +0000 +++ b/pidgin/gtkmedia.c Thu Feb 05 11:47:40 2009 +0000 @@ -33,6 +33,7 @@ #include "request.h" #include "gtkmedia.h" +#include "gtkutils.h" #ifdef USE_VV @@ -57,10 +58,9 @@ GstElement *send_level; GstElement *recv_level; - GtkWidget *calling; - GtkWidget *accept; - GtkWidget *reject; - GtkWidget *hangup; + GtkWidget *menubar; + GtkWidget *statusbar; + GtkWidget *mute; GtkWidget *send_progress; @@ -184,38 +184,123 @@ return FALSE; } +static int +pidgin_x_error_handler(Display *display, XErrorEvent *event) +{ + const gchar *error_type; + switch (event->error_code) { +#define XERRORCASE(type) case type: error_type = #type; break + XERRORCASE(BadAccess); + XERRORCASE(BadAlloc); + XERRORCASE(BadAtom); + XERRORCASE(BadColor); + XERRORCASE(BadCursor); + XERRORCASE(BadDrawable); + XERRORCASE(BadFont); + XERRORCASE(BadGC); + XERRORCASE(BadIDChoice); + XERRORCASE(BadImplementation); + XERRORCASE(BadLength); + XERRORCASE(BadMatch); + XERRORCASE(BadName); + XERRORCASE(BadPixmap); + XERRORCASE(BadRequest); + XERRORCASE(BadValue); + XERRORCASE(BadWindow); +#undef XERRORCASE + default: + error_type = "unknown"; + break; + } + purple_debug_error("media", "A %s Xlib error has occurred. " + "The program would normally crash now.\n", + error_type); + return 0; +} + +static void +menu_hangup(gpointer data, guint action, GtkWidget *item) +{ + PidginMedia *gtkmedia = PIDGIN_MEDIA(data); + purple_media_hangup(gtkmedia->priv->media); +} + +static GtkItemFactoryEntry menu_items[] = { + { N_("/_Media"), NULL, NULL, 0, "", NULL }, + { N_("/Media/_Hangup"), NULL, menu_hangup, 0, "", NULL }, +}; + +static gint menu_item_count = sizeof(menu_items) / sizeof(menu_items[0]); + +static const char * +item_factory_translate_func (const char *path, gpointer func_data) +{ + return _(path); +} + +static GtkWidget * +setup_menubar(PidginMedia *window) +{ + GtkItemFactory *item_factory; + GtkAccelGroup *accel_group; + GtkWidget *menu; + + accel_group = gtk_accel_group_new (); + gtk_window_add_accel_group(GTK_WINDOW(window), accel_group); + g_object_unref(accel_group); + + item_factory = gtk_item_factory_new(GTK_TYPE_MENU_BAR, + "
", accel_group); + + gtk_item_factory_set_translate_func(item_factory, + (GtkTranslateFunc)item_factory_translate_func, + NULL, NULL); + + gtk_item_factory_create_items(item_factory, menu_item_count, + menu_items, window); + g_signal_connect(G_OBJECT(accel_group), "accel-changed", + G_CALLBACK(pidgin_save_accels_cb), NULL); + + menu = gtk_item_factory_get_widget(item_factory, "
"); + + gtk_widget_show(menu); + return menu; +} + static void pidgin_media_init (PidginMedia *media) { GtkWidget *vbox, *hbox; media->priv = PIDGIN_MEDIA_GET_PRIVATE(media); + XSetErrorHandler(pidgin_x_error_handler); + vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); gtk_container_add(GTK_CONTAINER(media), vbox); + media->priv->statusbar = gtk_statusbar_new(); + gtk_box_pack_end(GTK_BOX(vbox), media->priv->statusbar, + FALSE, FALSE, 0); + gtk_statusbar_push(GTK_STATUSBAR(media->priv->statusbar), + 0, _("Calling...")); + gtk_widget_show(media->priv->statusbar); + + media->priv->menubar = setup_menubar(media); + gtk_box_pack_start(GTK_BOX(vbox), media->priv->menubar, + FALSE, TRUE, 0); + hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); gtk_widget_show(GTK_WIDGET(hbox)); - media->priv->calling = gtk_label_new("Calling..."); - media->priv->hangup = gtk_button_new_with_mnemonic("_Hangup"); - media->priv->accept = gtk_button_new_with_mnemonic("_Accept"); - media->priv->reject = gtk_button_new_with_mnemonic("_Reject"); media->priv->mute = gtk_toggle_button_new_with_mnemonic("_Mute"); g_signal_connect(media->priv->mute, "toggled", G_CALLBACK(pidgin_media_mute_toggled), media); - gtk_box_pack_end(GTK_BOX(hbox), media->priv->reject, FALSE, FALSE, 0); - gtk_box_pack_end(GTK_BOX(hbox), media->priv->accept, FALSE, FALSE, 0); - gtk_box_pack_end(GTK_BOX(hbox), media->priv->hangup, FALSE, FALSE, 0); gtk_box_pack_end(GTK_BOX(hbox), media->priv->mute, FALSE, FALSE, 0); - gtk_box_pack_end(GTK_BOX(hbox), media->priv->calling, FALSE, FALSE, 0); - gtk_widget_show_all(media->priv->accept); - gtk_widget_show_all(media->priv->reject); - - media->priv->display = gtk_vbox_new(TRUE, PIDGIN_HIG_BOX_SPACE); + media->priv->display = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); gtk_box_pack_start(GTK_BOX(vbox), media->priv->display, TRUE, TRUE, PIDGIN_HIG_BOX_SPACE); gtk_widget_show(vbox); @@ -285,6 +370,7 @@ purple_debug_info("gtkmedia", "pidgin_media_dispose\n"); if (gtkmedia->priv->media) { + purple_request_close_with_handle(gtkmedia); purple_media_remove_output_windows(gtkmedia->priv->media); pidgin_media_disconnect_levels(gtkmedia->priv->media, gtkmedia); g_object_unref(gtkmedia->priv->media); @@ -366,6 +452,8 @@ if (conv != NULL) purple_conversation_write(conv, NULL, error, PURPLE_MESSAGE_ERROR, time(NULL)); + gtk_statusbar_push(GTK_STATUSBAR(gtkmedia->priv->statusbar), + 0, error); } static void @@ -374,6 +462,8 @@ { pidgin_media_set_state(gtkmedia, PIDGIN_MEDIA_ACCEPTED); pidgin_media_emit_message(gtkmedia, _("Call in progress.")); + gtk_statusbar_push(GTK_STATUSBAR(gtkmedia->priv->statusbar), + 0, _("Call in progress.")); gtk_widget_show(GTK_WIDGET(gtkmedia)); } @@ -430,6 +520,7 @@ GtkWidget *remote_video; GtkWidget *plug; GtkWidget *socket; + GdkColor color = {0, 0, 0, 0}; aspect = gtk_aspect_frame_new(NULL, 0.5, 0.5, 4.0/3.0, FALSE); gtk_frame_set_shadow_type(GTK_FRAME(aspect), GTK_SHADOW_IN); @@ -454,10 +545,11 @@ data->participant = g_strdup(gtkmedia->priv->screenname); remote_video = gtk_drawing_area_new(); + gtk_widget_modify_bg(remote_video, GTK_STATE_NORMAL, &color); g_signal_connect(G_OBJECT(remote_video), "realize", G_CALLBACK(realize_cb), data); gtk_container_add(GTK_CONTAINER(plug), remote_video); - gtk_widget_set_size_request (GTK_WIDGET(remote_video), -1, 100); + gtk_widget_set_size_request (GTK_WIDGET(remote_video), 320, 240); gtk_widget_show(remote_video); gtk_widget_show(aspect); @@ -469,6 +561,7 @@ GtkWidget *local_video; GtkWidget *plug; GtkWidget *socket; + GdkColor color = {0, 0, 0, 0}; aspect = gtk_aspect_frame_new(NULL, 0.5, 0.5, 4.0/3.0, FALSE); gtk_frame_set_shadow_type(GTK_FRAME(aspect), GTK_SHADOW_IN); @@ -493,10 +586,11 @@ data->participant = NULL; local_video = gtk_drawing_area_new(); + gtk_widget_modify_bg(local_video, GTK_STATE_NORMAL, &color); g_signal_connect(G_OBJECT(local_video), "realize", G_CALLBACK(realize_cb), data); gtk_container_add(GTK_CONTAINER(plug), local_video); - gtk_widget_set_size_request (GTK_WIDGET(local_video), -1, 100); + gtk_widget_set_size_request (GTK_WIDGET(local_video), 160, 120); gtk_widget_show(local_video); gtk_widget_show(aspect); @@ -506,14 +600,14 @@ if (type & PURPLE_MEDIA_RECV_AUDIO) { gtkmedia->priv->recv_progress = gtk_progress_bar_new(); - gtk_widget_set_size_request(gtkmedia->priv->recv_progress, 70, 10); + gtk_widget_set_size_request(gtkmedia->priv->recv_progress, 320, 10); gtk_box_pack_end(GTK_BOX(recv_widget), gtkmedia->priv->recv_progress, FALSE, FALSE, 0); gtk_widget_show(gtkmedia->priv->recv_progress); } if (type & PURPLE_MEDIA_SEND_AUDIO) { gtkmedia->priv->send_progress = gtk_progress_bar_new(); - gtk_widget_set_size_request(gtkmedia->priv->send_progress, 70, 10); + gtk_widget_set_size_request(gtkmedia->priv->send_progress, 320, 10); gtk_box_pack_end(GTK_BOX(send_widget), gtkmedia->priv->send_progress, FALSE, FALSE, 0); gtk_widget_show(gtkmedia->priv->send_progress); @@ -551,6 +645,8 @@ pidgin_media_emit_message(gtkmedia, message); g_free(message); } + + gtk_widget_show(gtkmedia->priv->display); } static void @@ -573,15 +669,6 @@ } else if (type == PURPLE_MEDIA_STATE_CHANGED_NEW && sid != NULL && name != NULL) { pidgin_media_ready_cb(media, gtkmedia, sid); - } else if (type == PURPLE_MEDIA_STATE_CHANGED_CONNECTED) { - GstElement *audiosendbin = NULL, *audiorecvbin = NULL; - GstElement *videosendbin = NULL, *videorecvbin = NULL; - - purple_media_get_elements(media, &audiosendbin, &audiorecvbin, - &videosendbin, &videorecvbin); - - if (audiorecvbin || audiosendbin || videorecvbin || videosendbin) - gtk_widget_show(gtkmedia->priv->display); } } @@ -608,13 +695,6 @@ else pidgin_media_set_state(media, PIDGIN_MEDIA_REQUESTED); - g_signal_connect_swapped(G_OBJECT(media->priv->accept), "clicked", - G_CALLBACK(purple_media_accept), media->priv->media); - g_signal_connect_swapped(G_OBJECT(media->priv->reject), "clicked", - G_CALLBACK(purple_media_reject), media->priv->media); - g_signal_connect_swapped(G_OBJECT(media->priv->hangup), "clicked", - G_CALLBACK(purple_media_hangup), media->priv->media); - g_signal_connect(G_OBJECT(media->priv->media), "error", G_CALLBACK(pidgin_media_error_cb), media); g_signal_connect(G_OBJECT(media->priv->media), "accepted", @@ -686,28 +766,6 @@ pidgin_media_set_state(PidginMedia *gtkmedia, PidginMediaState state) { gtkmedia->priv->state = state; - switch (state) { - case PIDGIN_MEDIA_WAITING: - gtk_widget_show(gtkmedia->priv->calling); - gtk_widget_hide(gtkmedia->priv->accept); - gtk_widget_hide(gtkmedia->priv->reject); - gtk_widget_show(gtkmedia->priv->hangup); - break; - case PIDGIN_MEDIA_REQUESTED: - gtk_widget_hide(gtkmedia->priv->calling); - gtk_widget_show(gtkmedia->priv->accept); - gtk_widget_show(gtkmedia->priv->reject); - gtk_widget_hide(gtkmedia->priv->hangup); - break; - case PIDGIN_MEDIA_ACCEPTED: - gtk_widget_show(gtkmedia->priv->hangup); - gtk_widget_hide(gtkmedia->priv->calling); - gtk_widget_hide(gtkmedia->priv->accept); - gtk_widget_hide(gtkmedia->priv->reject); - break; - default: - break; - } } static gboolean @@ -728,7 +786,7 @@ if (initiator == FALSE) { gchar *message = g_strdup_printf("%s wishes to start a " "media session with you\n", alias); - purple_request_accept_cancel(media, "Media invitation", + purple_request_accept_cancel(gtkmedia, "Media invitation", message, NULL, 1, (void*)pc, screenname, NULL, media, purple_media_accept, purple_media_reject); @@ -739,11 +797,88 @@ return TRUE; } +static GstElement * +create_default_video_src(void) +{ + GstElement *ret = NULL; + purple_media_video_init_src(&ret); + return ret; +} + +static GstElement * +create_default_video_sink(void) +{ + GstElement *ret = NULL; + purple_media_video_init_recv(&ret); + return ret; +} + +static GstElement * +create_default_audio_src(void) +{ + GstElement *ret = NULL, *level = NULL; + purple_media_audio_init_src(&ret, &level); + return ret; +} + +static GstElement * +create_default_audio_sink(void) +{ + GstElement *ret = NULL, *level = NULL; + purple_media_audio_init_recv(&ret, &level); + return ret; +} + +static PurpleMediaElementInfo default_video_src = +{ + "pidgindefaultvideosrc", /* id */ + PURPLE_MEDIA_ELEMENT_VIDEO /* type */ + | PURPLE_MEDIA_ELEMENT_SRC + | PURPLE_MEDIA_ELEMENT_ONE_SRC + | PURPLE_MEDIA_ELEMENT_UNIQUE, + create_default_video_src, /* create */ +}; + +static PurpleMediaElementInfo default_video_sink = +{ + "pidgindefaultvideosink", /* id */ + PURPLE_MEDIA_ELEMENT_VIDEO /* type */ + | PURPLE_MEDIA_ELEMENT_SINK + | PURPLE_MEDIA_ELEMENT_ONE_SINK, + create_default_video_sink, /* create */ +}; + +static PurpleMediaElementInfo default_audio_src = +{ + "pidgindefaultaudiosrc", /* id */ + PURPLE_MEDIA_ELEMENT_AUDIO /* type */ + | PURPLE_MEDIA_ELEMENT_SRC + | PURPLE_MEDIA_ELEMENT_ONE_SRC + | PURPLE_MEDIA_ELEMENT_UNIQUE, + create_default_audio_src, /* create */ +}; + +static PurpleMediaElementInfo default_audio_sink = +{ + "pidgindefaultaudiosink", /* id */ + PURPLE_MEDIA_ELEMENT_AUDIO /* type */ + | PURPLE_MEDIA_ELEMENT_SINK + | PURPLE_MEDIA_ELEMENT_ONE_SINK, + create_default_audio_sink, /* create */ +}; + void pidgin_medias_init(void) { - g_signal_connect(G_OBJECT(purple_media_manager_get()), "init-media", + PurpleMediaManager *manager = purple_media_manager_get(); + g_signal_connect(G_OBJECT(manager), "init-media", G_CALLBACK(pidgin_media_new_cb), NULL); + + purple_debug_info("gtkmedia", "Registering media element types\n"); + purple_media_manager_set_active_element(manager, &default_video_src); + purple_media_manager_set_active_element(manager, &default_video_sink); + purple_media_manager_set_active_element(manager, &default_audio_src); + purple_media_manager_set_active_element(manager, &default_audio_sink); } #endif /* USE_VV */ diff -r 23038e1a1754 -r f06eb6e7d907 pidgin/plugins/win32/winprefs/winprefs.c --- a/pidgin/plugins/win32/winprefs/winprefs.c Thu Feb 05 11:47:36 2009 +0000 +++ b/pidgin/plugins/win32/winprefs/winprefs.c Thu Feb 05 11:47:40 2009 +0000 @@ -55,9 +55,8 @@ static const char *PREF_DBLIST_HEIGHT = "/plugins/gtk/win32/winprefs/dblist_height"; static const char *PREF_DBLIST_SIDE = "/plugins/gtk/win32/winprefs/dblist_side"; static const char *PREF_BLIST_ON_TOP = "/plugins/gtk/win32/winprefs/blist_on_top"; +/* Deprecated */ static const char *PREF_CHAT_BLINK = "/plugins/gtk/win32/winprefs/chat_blink"; - -/* Deprecated */ static const char *PREF_DBLIST_ON_TOP = "/plugins/gtk/win32/winprefs/dblist_on_top"; static PurplePlugin *handle = NULL; @@ -229,17 +228,6 @@ blist_set_ontop(FALSE); } -static gboolean -winpidgin_conv_chat_blink(PurpleAccount *account, const char *who, char **message, - PurpleConversation *conv, PurpleMessageFlags flags, void *data) -{ - if(purple_prefs_get_bool(PREF_CHAT_BLINK)) - winpidgin_conv_blink(conv, flags); - - return FALSE; -} - - /* * EXPORTED FUNCTIONS */ @@ -258,10 +246,6 @@ purple_signal_connect(pidgin_blist_get_handle(), "gtkblist-created", plugin, PURPLE_CALLBACK(blist_create_cb), NULL); - purple_signal_connect(pidgin_conversations_get_handle(), - "displaying-chat-msg", plugin, PURPLE_CALLBACK(winpidgin_conv_chat_blink), - NULL); - purple_signal_connect((void*)purple_get_core(), "quitting", plugin, PURPLE_CALLBACK(purple_quit_cb), NULL); @@ -336,11 +320,6 @@ _("Only when docked"), BLIST_TOP_DOCKED, NULL); - /* Conversations */ - vbox = pidgin_make_frame(ret, _("Conversations")); - pidgin_prefs_checkbox(_("_Flash window when chat messages are received"), - PREF_CHAT_BLINK, vbox); - gtk_widget_show_all(ret); return ret; } @@ -399,7 +378,6 @@ purple_prefs_add_bool(PREF_DBLIST_DOCKED, FALSE); purple_prefs_add_int(PREF_DBLIST_HEIGHT, 0); purple_prefs_add_int(PREF_DBLIST_SIDE, 0); - purple_prefs_add_bool(PREF_CHAT_BLINK, FALSE); /* Convert old preferences */ if(purple_prefs_exists(PREF_DBLIST_ON_TOP)) { @@ -413,6 +391,7 @@ purple_prefs_add_int(PREF_BLIST_ON_TOP, blist_top); } else purple_prefs_add_int(PREF_BLIST_ON_TOP, BLIST_TOP_NEVER); + purple_prefs_remove(PREF_CHAT_BLINK); } PURPLE_INIT_PLUGIN(winprefs, init_plugin, info) diff -r 23038e1a1754 -r f06eb6e7d907 pidgin/win32/nsis/pidgin-installer.nsi --- a/pidgin/win32/nsis/pidgin-installer.nsi Thu Feb 05 11:47:36 2009 +0000 +++ b/pidgin/win32/nsis/pidgin-installer.nsi Thu Feb 05 11:47:40 2009 +0000 @@ -1247,6 +1247,9 @@ Push $R0 Push $R1 Push $R2 + Push $R3 ; This is only used for the Parameters throughout the function + + ${GetParameters} $R3 IntOp $R1 0 + 0 retry_runcheck: @@ -1258,7 +1261,14 @@ IntCmp $R0 0 +3 ;This could check for ERROR_ALREADY_EXISTS(183), but lets just assume MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION $(INSTALLER_IS_RUNNING) /SD IDCANCEL IDRETRY retry_runcheck Abort + + ; Allow installer to run even if pidgin is running via "/NOPIDGINRUNCHECK=1" + ; This is useful for testing + ClearErrors + ${GetOptions} "$R3" "/NOPIDGINRUNCHECK=" $R1 + IfErrors 0 +2 Call RunCheck + StrCpy $name "Pidgin ${PIDGIN_VERSION}" StrCpy $SPELLCHECK_SEL "" @@ -1312,16 +1322,13 @@ SetShellVarContext "current" StrCpy $ISSILENT "/S" - - ; GTK installer has two silent states.. one with Message boxes, one without + ; GTK installer has two silent states - one with Message boxes, one without ; If pidgin installer was run silently, we want to supress gtk installer msg boxes. - IfSilent 0 set_gtk_normal - StrCpy $ISSILENT "/NOUI" - set_gtk_normal: + IfSilent 0 +2 + StrCpy $ISSILENT "/NOUI" - ${GetParameters} $R0 ClearErrors - ${GetOptions} "$R0" "/L=" $R1 + ${GetOptions} "$R3" "/L=" $R1 IfErrors +3 StrCpy $LANGUAGE $R1 Goto skip_lang @@ -1332,7 +1339,7 @@ skip_lang: ClearErrors - ${GetOptions} "$R0" "/DS=" $R1 + ${GetOptions} "$R3" "/DS=" $R1 IfErrors +8 SectionGetFlags ${SecDesktopShortcut} $R2 StrCmp "1" $R1 0 +2 @@ -1343,7 +1350,7 @@ SectionSetFlags ${SecDesktopShortcut} $R2 ClearErrors - ${GetOptions} "$R0" "/SMS=" $R1 + ${GetOptions} "$R3" "/SMS=" $R1 IfErrors +8 SectionGetFlags ${SecStartMenuShortcut} $R2 StrCmp "1" $R1 0 +2 @@ -1380,6 +1387,7 @@ instdir_done: ;LogSet on + Pop $R3 Pop $R2 Pop $R1 Pop $R0 @@ -1694,6 +1702,7 @@ Push $R1 Push $R2 Push $R3 + Push $R4 check: ClearErrors @@ -1714,7 +1723,12 @@ StrCmp $R3 "success" +3 StrCpy $R0 $R3 Goto done + ; Use a specific temporary $OUTDIR for each dictionary because the installer doesn't clean up after itself + StrCpy $R4 "$OUTDIR" + SetOutPath "$TEMP\aspell_dict-$R0" ExecWait '"$R1"' + SetOutPath "$R4" + RMDir /r "$TEMP\aspell_dict-$R0" Delete $R1 Goto check ; Check that it is now installed correctly @@ -1723,6 +1737,7 @@ StrCpy $R0 '' done: + Pop $R4 Pop $R3 Pop $R2 Pop $R1