# HG changeset patch # User sulabh@pidgin.im # Date 1284058140 0 # Node ID 8eb46a838ce011489d14ef0ffcec3ac5ab2ecbbe # Parent fc961dfd4122570097de02faf8a1c056940a4cee# Parent 4d8569be274c0560b2ed53b3f08110344f1f0096 propagate from branch 'im.pidgin.pidgin' (head 7b998c02b684a694f1089bee715e4a3952bbcf08) to branch 'im.pidgin.cpw.sulabh.yahoo' (head 15bec46c35831daf795a870712e4ef6dd91034ce) diff -r fc961dfd4122 -r 8eb46a838ce0 ChangeLog --- a/ChangeLog Wed Sep 01 20:29:32 2010 +0000 +++ b/ChangeLog Thu Sep 09 18:49:00 2010 +0000 @@ -15,8 +15,8 @@ * Remap the "Set User Mood" shortcut to Control-D, which does not conflict with the previous shortcut for Get Buddy Info on the selected buddy. - * Add a plugin action menu (unter Tools) for the Voice and Video Settings - plugin. + * Add a plugin action menu (under Tools) for the Voice and Video + Settings plugin. Finch: * Add support for drop-down account options (like the SILC cipher @@ -29,6 +29,8 @@ to Jan Kaluza) (#11560) * Restore the ability to connect to XMPP servers that do not offer Stream ID. (#12331) + * Added support for using Google's relay servers when making voice and + video calls to Google clients. Yahoo/Yahoo JAPAN: * Stop doing unnecessary lookups of certain alias information. This diff -r fc961dfd4122 -r 8eb46a838ce0 ChangeLog.API --- a/ChangeLog.API Wed Sep 01 20:29:32 2010 +0000 +++ b/ChangeLog.API Thu Sep 09 18:49:00 2010 +0000 @@ -1,6 +1,14 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul version 2.7.4 (MM/DD/YYYY): + Perl: + Added: + * Purple::BuddyList::Chat::get_components + + Changed: + * Purple::BuddyList::Chat::new now works properly. Thanks + to Rafael in devel@conference.pidgin.im for reporting and + testing. version 2.7.3 (08/10/2010): libpurple: diff -r fc961dfd4122 -r 8eb46a838ce0 libpurple/media/backend-fs2.c --- a/libpurple/media/backend-fs2.c Wed Sep 01 20:29:32 2010 +0000 +++ b/libpurple/media/backend-fs2.c Thu Sep 09 18:49:00 2010 +0000 @@ -1605,6 +1605,18 @@ PurpleMediaBackendFs2Session *session; PurpleMediaBackendFs2Stream *stream; FsParticipant *participant; + /* check if the prpl has already specified a relay-info + we need to do this to allow them to override when using non-standard + TURN modes, like Google f.ex. */ + gboolean got_turn_from_prpl = FALSE; + int i; + + for (i = 0 ; i < num_params ; i++) { + if (purple_strequal(params[i].name, "relay-info")) { + got_turn_from_prpl = TRUE; + break; + } + } memcpy(_params, params, sizeof(GParameter) * num_params); @@ -1624,7 +1636,7 @@ ++_num_params; } - if (turn_ip && !strcmp("nice", transmitter)) { + if (turn_ip && !strcmp("nice", transmitter) && !got_turn_from_prpl) { GValueArray *relay_info = g_value_array_new(0); gint port; const gchar *username = purple_prefs_get_string( diff -r fc961dfd4122 -r 8eb46a838ce0 libpurple/network.c --- a/libpurple/network.c Wed Sep 01 20:29:32 2010 +0000 +++ b/libpurple/network.c Thu Sep 09 18:49:00 2010 +0000 @@ -1077,12 +1077,10 @@ if (protocol) { purple_network_upnp_mapping_remove(&port, protocol, NULL); - g_hash_table_remove(upnp_port_mappings, protocol); } else { protocol = g_hash_table_lookup(nat_pmp_port_mappings, &port); if (protocol) { purple_network_nat_pmp_mapping_remove(&port, protocol, NULL); - g_hash_table_remove(nat_pmp_port_mappings, protocol); } } } diff -r fc961dfd4122 -r 8eb46a838ce0 libpurple/plugins/perl/common/BuddyList.xs --- a/libpurple/plugins/perl/common/BuddyList.xs Wed Sep 01 20:29:32 2010 +0000 +++ b/libpurple/plugins/perl/common/BuddyList.xs Thu Sep 09 18:49:00 2010 +0000 @@ -2,6 +2,13 @@ #include "module.h" #include "../perl-handlers.h" +static void +chat_components_foreach(gpointer key, gpointer value, gpointer user_data) +{ + HV *hv = user_data; + hv_store(hv, key, strlen(key), newSVpv(value, 0), 0); +} + MODULE = Purple::BuddyList PACKAGE = Purple PREFIX = purple_ PROTOTYPES: ENABLE @@ -331,6 +338,19 @@ purple_chat_get_name(chat) Purple::BuddyList::Chat chat +HV * +purple_chat_get_components(chat) + Purple::BuddyList::Chat chat +INIT: + HV * t_HV; + GHashTable * t_GHash; +CODE: + t_GHash = purple_chat_get_components(chat); + RETVAL = t_HV = newHV(); + g_hash_table_foreach(t_GHash, chat_components_foreach, t_HV); +OUTPUT: + RETVAL + Purple::BuddyList::Chat purple_chat_new(account, alias, components) Purple::Account account @@ -345,14 +365,14 @@ char *t_key, *t_value; CODE: t_HV = (HV *)SvRV(components); - t_GHash = g_hash_table_new(g_str_hash, g_str_equal); + t_GHash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); for (t_HE = hv_iternext(t_HV); t_HE != NULL; t_HE = hv_iternext(t_HV) ) { t_key = hv_iterkey(t_HE, &len); t_SV = *hv_fetch(t_HV, t_key, len, 0); t_value = SvPVutf8_nolen(t_SV); - g_hash_table_insert(t_GHash, t_key, t_value); + g_hash_table_insert(t_GHash, g_strdup(t_key), g_strdup(t_value)); } RETVAL = purple_chat_new(account, alias, t_GHash); diff -r fc961dfd4122 -r 8eb46a838ce0 libpurple/plugins/perl/common/Server.xs --- a/libpurple/plugins/perl/common/Server.xs Wed Sep 01 20:29:32 2010 +0000 +++ b/libpurple/plugins/perl/common/Server.xs Thu Sep 09 18:49:00 2010 +0000 @@ -144,6 +144,7 @@ g_hash_table_insert(t_GHash, t_key, t_value); } serv_join_chat(conn, t_GHash); + g_hash_table_destroy(t_GHash); void serv_move_buddy(buddy, group1, group2) diff -r fc961dfd4122 -r 8eb46a838ce0 libpurple/protocols/jabber/Makefile.am --- a/libpurple/protocols/jabber/Makefile.am Wed Sep 01 20:29:32 2010 +0000 +++ b/libpurple/protocols/jabber/Makefile.am Thu Sep 09 18:49:00 2010 +0000 @@ -38,6 +38,8 @@ google/google_session.h \ google/jingleinfo.c \ google/jingleinfo.h \ + google/relay.c \ + google/relay.h \ ibb.c \ ibb.h \ iq.c \ @@ -108,7 +110,10 @@ st = pkg_LTLIBRARIES = libjabber.la libxmpp.la libjabber_la_SOURCES = $(JABBERSOURCES) -libjabber_la_LIBADD = $(GLIB_LIBS) $(SASL_LIBS) $(LIBXML_LIBS) $(IDN_LIBS) +libjabber_la_LIBADD = $(GLIB_LIBS) $(SASL_LIBS) $(LIBXML_LIBS) $(IDN_LIBS)\ + $(FARSIGHT_LIBS) \ + $(GSTREAMER_LIBS) \ + $(GSTINTERFACES_LIBS) libxmpp_la_SOURCES = libxmpp.c libxmpp_la_LIBADD = libjabber.la @@ -121,4 +126,7 @@ $(DEBUG_CFLAGS) \ $(GLIB_CFLAGS) \ $(IDN_CFLAGS) \ - $(LIBXML_CFLAGS) + $(LIBXML_CFLAGS) \ + $(FARSIGHT_CFLAGS) \ + $(GSTREAMER_CFLAGS) \ + $(GSTINTERFACES_CFLAGS) diff -r fc961dfd4122 -r 8eb46a838ce0 libpurple/protocols/jabber/Makefile.mingw --- a/libpurple/protocols/jabber/Makefile.mingw Wed Sep 01 20:29:32 2010 +0000 +++ b/libpurple/protocols/jabber/Makefile.mingw Thu Sep 09 18:49:00 2010 +0000 @@ -61,6 +61,7 @@ google/google_roster.c \ google/google_session.c \ google/jingleinfo.c \ + google/relay.c \ ibb.c \ iq.c \ jabber.c \ diff -r fc961dfd4122 -r 8eb46a838ce0 libpurple/protocols/jabber/google/google_session.c --- a/libpurple/protocols/jabber/google/google_session.c Wed Sep 01 20:29:32 2010 +0000 +++ b/libpurple/protocols/jabber/google/google_session.c Thu Sep 09 18:49:00 2010 +0000 @@ -21,6 +21,7 @@ #include "internal.h" #include "debug.h" #include "google_session.h" +#include "relay.h" #include "jingle/jingle.h" @@ -29,6 +30,10 @@ typedef struct { PurpleMedia *media; gboolean video; + GList *remote_audio_candidates; /* list of PurpleMediaCandidate */ + GList *remote_video_candidates; /* list of PurpleMediaCandidate */ + gboolean added_streams; /* this indicates if the streams have been + to media (ie. after getting relay credentials */ } GoogleAVSessionData; static gboolean @@ -43,9 +48,18 @@ static void google_session_destroy(GoogleSession *session) { + GoogleAVSessionData *session_data = + (GoogleAVSessionData *) session->session_data; g_free(session->id.id); g_free(session->id.initiator); g_free(session->remote_jid); + + if (session_data->remote_audio_candidates) + purple_media_candidate_list_free(session_data->remote_audio_candidates); + + if (session_data->remote_video_candidates) + purple_media_candidate_list_free(session_data->remote_video_candidates); + g_free(session->session_data); g_free(session); } @@ -301,10 +315,14 @@ } static GParameter * -jabber_google_session_get_params(JabberStream *js, guint *num) +jabber_google_session_get_params(JabberStream *js, const gchar *relay_ip, + guint16 relay_udp, guint16 relay_tcp, guint16 relay_ssltcp, + const gchar *relay_username, const gchar *relay_password, guint *num) { guint num_params; - GParameter *params = jingle_get_params(js, &num_params); + GParameter *params = + jingle_get_params(js, relay_ip, relay_udp, relay_tcp, relay_ssltcp, + relay_username, relay_password, &num_params); GParameter *new_params = g_new0(GParameter, num_params + 1); memcpy(new_params, params, sizeof(GParameter) * num_params); @@ -320,6 +338,56 @@ } +static void +jabber_google_relay_response_session_initiate_cb(GoogleSession *session, + const gchar *relay_ip, guint relay_udp, guint relay_tcp, guint relay_ssltcp, + const gchar *relay_username, const gchar *relay_password) +{ + GParameter *params; + guint num_params; + JabberStream *js = session->js; + GoogleAVSessionData *session_data = + (GoogleAVSessionData *) session->session_data; + + session_data->media = purple_media_manager_create_media( + purple_media_manager_get(), + purple_connection_get_account(js->gc), + "fsrtpconference", session->remote_jid, TRUE); + + purple_media_set_prpl_data(session_data->media, session); + + g_signal_connect_swapped(G_OBJECT(session_data->media), + "candidates-prepared", + G_CALLBACK(google_session_ready), session); + g_signal_connect_swapped(G_OBJECT(session_data->media), "codecs-changed", + G_CALLBACK(google_session_ready), session); + g_signal_connect(G_OBJECT(session_data->media), "state-changed", + G_CALLBACK(google_session_state_changed_cb), session); + g_signal_connect(G_OBJECT(session_data->media), "stream-info", + G_CALLBACK(google_session_stream_info_cb), session); + + params = + jabber_google_session_get_params(js, relay_ip, relay_udp, relay_tcp, + relay_ssltcp, relay_username, relay_password, &num_params); + + if (purple_media_add_stream(session_data->media, "google-voice", + session->remote_jid, PURPLE_MEDIA_AUDIO, + TRUE, "nice", num_params, params) == FALSE || + (session_data->video && purple_media_add_stream( + session_data->media, "google-video", + session->remote_jid, PURPLE_MEDIA_VIDEO, + TRUE, "nice", num_params, params) == FALSE)) { + purple_media_error(session_data->media, "Error adding stream."); + purple_media_end(session_data->media, NULL, NULL); + g_free(params); + } else { + session_data->added_streams = TRUE; + } + + g_free(params); +} + + gboolean jabber_google_session_initiate(JabberStream *js, const gchar *who, PurpleMediaSessionType type) { @@ -327,10 +395,8 @@ JabberBuddy *jb; JabberBuddyResource *jbr; gchar *jid; - GParameter *params; - guint num_params; - GoogleAVSessionData *session_data; - + GoogleAVSessionData *session_data = NULL; + /* construct JID to send to */ jb = jabber_buddy_find(js, who, FALSE); if (!jb) { @@ -363,92 +429,42 @@ if (type & PURPLE_MEDIA_VIDEO) session_data->video = TRUE; - session_data->media = purple_media_manager_create_media( - purple_media_manager_get(), - purple_connection_get_account(js->gc), - "fsrtpconference", session->remote_jid, TRUE); - - purple_media_set_prpl_data(session_data->media, session); - - g_signal_connect_swapped(G_OBJECT(session_data->media), - "candidates-prepared", - G_CALLBACK(google_session_ready), session); - g_signal_connect_swapped(G_OBJECT(session_data->media), "codecs-changed", - G_CALLBACK(google_session_ready), session); - g_signal_connect(G_OBJECT(session_data->media), "state-changed", - G_CALLBACK(google_session_state_changed_cb), session); - g_signal_connect(G_OBJECT(session_data->media), "stream-info", - G_CALLBACK(google_session_stream_info_cb), session); - - params = jabber_google_session_get_params(js, &num_params); - - if (purple_media_add_stream(session_data->media, "google-voice", - session->remote_jid, PURPLE_MEDIA_AUDIO, - TRUE, "nice", num_params, params) == FALSE || - (session_data->video && purple_media_add_stream( - session_data->media, "google-video", - session->remote_jid, PURPLE_MEDIA_VIDEO, - TRUE, "nice", num_params, params) == FALSE)) { - purple_media_error(session_data->media, "Error adding stream."); - purple_media_end(session_data->media, NULL, NULL); - g_free(params); - return FALSE; + /* if we got a relay token and relay host in google:jingleinfo, issue an + HTTP request to get that data */ + if (js->google_relay_host && js->google_relay_token) { + jabber_google_do_relay_request(js, session, + jabber_google_relay_response_session_initiate_cb); + } else { + jabber_google_relay_response_session_initiate_cb(session, NULL, 0, 0, 0, + NULL, NULL); } - - g_free(params); - - return (session_data->media != NULL) ? TRUE : FALSE; + + /* we don't actually know yet wether it succeeded... maybe this is very + wrong... */ + return TRUE; } -static gboolean -google_session_handle_initiate(JabberStream *js, GoogleSession *session, xmlnode *sess, const char *iq_id) +static void +jabber_google_relay_response_session_handle_initiate_cb(GoogleSession *session, + const gchar *relay_ip, guint relay_udp, guint relay_tcp, guint relay_ssltcp, + const gchar *relay_username, const gchar *relay_password) { - JabberIq *result; - GList *codecs = NULL, *video_codecs = NULL; - xmlnode *desc_element, *codec_element; - PurpleMediaCodec *codec; - const char *xmlns; GParameter *params; guint num_params; + JabberStream *js = session->js; + xmlnode *codec_element; + xmlnode *desc_element; + const gchar *xmlns; + PurpleMediaCodec *codec; + GList *video_codecs = NULL; + GList *codecs = NULL; + JabberIq *result; GoogleAVSessionData *session_data = (GoogleAVSessionData *) session->session_data; - - if (session->state != UNINIT) { - purple_debug_error("jabber", "Received initiate for active session.\n"); - return FALSE; - } - desc_element = xmlnode_get_child(sess, "description"); - xmlns = xmlnode_get_namespace(desc_element); - - if (purple_strequal(xmlns, NS_GOOGLE_SESSION_PHONE)) - session_data->video = FALSE; - else if (purple_strequal(xmlns, NS_GOOGLE_SESSION_VIDEO)) - session_data->video = TRUE; - else { - purple_debug_error("jabber", "Received initiate with " - "invalid namespace %s.\n", xmlns); - return FALSE; - } - - session_data->media = purple_media_manager_create_media( - purple_media_manager_get(), - purple_connection_get_account(js->gc), - "fsrtpconference", session->remote_jid, FALSE); - - purple_media_set_prpl_data(session_data->media, session); - - g_signal_connect_swapped(G_OBJECT(session_data->media), - "candidates-prepared", - G_CALLBACK(google_session_ready), session); - g_signal_connect_swapped(G_OBJECT(session_data->media), "codecs-changed", - G_CALLBACK(google_session_ready), session); - g_signal_connect(G_OBJECT(session_data->media), "state-changed", - G_CALLBACK(google_session_state_changed_cb), session); - g_signal_connect(G_OBJECT(session_data->media), "stream-info", - G_CALLBACK(google_session_stream_info_cb), session); - - params = jabber_google_session_get_params(js, &num_params); + params = + jabber_google_session_get_params(js, relay_ip, relay_udp, relay_tcp, + relay_ssltcp, relay_username, relay_password, &num_params); if (purple_media_add_stream(session_data->media, "google-voice", session->remote_jid, PURPLE_MEDIA_AUDIO, FALSE, @@ -460,10 +476,26 @@ purple_media_error(session_data->media, "Error adding stream."); purple_media_stream_info(session_data->media, PURPLE_MEDIA_INFO_REJECT, NULL, NULL, TRUE); - g_free(params); - return FALSE; + } else { + /* successfully added stream(s) */ + session_data->added_streams = TRUE; + + if (session_data->remote_audio_candidates) { + purple_media_add_remote_candidates(session_data->media, + "google-voice", session->remote_jid, + session_data->remote_audio_candidates); + purple_media_candidate_list_free(session_data->remote_audio_candidates); + session_data->remote_audio_candidates = NULL; + } + if (session_data->remote_video_candidates) { + purple_media_add_remote_candidates(session_data->media, + "google-video", session->remote_jid, + session_data->remote_video_candidates); + purple_media_candidate_list_free(session_data->remote_video_candidates); + session_data->remote_video_candidates = NULL; + } } - + g_free(params); for (codec_element = xmlnode_get_child(desc_element, "payload-type"); @@ -517,13 +549,68 @@ purple_media_codec_list_free(video_codecs); result = jabber_iq_new(js, JABBER_IQ_RESULT); - jabber_iq_set_id(result, iq_id); + jabber_iq_set_id(result, session->iq_id); xmlnode_set_attrib(result->node, "to", session->remote_jid); jabber_iq_send(result); +} + +static gboolean +google_session_handle_initiate(JabberStream *js, GoogleSession *session, xmlnode *sess, const char *iq_id) +{ + xmlnode *desc_element; + const gchar *xmlns; + GoogleAVSessionData *session_data = + (GoogleAVSessionData *) session->session_data; + + if (session->state != UNINIT) { + purple_debug_error("jabber", "Received initiate for active session.\n"); + return FALSE; + } + + desc_element = xmlnode_get_child(sess, "description"); + xmlns = xmlnode_get_namespace(desc_element); + + if (purple_strequal(xmlns, NS_GOOGLE_SESSION_PHONE)) + session_data->video = FALSE; + else if (purple_strequal(xmlns, NS_GOOGLE_SESSION_VIDEO)) + session_data->video = TRUE; + else { + purple_debug_error("jabber", "Received initiate with " + "invalid namespace %s.\n", xmlns); + return FALSE; + } + + session_data->media = purple_media_manager_create_media( + purple_media_manager_get(), + purple_connection_get_account(js->gc), + "fsrtpconference", session->remote_jid, FALSE); + + purple_media_set_prpl_data(session_data->media, session); + + g_signal_connect_swapped(G_OBJECT(session_data->media), + "candidates-prepared", + G_CALLBACK(google_session_ready), session); + g_signal_connect_swapped(G_OBJECT(session_data->media), "codecs-changed", + G_CALLBACK(google_session_ready), session); + g_signal_connect(G_OBJECT(session_data->media), "state-changed", + G_CALLBACK(google_session_state_changed_cb), session); + g_signal_connect(G_OBJECT(session_data->media), "stream-info", + G_CALLBACK(google_session_stream_info_cb), session); + + session->iq_id = g_strdup(iq_id); + + if (js->google_relay_host && js->google_relay_token) { + jabber_google_do_relay_request(js, session, + jabber_google_relay_response_session_handle_initiate_cb); + } else { + jabber_google_relay_response_session_handle_initiate_cb(session, NULL, + 0, 0, 0, NULL, NULL); + } return TRUE; } + static void google_session_handle_candidates(JabberStream *js, GoogleSession *session, xmlnode *sess, const char *iq_id) { @@ -543,13 +630,15 @@ const gchar *protocol = xmlnode_get_attrib(cand, "protocol"); const gchar *address = xmlnode_get_attrib(cand, "address"); const gchar *port = xmlnode_get_attrib(cand, "port"); + const gchar *preference = xmlnode_get_attrib(cand, "preference"); guint component_id; if (cname && type && address && port) { PurpleMediaCandidateType candidate_type; - + guint prio = preference ? atof(preference) * 1000 : 0; + g_snprintf(n, sizeof(n), "S%d", name++); - + if (g_str_equal(type, "local")) candidate_type = PURPLE_MEDIA_CANDIDATE_TYPE_HOST; else if (g_str_equal(type, "stun")) @@ -573,24 +662,38 @@ address, atoi(port)); g_object_set(info, "username", xmlnode_get_attrib(cand, "username"), - "password", xmlnode_get_attrib(cand, "password"), NULL); - if (!strncmp(cname, "video_", 6)) - video_list = g_list_append(video_list, info); - else - list = g_list_append(list, info); + "password", xmlnode_get_attrib(cand, "password"), + "priority", prio, NULL); + if (!strncmp(cname, "video_", 6)) { + if (session_data->added_streams) { + video_list = g_list_append(video_list, info); + } else { + session_data->remote_video_candidates = + g_list_append(session_data->remote_video_candidates, + info); + } + } else { + if (session_data->added_streams) { + list = g_list_append(list, info); + } else { + session_data->remote_audio_candidates = + g_list_append(session_data->remote_audio_candidates, + info); + } + } } } - if (list) - purple_media_add_remote_candidates( - session_data->media, "google-voice", - session->remote_jid, list); - if (video_list) - purple_media_add_remote_candidates( - session_data->media, "google-video", - session->remote_jid, video_list); - purple_media_candidate_list_free(list); - purple_media_candidate_list_free(video_list); + if (list) { + purple_media_add_remote_candidates(session_data->media, "google-voice", + session->remote_jid, list); + purple_media_candidate_list_free(list); + } + if (video_list) { + purple_media_add_remote_candidates(session_data->media, "google-video", + session->remote_jid, video_list); + purple_media_candidate_list_free(video_list); + } result = jabber_iq_new(js, JABBER_IQ_RESULT); jabber_iq_set_id(result, iq_id); @@ -754,6 +857,7 @@ session->state = UNINIT; session->js = js; session->remote_jid = g_strdup(session->id.initiator); + session->session_data = g_new0(GoogleAVSessionData, 1); google_session_handle_initiate(js, session, session_node, iq_id); } diff -r fc961dfd4122 -r 8eb46a838ce0 libpurple/protocols/jabber/google/google_session.h --- a/libpurple/protocols/jabber/google/google_session.h Wed Sep 01 20:29:32 2010 +0000 +++ b/libpurple/protocols/jabber/google/google_session.h Thu Sep 09 18:49:00 2010 +0000 @@ -41,6 +41,7 @@ GoogleSessionState state; JabberStream *js; char *remote_jid; + char *iq_id; gpointer session_data; } GoogleSession; diff -r fc961dfd4122 -r 8eb46a838ce0 libpurple/protocols/jabber/google/jingleinfo.c --- a/libpurple/protocols/jabber/google/jingleinfo.c Wed Sep 01 20:29:32 2010 +0000 +++ b/libpurple/protocols/jabber/google/jingleinfo.c Thu Sep 09 18:49:00 2010 +0000 @@ -76,6 +76,7 @@ JabberIqType type, xmlnode *query) { const xmlnode *stun = xmlnode_get_child(query, "stun"); + const xmlnode *relay = xmlnode_get_child(query, "relay"); gchar *my_bare_jid; /* @@ -119,8 +120,23 @@ } } } - /* should perhaps handle relays later on, or maybe wait until - Google supports a common standard... */ + + if (relay) { + xmlnode *token = xmlnode_get_child(relay, "token"); + xmlnode *server = xmlnode_get_child(relay, "server"); + + if (token) { + gchar *relay_token = xmlnode_get_data(token); + + /* we let js own the string returned from xmlnode_get_data */ + js->google_relay_token = relay_token; + } + + if (server) { + js->google_relay_host = + g_strdup(xmlnode_get_attrib(server, "host")); + } + } } static void diff -r fc961dfd4122 -r 8eb46a838ce0 libpurple/protocols/jabber/google/relay.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/jabber/google/relay.c Thu Sep 09 18:49:00 2010 +0000 @@ -0,0 +1,151 @@ +/** + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "internal.h" +#include "debug.h" + +#include "relay.h" + +typedef struct { + GoogleSession *session; + JabberGoogleRelayCallback *cb; +} JabberGoogleRelayCallbackData; + +static void +jabber_google_relay_parse_response(const gchar *response, gchar **ip, + guint *udp, guint *tcp, guint *ssltcp, gchar **username, gchar **password) +{ + gchar **lines = g_strsplit(response, "\n", -1); + int i = 0; + + for (; lines[i] ; i++) { + gchar *line = lines[i]; + gchar **parts = g_strsplit(line, "=", 2); + + if (parts[0] && parts[1]) { + if (purple_strequal(parts[0], "relay.ip")) { + *ip = g_strdup(parts[1]); + } else if (purple_strequal(parts[0], "relay.udp_port")) { + *udp = atoi(parts[1]); + } else if (purple_strequal(parts[0], "relay.tcp_port")) { + *tcp = atoi(parts[1]); + } else if (purple_strequal(parts[0], "relay.ssltcp_port")) { + *ssltcp = atoi(parts[1]); + } else if (purple_strequal(parts[0], "username")) { + *username = g_strdup(parts[1]); + } else if (purple_strequal(parts[0], "password")) { + *password = g_strdup(parts[1]); + } + } + g_strfreev(parts); + } + + g_strfreev(lines); +} + +static void +jabber_google_relay_remove_url_data(JabberStream *js, + PurpleUtilFetchUrlData *url_data) +{ + GList *iter = js->google_relay_requests; + + while (iter) { + if (iter->data == url_data) { + js->google_relay_requests = + g_list_delete_link(js->google_relay_requests, iter); + break; + } + } +} + +static void +jabber_google_relay_fetch_cb(PurpleUtilFetchUrlData *url_data, + gpointer user_data, const gchar *url_text, gsize len, + const gchar *error_message) +{ + JabberGoogleRelayCallbackData *data = + (JabberGoogleRelayCallbackData *) user_data; + GoogleSession *session = data->session; + JabberStream *js = session->js; + JabberGoogleRelayCallback *cb = data->cb; + gchar *relay_ip = NULL; + guint relay_udp = 0; + guint relay_tcp = 0; + guint relay_ssltcp = 0; + gchar *relay_username = NULL; + gchar *relay_password = NULL; + + g_free(data); + + if (url_data) { + jabber_google_relay_remove_url_data(js, url_data); + } + + purple_debug_info("jabber", "got response on HTTP request to relay server\n"); + + if (url_text && len > 0) { + purple_debug_info("jabber", "got Google relay request response:\n%s\n", + url_text); + jabber_google_relay_parse_response(url_text, &relay_ip, &relay_udp, + &relay_tcp, &relay_ssltcp, &relay_username, &relay_password); + } + + if (cb) + cb(session, relay_ip, relay_udp, relay_tcp, relay_ssltcp, + relay_username, relay_password); + + g_free(relay_ip); + g_free(relay_username); + g_free(relay_password); +} + +void +jabber_google_do_relay_request(JabberStream *js, GoogleSession *session, + JabberGoogleRelayCallback cb) +{ + PurpleUtilFetchUrlData *url_data = NULL; + gchar *url = g_strdup_printf("http://%s", js->google_relay_host); + /* yes, the relay token is included twice as different request headers, + this is apparently needed to make Google's relay servers work... */ + gchar *request = + g_strdup_printf("GET /create_session HTTP/1.0\r\n" + "Host: %s\r\n" + "X-Talk-Google-Relay-Auth: %s\r\n" + "X-Google-Relay-Auth: %s\r\n\r\n", + js->google_relay_host, js->google_relay_token, js->google_relay_token); + JabberGoogleRelayCallbackData *data = g_new0(JabberGoogleRelayCallbackData, 1); + + data->session = session; + data->cb = cb; + purple_debug_info("jabber", + "sending Google relay request %s to %s\n", request, url); + url_data = + purple_util_fetch_url_request(url, FALSE, NULL, FALSE, request, FALSE, + jabber_google_relay_fetch_cb, data); + if (url_data) { + js->google_relay_requests = + g_list_prepend(js->google_relay_requests, url_data); + } else { + purple_debug_error("jabber", "unable to create Google relay request\n"); + jabber_google_relay_fetch_cb(NULL, data, NULL, 0, NULL); + } + g_free(url); + g_free(request); +} \ No newline at end of file diff -r fc961dfd4122 -r 8eb46a838ce0 libpurple/protocols/jabber/google/relay.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/jabber/google/relay.h Thu Sep 09 18:49:00 2010 +0000 @@ -0,0 +1,33 @@ +/** + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef JABBER_GOOGLE_RELAY +#define JABBER_GOOGLE_RELAY + +#include "google_session.h" + +typedef void (JabberGoogleRelayCallback)(GoogleSession *session, const gchar *ip, + guint udp_port, guint tcp_port, guint tls_port, + const gchar *username, const gchar *password); + +void jabber_google_do_relay_request(JabberStream *js, GoogleSession *session, + JabberGoogleRelayCallback cb); + +#endif /* JABBER_GOOGLE_RELAY */ diff -r fc961dfd4122 -r 8eb46a838ce0 libpurple/protocols/jabber/jabber.c --- a/libpurple/protocols/jabber/jabber.c Wed Sep 01 20:29:32 2010 +0000 +++ b/libpurple/protocols/jabber/jabber.c Thu Sep 09 18:49:00 2010 +0000 @@ -978,6 +978,9 @@ js->stun_ip = NULL; js->stun_port = 0; js->stun_query = NULL; + js->google_relay_token = NULL; + js->google_relay_host = NULL; + js->google_relay_requests = NULL; /* if we are idle, set idle-ness on the stream (this could happen if we get disconnected and the reconnects while being idle. I don't think it makes @@ -1681,6 +1684,21 @@ js->stun_query = NULL; } + /* remove Google relay-related stuff */ + g_free(js->google_relay_token); + g_free(js->google_relay_host); + if (js->google_relay_requests) { + while (js->google_relay_requests) { + PurpleUtilFetchUrlData *url_data = + (PurpleUtilFetchUrlData *) js->google_relay_requests->data; + purple_util_fetch_url_cancel(url_data); + g_free(url_data); + js->google_relay_requests = + g_list_delete_link(js->google_relay_requests, + js->google_relay_requests); + } + } + g_free(js); gc->proto_data = NULL; @@ -3237,9 +3255,9 @@ g_free(resource); if (type & PURPLE_MEDIA_AUDIO && - !jabber_resource_has_capability(jbr, - JINGLE_APP_RTP_SUPPORT_AUDIO) && - jabber_resource_has_capability(jbr, NS_GOOGLE_VOICE)) + !jabber_resource_has_capability(jbr, + JINGLE_APP_RTP_SUPPORT_AUDIO) && + jabber_resource_has_capability(jbr, NS_GOOGLE_VOICE)) return jabber_google_session_initiate(js, who, type); else return jingle_rtp_initiate_media(js, who, type); diff -r fc961dfd4122 -r 8eb46a838ce0 libpurple/protocols/jabber/jabber.h --- a/libpurple/protocols/jabber/jabber.h Wed Sep 01 20:29:32 2010 +0000 +++ b/libpurple/protocols/jabber/jabber.h Thu Sep 09 18:49:00 2010 +0000 @@ -276,7 +276,12 @@ gchar *stun_ip; int stun_port; PurpleDnsQueryData *stun_query; - /* later add stuff to handle TURN relays... */ + + /* stuff for Google's relay handling */ + gchar *google_relay_token; + gchar *google_relay_host; + GList *google_relay_requests; /* the HTTP requests to get */ + /* relay info */ }; typedef gboolean (JabberFeatureEnabled)(JabberStream *js, const gchar *namespace); diff -r fc961dfd4122 -r 8eb46a838ce0 libpurple/protocols/jabber/jingle/jingle.c --- a/libpurple/protocols/jabber/jingle/jingle.c Wed Sep 01 20:29:32 2010 +0000 +++ b/libpurple/protocols/jabber/jingle/jingle.c Thu Sep 09 18:49:00 2010 +0000 @@ -35,6 +35,9 @@ #include "rtp.h" #include +#ifdef USE_VV +#include +#endif GType jingle_get_type(const gchar *type) @@ -431,32 +434,91 @@ jingle_terminate_sessions_gh, NULL); } +#ifdef USE_VV +static GValueArray * +jingle_create_relay_info(const gchar *ip, guint port, const gchar *username, + const gchar *password, const gchar *relay_type, GValueArray *relay_info) +{ + GValue value; + GstStructure *turn_setup = gst_structure_new("relay-info", + "ip", G_TYPE_STRING, ip, + "port", G_TYPE_UINT, port, + "username", G_TYPE_STRING, username, + "password", G_TYPE_STRING, password, + "relay-type", G_TYPE_STRING, relay_type, + NULL); + purple_debug_info("jabber", "created gst_structure %" GST_PTR_FORMAT "\n", + turn_setup); + if (turn_setup) { + memset(&value, 0, sizeof(GValue)); + g_value_init(&value, GST_TYPE_STRUCTURE); + gst_value_set_structure(&value, turn_setup); + relay_info = g_value_array_append(relay_info, &value); + gst_structure_free(turn_setup); + } + return relay_info; +} + GParameter * -jingle_get_params(JabberStream *js, guint *num) +jingle_get_params(JabberStream *js, const gchar *relay_ip, guint relay_udp, + guint relay_tcp, guint relay_ssltcp, const gchar *relay_username, + const gchar *relay_password, 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; + guint num_params = has_account_stun ? + (relay_ip ? 3 : 2) : (relay_ip ? 1 : 0); GParameter *params = NULL; - + int next_index = 0; + if (num_params > 0) { params = g_new0(GParameter, num_params); - purple_debug_info("jabber", - "setting param stun-ip for stream using auto-discovered IP: %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 auto-discovered port: %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); + if (has_account_stun) { + purple_debug_info("jabber", + "setting param stun-ip for stream using Google auto-config: %s\n", + js->stun_ip); + params[next_index].name = "stun-ip"; + g_value_init(¶ms[next_index].value, G_TYPE_STRING); + g_value_set_string(¶ms[next_index].value, js->stun_ip); + purple_debug_info("jabber", + "setting param stun-port for stream using Google auto-config: %d\n", + js->stun_port); + next_index++; + params[next_index].name = "stun-port"; + g_value_init(¶ms[next_index].value, G_TYPE_UINT); + g_value_set_uint(¶ms[next_index].value, js->stun_port); + next_index++; + } + + if (relay_ip) { + GValueArray *relay_info = g_value_array_new(0); + + if (relay_udp) { + relay_info = + jingle_create_relay_info(relay_ip, relay_udp, relay_username, + relay_password, "udp", relay_info); + } + if (relay_tcp) { + relay_info = + jingle_create_relay_info(relay_ip, relay_tcp, relay_username, + relay_password, "tcp", relay_info); + } + if (relay_ssltcp) { + relay_info = + jingle_create_relay_info(relay_ip, relay_ssltcp, relay_username, + relay_password, "tls", relay_info); + } + params[next_index].name = "relay-info"; + g_value_init(¶ms[next_index].value, G_TYPE_VALUE_ARRAY); + g_value_set_boxed(¶ms[next_index].value, relay_info); + g_value_array_free(relay_info); + } } *num = num_params; return params; } +#endif + diff -r fc961dfd4122 -r 8eb46a838ce0 libpurple/protocols/jabber/jingle/jingle.h --- a/libpurple/protocols/jabber/jingle/jingle.h Wed Sep 01 20:29:32 2010 +0000 +++ b/libpurple/protocols/jabber/jingle/jingle.h Thu Sep 09 18:49:00 2010 +0000 @@ -78,9 +78,13 @@ void jingle_terminate_sessions(JabberStream *js); +#ifdef USE_VV /* 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); +GParameter *jingle_get_params(JabberStream *js, const gchar *relay_ip, + guint relay_udp, guint relay_tcp, guint relay_ssltcp, + const gchar *relay_username, const gchar *relay_password, guint *num_params); +#endif #ifdef __cplusplus } diff -r fc961dfd4122 -r 8eb46a838ce0 libpurple/protocols/jabber/jingle/rtp.c --- a/libpurple/protocols/jabber/jingle/rtp.c Wed Sep 01 20:29:32 2010 +0000 +++ b/libpurple/protocols/jabber/jingle/rtp.c Thu Sep 09 18:49:00 2010 +0000 @@ -611,7 +611,8 @@ : PURPLE_MEDIA_RECV_VIDEO; params = - jingle_get_params(jingle_session_get_js(session), &num_params); + jingle_get_params(jingle_session_get_js(session), NULL, 0, 0, 0, + NULL, NULL, &num_params); creator = jingle_content_get_creator(content); if (!strcmp(creator, "initiator")) diff -r fc961dfd4122 -r 8eb46a838ce0 libpurple/protocols/yahoo/util.c --- a/libpurple/protocols/yahoo/util.c Wed Sep 01 20:29:32 2010 +0000 +++ b/libpurple/protocols/yahoo/util.c Thu Sep 09 18:49:00 2010 +0000 @@ -43,7 +43,7 @@ if(proxy_ssl) ppi = purple_proxy_get_setup(account); else - ppi = purple_global_proxy_get_info(); + ppi = purple_proxy_get_setup(NULL); type = purple_proxy_info_get_type(ppi); diff -r fc961dfd4122 -r 8eb46a838ce0 pidgin/gtkplugin.c --- a/pidgin/gtkplugin.c Wed Sep 01 20:29:32 2010 +0000 +++ b/pidgin/gtkplugin.c Thu Sep 09 18:49:00 2010 +0000 @@ -800,5 +800,8 @@ g_signal_connect (G_OBJECT (sel), "changed", G_CALLBACK (prefs_plugin_sel), NULL); g_signal_connect(G_OBJECT(plugin_dialog), "response", G_CALLBACK(plugin_dialog_response_cb), sel); gtk_window_set_default_size(GTK_WINDOW(plugin_dialog), 430, 530); + + pidgin_auto_parent_window(GTK_WINDOW(plugin_dialog)); + gtk_widget_show_all(plugin_dialog); } diff -r fc961dfd4122 -r 8eb46a838ce0 pidgin/plugins/vvconfig.c --- a/pidgin/plugins/vvconfig.c Wed Sep 01 20:29:32 2010 +0000 +++ b/pidgin/plugins/vvconfig.c Thu Sep 09 18:49:00 2010 +0000 @@ -543,8 +543,8 @@ } gtk_window_present(GTK_WINDOW(window)); } - - + + static GList * actions(PurplePlugin *plugin, gpointer context) { @@ -581,32 +581,32 @@ static PurplePluginInfo info = { - PURPLE_PLUGIN_MAGIC, /**< magic */ - PURPLE_MAJOR_VERSION, /**< major version */ - PURPLE_MINOR_VERSION, /**< minor version */ - PURPLE_PLUGIN_STANDARD, /**< type */ - PIDGIN_PLUGIN_TYPE, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ + PURPLE_PLUGIN_MAGIC, /**< magic */ + PURPLE_MAJOR_VERSION, /**< major version */ + PURPLE_MINOR_VERSION, /**< minor version */ + PURPLE_PLUGIN_STANDARD, /**< type */ + PIDGIN_PLUGIN_TYPE, /**< ui_requirement */ + 0, /**< flags */ + NULL, /**< dependencies */ + PURPLE_PRIORITY_DEFAULT, /**< priority */ - "gtk-maiku-vvconfig", /**< id */ - N_("Voice/Video Settings"), /**< name */ - DISPLAY_VERSION, /**< version */ - N_("Configure your microphone and webcam."), /**< summary */ + "gtk-maiku-vvconfig", /**< id */ + N_("Voice/Video Settings"), /**< name */ + DISPLAY_VERSION, /**< version */ + N_("Configure your microphone and webcam."), /**< summary */ N_("Configure microphone and webcam " - "settings for voice/video calls."), /**< description */ - "Mike Ruprecht ", /**< author */ - PURPLE_WEBSITE, /**< homepage */ + "settings for voice/video calls."), /**< description */ + "Mike Ruprecht ", /**< author */ + PURPLE_WEBSITE, /**< homepage */ - plugin_load, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ + plugin_load, /**< load */ + plugin_unload, /**< unload */ + NULL, /**< destroy */ - &ui_info, /**< ui_info */ - NULL, /**< extra_info */ - NULL, /**< prefs_info */ - actions, /**< actions */ + &ui_info, /**< ui_info */ + NULL, /**< extra_info */ + NULL, /**< prefs_info */ + actions, /**< actions */ /* padding */ NULL, diff -r fc961dfd4122 -r 8eb46a838ce0 po/de.po --- a/po/de.po Wed Sep 01 20:29:32 2010 +0000 +++ b/po/de.po Thu Sep 09 18:49:00 2010 +0000 @@ -11,9 +11,9 @@ msgstr "" "Project-Id-Version: de\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-08-01 00:18+0200\n" -"PO-Revision-Date: 2010-08-01 00:16+0200\n" -"Last-Translator: Jochen Kemnade \n" +"POT-Creation-Date: 2010-09-02 19:45+0200\n" +"PO-Revision-Date: 2010-09-06 10:01+0200\n" +"Last-Translator: Björn Voigt \n" "Language-Team: Deutsch \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -4772,11 +4772,17 @@ msgid "Domain" msgstr "Domain" -msgid "Require SSL/TLS" -msgstr "SSL/TLS voraussetzen" - -msgid "Force old (port 5223) SSL" -msgstr "Erzwinge altes SSL (Port 5223)" +msgid "Require encryption" +msgstr "Verschlüsselung fordern" + +msgid "Use encryption if available" +msgstr "Verschlüsselung benutzen, wenn verfügbar" + +msgid "Use old-style SSL" +msgstr "Alte SSL-Methode verwenden" + +msgid "Connection security" +msgstr "Verbindungssicherheit" msgid "Allow plaintext auth over unencrypted streams" msgstr "Erlaube Klartext-Authentifikation über einen unverschlüsselten Kanal" @@ -7218,96 +7224,6 @@ msgid "File %s is %s, which is larger than the maximum size of %s." msgstr "Datei %s (%s) ist größer als die maximale Größe von %s." -msgid "" -"(There was an error receiving this message. The buddy you are speaking with " -"is probably using a different encoding than expected. If you know what " -"encoding he is using, you can specify it in the advanced account options for " -"your AIM/ICQ account.)" -msgstr "" -"(Es gab einen Fehler beim Empfangen dieser Nachricht. Der Buddy, mit dem " -"Sie sich unterhalten benutzt wahrscheinlich eine andere Kodierung als " -"erwartet. Wenn Sie wissen, welche Kodierung er benutzt, können Sie diese in " -"den erweiterten Konto-Optionen Ihres AIM/ICQ-Kontos angeben.)" - -#, c-format -msgid "" -"(There was an error receiving this message. Either you and %s have " -"different encodings selected, or %s has a buggy client.)" -msgstr "" -"(Es gab einen Fehler beim Empfang dieser Nachricht. Entweder haben Sie und %" -"s unterschiedliche Kodierungen gesetzt oder %s hat einen fehlerhaften " -"Client.)" - -#. Label -msgid "Buddy Icon" -msgstr "Buddy-Icon" - -msgid "Voice" -msgstr "Stimme" - -msgid "AIM Direct IM" -msgstr "AIM direkte Nachricht" - -msgid "Get File" -msgstr "Datei abrufen" - -msgid "Games" -msgstr "Spiele" - -msgid "ICQ Xtraz" -msgstr "ICQ Xtraz" - -msgid "Add-Ins" -msgstr "Zusätze" - -msgid "Send Buddy List" -msgstr "Buddy-Liste senden" - -msgid "ICQ Direct Connect" -msgstr "ICQ direkte Verbindung" - -msgid "AP User" -msgstr "AP Benutzer" - -msgid "ICQ RTF" -msgstr "ICQ RTF" - -msgid "Nihilist" -msgstr "Nihilist" - -msgid "ICQ Server Relay" -msgstr "ICQ Server Relay" - -msgid "Old ICQ UTF8" -msgstr "Altes ICQ UTF-8" - -msgid "Trillian Encryption" -msgstr "Trillian-Verschlüsselung" - -msgid "ICQ UTF8" -msgstr "ICQ UTF-8" - -msgid "Hiptop" -msgstr "Hiptop" - -msgid "Security Enabled" -msgstr "Sicherheit aktiviert" - -msgid "Video Chat" -msgstr "Video-Chat" - -msgid "iChat AV" -msgstr "iChat AV" - -msgid "Live Video" -msgstr "Live-Video" - -msgid "Camera" -msgstr "Kamera" - -msgid "Screen Sharing" -msgstr "Gemeinsamer Bildschirm" - msgid "Free For Chat" msgstr "Bereit zum Chatten" @@ -7338,15 +7254,6 @@ msgid "At lunch" msgstr "Zur Mittagspause" -msgid "IP Address" -msgstr "IP-Adresse" - -msgid "Warning Level" -msgstr "Warnstufe" - -msgid "Buddy Comment" -msgstr "Buddy-Kommentar" - #, c-format msgid "Unable to connect to authentication server: %s" msgstr "Verbindung zum Authentifizierungsserver nicht möglich: %s" @@ -7446,17 +7353,6 @@ msgid "Unable to initialize connection" msgstr "Kann Verbindung nicht erstellen" -msgid "Please authorize me so I can add you to my buddy list." -msgstr "" -"Bitte autorisieren Sie mich, sodass ich Sie in meine Buddy-Liste aufnehmen " -"kann." - -msgid "No reason given." -msgstr "Kein Grund angegeben." - -msgid "Authorization Denied Message:" -msgstr "Nachricht für die Ablehnung der Autorisierung:" - #, c-format msgid "" "The user %u has denied your request to add them to your buddy list for the " @@ -7467,6 +7363,9 @@ "Liste hinzufügen zu dürfen, und zwar aus folgendem Grund:\n" "%s" +msgid "No reason given." +msgstr "Kein Grund angegeben." + msgid "ICQ authorization denied." msgstr "ICQ-Autorisierung verweigert." @@ -7582,60 +7481,13 @@ msgstr[1] "" "Sie haben %hu Nachrichten von %s aus unbekannten Gründen nicht erhalten." -#, c-format -msgid "User information not available: %s" -msgstr "Benutzerinformation nicht verfügbar: %s" - -msgid "Online Since" -msgstr "Online seit" - -msgid "Member Since" -msgstr "Mitglied seit" - -msgid "Capabilities" -msgstr "Fähigkeiten" - msgid "Your AIM connection may be lost." msgstr "Ihre AIM-Verbindung könnte unterbrochen sein." -#. The conversion failed! -msgid "" -"[Unable to display a message from this user because it contained invalid " -"characters.]" -msgstr "" -"[Kann die Nachricht von diesem Benutzer nicht anzeigen, da sie ungültige " -"Zeichen enthält.]" - #, c-format msgid "You have been disconnected from chat room %s." msgstr "Die Verbindung zum Raum %s wurde unterbrochen." -msgid "Mobile Phone" -msgstr "Handynummer" - -msgid "Personal Web Page" -msgstr "Persönliche Webseite" - -#. aim_userinfo_t -#. strip_html_tags -msgid "Additional Information" -msgstr "Zusätzliche Informationen" - -msgid "Zip Code" -msgstr "PLZ" - -msgid "Work Information" -msgstr "Information (Arbeit)" - -msgid "Division" -msgstr "Abteilung" - -msgid "Position" -msgstr "Position" - -msgid "Web Page" -msgstr "Webseite" - msgid "Pop-Up Message" msgstr "Pop-Up Nachricht" @@ -7922,8 +7774,8 @@ msgid "Change Address To:" msgstr "Ändere die Adresse zu:" -msgid "you are not waiting for authorization" -msgstr "Sie warten derzeit auf keine Autorisierungen" +msgid "you are not waiting for authorization" +msgstr "Sie warten derzeit auf keine Autorisierungen" msgid "You are awaiting authorization from the following buddies" msgstr "Sie warten auf Autorisierung von den folgenden Buddys" @@ -7978,9 +7830,6 @@ msgid "Search for Buddy by Email Address..." msgstr "Suche Buddys nach E-Mail-Adresse..." -msgid "Search for Buddy by Information" -msgstr "Suche Buddy nach Information" - msgid "Use clientLogin" msgstr "clientLogin benutzen" @@ -8268,8 +8117,8 @@ msgstr "Ihre Anfrage wurde abgelehnt." #, c-format -msgid "%u requires verification" -msgstr "%u erfordert Autorisierung" +msgid "%u requires verification: %s" +msgstr "%u erfordert Überprüfung: %s" msgid "Add buddy question" msgstr "Buddy-Frage hinzufügen" @@ -10146,6 +9995,9 @@ msgid "Computer" msgstr "Computer" +msgid "Mobile Phone" +msgstr "Handynummer" + msgid "PDA" msgstr "PDA" @@ -10580,6 +10432,9 @@ msgid "Write Error" msgstr "Schreibfehler" +msgid "IP Address" +msgstr "IP-Adresse" + msgid "Yahoo! Japan Profile" msgstr "Yahoo!-Japan-Profil" @@ -10621,6 +10476,9 @@ msgid "Cool Link 3" msgstr "Cooler Link 3" +msgid "Member Since" +msgstr "Mitglied seit" + msgid "Last Update" msgstr "Letzte Aktualisierung" @@ -11247,6 +11105,11 @@ "Listenfenster zu diesem Dialog zurückkehren und Konten hinzufügen, " "bearbeiten oder löschen" +#, c-format +msgid "%s%s%s%s wants to add you (%s) to his or her buddy list%s%s" +msgstr "" +"%s%s%s%s möchte Sie (%s) zu seiner oder ihrer Buddy-Liste hinzufügen%s%s" + #. Buddy List msgid "Background Color" msgstr "Hintergrundfarbe" @@ -11506,6 +11369,8 @@ msgid "Edit User Mood" msgstr "Benutzerstimmung ändern" +#. NOTE: Do not set any accelerator to Control+O. It is mapped by +#. gtk_blist_key_press_cb to "Get User Info" on the selected buddy. #. Buddies menu msgid "/_Buddies" msgstr "/_Buddys" @@ -13648,6 +13513,9 @@ msgid "_TURN server:" msgstr "_TURN-Server:" +msgid "_UDP Port:" +msgstr "_UDP-Port:" + msgid "Use_rname:" msgstr "_Benutzername:" @@ -14143,6 +14011,10 @@ "Dateigröße: %s\n" "Bildgröße: %dx%d" +#. Label +msgid "Buddy Icon" +msgstr "Buddy-Icon" + #, c-format msgid "The file '%s' is too large for %s. Please try a smaller image.\n" msgstr "" @@ -14228,7 +14100,7 @@ msgid "Small" msgstr "Klein" -msgid "Smaller versions of the default smilies" +msgid "Smaller versions of the default smileys" msgstr "Kleinere Versionen der Default-Smileys" msgid "Response Probability:" @@ -15079,6 +14951,9 @@ msgid "Half Operator" msgstr "Half-Operator" +msgid "Voice" +msgstr "Stimme" + msgid "Authorization dialog" msgstr "Autorisierungsdialog" @@ -15268,6 +15143,9 @@ msgid "Voice/Video Settings" msgstr "Sprach-/Video-Einstellungen" +msgid "Voice and Video Settings" +msgstr "Sprach- und Video-Einstellungen" + #. *< name #. *< version msgid "Configure your microphone and webcam." @@ -15397,7 +15275,7 @@ msgstr "" "Dieses Plugin ist nützlich zur Fehlersuche in XMPP-Servern oder -Clients." -#. $(^Name) is the current Version name (e.g. Pidgin 2.7.0). $_CLICK will become a translated version of "Click Next to continue." +#. $(^Name) is the current Version name (e.g. Pidgin 2.7.0). $_CLICK will become a translated version of "Click Next to continue." DO NOT translate the CLICK in $_CLICK. It will break the installer. msgid "" "$(^Name) is released under the GNU General Public License (GPL). The license " "is provided here for information purposes only. $_CLICK"