comparison libpurple/protocols/jabber/jabber.c @ 26567:242a8c97270b

propagate from branch 'im.pidgin.pidgin' (head f144c6bda9daf701aa891c875fce7a4dedd611ae) to branch 'im.pidgin.cpw.darkrain42.xmpp.avatars' (head 94d20f2f1d6e10ad1543c226dc01fb5c518bcea0)
author Paul Aurich <paul@darkrain42.org>
date Sun, 05 Apr 2009 21:49:01 +0000
parents 7027cc42e56c 2b8c161c9d7b
children d6a863df7884
comparison
equal deleted inserted replaced
26360:14e1f9af369f 26567:242a8c97270b
58 #include "si.h" 58 #include "si.h"
59 #include "xdata.h" 59 #include "xdata.h"
60 #include "pep.h" 60 #include "pep.h"
61 #include "adhoccommands.h" 61 #include "adhoccommands.h"
62 62
63 #include "jingle/jingle.h"
64 #include "jingle/rtp.h"
63 65
64 #define JABBER_CONNECT_STEPS (js->gsc ? 9 : 5) 66 #define JABBER_CONNECT_STEPS (js->gsc ? 9 : 5)
65 67
66 static PurplePlugin *my_protocol = NULL; 68 static PurplePlugin *my_protocol = NULL;
67 GList *jabber_features = NULL; 69 GList *jabber_features = NULL;
84 js->reinit = FALSE; 86 js->reinit = FALSE;
85 g_free(open_stream); 87 g_free(open_stream);
86 } 88 }
87 89
88 static void 90 static void
89 jabber_session_initialized_cb(JabberStream *js, xmlnode *packet, gpointer data) 91 jabber_session_initialized_cb(JabberStream *js, const char *from,
90 { 92 JabberIqType type, const char *id,
91 const char *type = xmlnode_get_attrib(packet, "type"); 93 xmlnode *packet, gpointer data)
92 if(type && !strcmp(type, "result")) { 94 {
95 if (type == JABBER_IQ_RESULT) {
93 jabber_stream_set_state(js, JABBER_STREAM_CONNECTED); 96 jabber_stream_set_state(js, JABBER_STREAM_CONNECTED);
94 if(js->unregistration) 97 if(js->unregistration)
95 jabber_unregister_account_cb(js); 98 jabber_unregister_account_cb(js);
96 } else { 99 } else {
97 purple_connection_error_reason (js->gc, 100 purple_connection_error_reason (js->gc,
111 xmlnode_set_namespace(session, "urn:ietf:params:xml:ns:xmpp-session"); 114 xmlnode_set_namespace(session, "urn:ietf:params:xml:ns:xmpp-session");
112 115
113 jabber_iq_send(iq); 116 jabber_iq_send(iq);
114 } 117 }
115 118
116 static void jabber_bind_result_cb(JabberStream *js, xmlnode *packet, 119 static void jabber_bind_result_cb(JabberStream *js, const char *from,
117 gpointer data) 120 JabberIqType type, const char *id,
118 { 121 xmlnode *packet, gpointer data)
119 const char *type = xmlnode_get_attrib(packet, "type"); 122 {
120 xmlnode *bind; 123 xmlnode *bind;
121 124
122 if(type && !strcmp(type, "result") && 125 if (type == JABBER_IQ_RESULT &&
123 (bind = xmlnode_get_child_with_namespace(packet, "bind", "urn:ietf:params:xml:ns:xmpp-bind"))) { 126 (bind = xmlnode_get_child_with_namespace(packet, "bind", "urn:ietf:params:xml:ns:xmpp-bind"))) {
124 xmlnode *jid; 127 xmlnode *jid;
125 char *full_jid; 128 char *full_jid;
126 if((jid = xmlnode_get_child(bind, "jid")) && (full_jid = xmlnode_get_data(jid))) { 129 if((jid = xmlnode_get_child(bind, "jid")) && (full_jid = xmlnode_get_data(jid))) {
127 JabberBuddy *my_jb = NULL; 130 JabberBuddy *my_jb = NULL;
445 txt = xmlnode_to_str(packet, &len); 448 txt = xmlnode_to_str(packet, &len);
446 jabber_send_raw(js, txt, len); 449 jabber_send_raw(js, txt, len);
447 g_free(txt); 450 g_free(txt);
448 } 451 }
449 452
450 static void jabber_pong_cb(JabberStream *js, xmlnode *packet, gpointer unused) 453 static gboolean jabber_keepalive_timeout(PurpleConnection *gc)
451 {
452 purple_timeout_remove(js->keepalive_timeout);
453 js->keepalive_timeout = -1;
454 }
455
456 static gboolean jabber_pong_timeout(PurpleConnection *gc)
457 { 454 {
458 JabberStream *js = gc->proto_data; 455 JabberStream *js = gc->proto_data;
459 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, 456 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
460 _("Ping timeout")); 457 _("Ping timeout"));
461 js->keepalive_timeout = -1; 458 js->keepalive_timeout = -1;
465 void jabber_keepalive(PurpleConnection *gc) 462 void jabber_keepalive(PurpleConnection *gc)
466 { 463 {
467 JabberStream *js = gc->proto_data; 464 JabberStream *js = gc->proto_data;
468 465
469 if (js->keepalive_timeout == -1) { 466 if (js->keepalive_timeout == -1) {
470 JabberIq *iq = jabber_iq_new(js, JABBER_IQ_GET); 467 jabber_ping_jid(js, NULL);
471 468 js->keepalive_timeout = purple_timeout_add_seconds(120,
472 xmlnode *ping = xmlnode_new_child(iq->node, "ping"); 469 (GSourceFunc)(jabber_keepalive_timeout), gc);
473 xmlnode_set_namespace(ping, "urn:xmpp:ping");
474
475 js->keepalive_timeout = purple_timeout_add_seconds(120, (GSourceFunc)(jabber_pong_timeout), gc);
476 jabber_iq_set_callback(iq, jabber_pong_cb, NULL);
477 jabber_iq_send(iq);
478 } 470 }
479 } 471 }
480 472
481 static void 473 static void
482 jabber_recv_cb_ssl(gpointer data, PurpleSslConnection *gsc, 474 jabber_recv_cb_ssl(gpointer data, PurpleSslConnection *gsc,
729 js->next_id = g_random_int(); 721 js->next_id = g_random_int();
730 js->write_buffer = purple_circ_buffer_new(512); 722 js->write_buffer = purple_circ_buffer_new(512);
731 js->old_length = 0; 723 js->old_length = 0;
732 js->keepalive_timeout = -1; 724 js->keepalive_timeout = -1;
733 js->certificate_CN = g_strdup(connect_server[0] ? connect_server : js->user ? js->user->domain : NULL); 725 js->certificate_CN = g_strdup(connect_server[0] ? connect_server : js->user ? js->user->domain : NULL);
726 js->sessions = NULL;
727 js->stun_ip = NULL;
728 js->stun_port = 0;
729 js->stun_query = NULL;
734 730
735 if(!js->user) { 731 if(!js->user) {
736 purple_connection_error_reason (gc, 732 purple_connection_error_reason (gc,
737 PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, 733 PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
738 _("Invalid XMPP ID")); 734 _("Invalid XMPP ID"));
807 { 803 {
808 purple_timeout_add(0, conn_close_cb, js); 804 purple_timeout_add(0, conn_close_cb, js);
809 } 805 }
810 806
811 static void 807 static void
812 jabber_registration_result_cb(JabberStream *js, xmlnode *packet, gpointer data) 808 jabber_registration_result_cb(JabberStream *js, const char *from,
809 JabberIqType type, const char *id,
810 xmlnode *packet, gpointer data)
813 { 811 {
814 PurpleAccount *account = purple_connection_get_account(js->gc); 812 PurpleAccount *account = purple_connection_get_account(js->gc);
815 const char *type = xmlnode_get_attrib(packet, "type");
816 char *buf; 813 char *buf;
817 char *to = data; 814 char *to = data;
818 815
819 if(!strcmp(type, "result")) { 816 if (type == JABBER_IQ_RESULT) {
820 if(js->registration) { 817 if(js->registration) {
821 buf = g_strdup_printf(_("Registration of %s@%s successful"), 818 buf = g_strdup_printf(_("Registration of %s@%s successful"),
822 js->user->node, js->user->domain); 819 js->user->node, js->user->domain);
823 if(account->registration_cb) 820 if(account->registration_cb)
824 (account->registration_cb)(account, TRUE, account->registration_cb_user_data); 821 (account->registration_cb)(account, TRUE, account->registration_cb_user_data);
842 if(account->registration_cb) 839 if(account->registration_cb)
843 (account->registration_cb)(account, FALSE, account->registration_cb_user_data); 840 (account->registration_cb)(account, FALSE, account->registration_cb_user_data);
844 } 841 }
845 g_free(to); 842 g_free(to);
846 if(js->registration) 843 if(js->registration)
847 jabber_connection_schedule_close(js); 844 jabber_connection_schedule_close(js);
848 } 845 }
849 846
850 static void 847 static void
851 jabber_unregistration_result_cb(JabberStream *js, xmlnode *packet, gpointer data) 848 jabber_unregistration_result_cb(JabberStream *js, const char *from,
852 { 849 JabberIqType type, const char *id,
853 const char *type = xmlnode_get_attrib(packet, "type"); 850 xmlnode *packet, gpointer data)
851 {
854 char *buf; 852 char *buf;
855 char *to = data; 853 char *to = data;
856 854
857 /* This function is never called for unregistering our XMPP account from 855 /* This function is never called for unregistering our XMPP account from
858 * the server, so there should always be a 'to' address. */ 856 * the server, so there should always be a 'to' address. */
859 g_return_if_fail(to != NULL); 857 g_return_if_fail(to != NULL);
860 858
861 if(!strcmp(type, "result")) { 859 if (type == JABBER_IQ_RESULT) {
862 buf = g_strdup_printf(_("Registration from %s successfully removed"), 860 buf = g_strdup_printf(_("Registration from %s successfully removed"),
863 to); 861 to);
864 purple_notify_info(NULL, _("Unregistration Successful"), 862 purple_notify_info(NULL, _("Unregistration Successful"),
865 _("Unregistration Successful"), buf); 863 _("Unregistration Successful"), buf);
866 g_free(buf); 864 g_free(buf);
1005 1003
1006 jabber_iq_set_callback(iq, jabber_registration_result_cb, to); 1004 jabber_iq_set_callback(iq, jabber_registration_result_cb, to);
1007 jabber_iq_send(iq); 1005 jabber_iq_send(iq);
1008 } 1006 }
1009 1007
1010 void jabber_register_parse(JabberStream *js, xmlnode *packet) 1008 void jabber_register_parse(JabberStream *js, const char *from, JabberIqType type,
1009 const char *id, xmlnode *query)
1011 { 1010 {
1012 PurpleAccount *account = purple_connection_get_account(js->gc); 1011 PurpleAccount *account = purple_connection_get_account(js->gc);
1013 const char *type;
1014 const char *from;
1015 PurpleRequestFields *fields; 1012 PurpleRequestFields *fields;
1016 PurpleRequestFieldGroup *group; 1013 PurpleRequestFieldGroup *group;
1017 PurpleRequestField *field; 1014 PurpleRequestField *field;
1018 xmlnode *query, *x, *y; 1015 xmlnode *x, *y;
1019 char *instructions; 1016 char *instructions;
1020 JabberRegisterCBData *cbdata; 1017 JabberRegisterCBData *cbdata;
1021 gboolean registered = FALSE; 1018 gboolean registered = FALSE;
1022 1019
1023 if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "result")) 1020 if (type != JABBER_IQ_RESULT)
1024 return; 1021 return;
1025 1022
1026 from = xmlnode_get_attrib(packet, "from"); 1023 if (!from)
1024 from = js->serverFQDN;
1025 g_return_if_fail(from != NULL);
1027 1026
1028 if(js->registration) { 1027 if(js->registration) {
1029 /* get rid of the login thingy */ 1028 /* get rid of the login thingy */
1030 purple_connection_set_state(js->gc, PURPLE_CONNECTED); 1029 purple_connection_set_state(js->gc, PURPLE_CONNECTED);
1031 } 1030 }
1032
1033 query = xmlnode_get_child(packet, "query");
1034 1031
1035 if(xmlnode_get_child(query, "registered")) { 1032 if(xmlnode_get_child(query, "registered")) {
1036 registered = TRUE; 1033 registered = TRUE;
1037 1034
1038 if(js->registration) { 1035 if(js->registration) {
1234 my_jb->subscription |= JABBER_SUB_BOTH; 1231 my_jb->subscription |= JABBER_SUB_BOTH;
1235 1232
1236 server = connect_server[0] ? connect_server : js->user->domain; 1233 server = connect_server[0] ? connect_server : js->user->domain;
1237 js->certificate_CN = g_strdup(server); 1234 js->certificate_CN = g_strdup(server);
1238 1235
1236 js->stun_ip = NULL;
1237 js->stun_port = 0;
1238 js->stun_query = NULL;
1239
1239 jabber_stream_set_state(js, JABBER_STREAM_CONNECTING); 1240 jabber_stream_set_state(js, JABBER_STREAM_CONNECTING);
1240 1241
1241 if(purple_account_get_bool(account, "old_ssl", FALSE)) { 1242 if(purple_account_get_bool(account, "old_ssl", FALSE)) {
1242 if(purple_ssl_is_supported()) { 1243 if(purple_ssl_is_supported()) {
1243 js->gsc = purple_ssl_connect(account, server, 1244 js->gsc = purple_ssl_connect(account, server,
1263 js); 1264 js);
1264 } 1265 }
1265 } 1266 }
1266 } 1267 }
1267 1268
1268 static void jabber_unregister_account_iq_cb(JabberStream *js, xmlnode *packet, gpointer data) { 1269 static void
1270 jabber_unregister_account_iq_cb(JabberStream *js, const char *from,
1271 JabberIqType type, const char *id,
1272 xmlnode *packet, gpointer data)
1273 {
1269 PurpleAccount *account = purple_connection_get_account(js->gc); 1274 PurpleAccount *account = purple_connection_get_account(js->gc);
1270 const char *type = xmlnode_get_attrib(packet,"type"); 1275
1271 if(!strcmp(type,"error")) { 1276 if (type == JABBER_IQ_ERROR) {
1272 char *msg = jabber_parse_error(js, packet, NULL); 1277 char *msg = jabber_parse_error(js, packet, NULL);
1273 1278
1274 purple_notify_error(js->gc, _("Error unregistering account"), 1279 purple_notify_error(js->gc, _("Error unregistering account"),
1275 _("Error unregistering account"), msg); 1280 _("Error unregistering account"), msg);
1276 g_free(msg); 1281 g_free(msg);
1277 if(js->unregistration_cb) 1282 if(js->unregistration_cb)
1278 js->unregistration_cb(account, FALSE, js->unregistration_user_data); 1283 js->unregistration_cb(account, FALSE, js->unregistration_user_data);
1279 } else if(!strcmp(type,"result")) { 1284 } else {
1280 purple_notify_info(js->gc, _("Account successfully unregistered"), 1285 purple_notify_info(js->gc, _("Account successfully unregistered"),
1281 _("Account successfully unregistered"), NULL); 1286 _("Account successfully unregistered"), NULL);
1282 if(js->unregistration_cb) 1287 if(js->unregistration_cb)
1283 js->unregistration_cb(account, TRUE, js->unregistration_user_data); 1288 js->unregistration_cb(account, TRUE, js->unregistration_user_data);
1284 } 1289 }
1330 } 1335 }
1331 1336
1332 void jabber_close(PurpleConnection *gc) 1337 void jabber_close(PurpleConnection *gc)
1333 { 1338 {
1334 JabberStream *js = gc->proto_data; 1339 JabberStream *js = gc->proto_data;
1340
1341 /* Close all of the open Jingle sessions on this stream */
1342 jingle_terminate_sessions(js);
1335 1343
1336 /* Don't perform any actions on the ssl connection 1344 /* Don't perform any actions on the ssl connection
1337 * if we were forcibly disconnected because it will crash 1345 * if we were forcibly disconnected because it will crash
1338 * on some SSL backends. 1346 * on some SSL backends.
1339 */ 1347 */
1433 purple_timeout_remove(js->keepalive_timeout); 1441 purple_timeout_remove(js->keepalive_timeout);
1434 1442
1435 g_free(js->srv_rec); 1443 g_free(js->srv_rec);
1436 js->srv_rec = NULL; 1444 js->srv_rec = NULL;
1437 1445
1446 g_free(js->stun_ip);
1447 js->stun_ip = NULL;
1448
1449 /* cancel DNS query for STUN, if one is ongoing */
1450 if (js->stun_query) {
1451 purple_dnsquery_destroy(js->stun_query);
1452 js->stun_query = NULL;
1453 }
1454
1438 g_free(js); 1455 g_free(js);
1439 1456
1440 gc->proto_data = NULL; 1457 gc->proto_data = NULL;
1441 } 1458 }
1442 1459
1504 JabberStream *js = gc->proto_data; 1521 JabberStream *js = gc->proto_data;
1505 1522
1506 js->idle = idle ? time(NULL) - idle : idle; 1523 js->idle = idle ? time(NULL) - idle : idle;
1507 } 1524 }
1508 1525
1509 static void jabber_blocklist_parse(JabberStream *js, xmlnode *packet, gpointer data) 1526 static void jabber_blocklist_parse(JabberStream *js, const char *from,
1527 JabberIqType type, const char *id,
1528 xmlnode *packet, gpointer data)
1510 { 1529 {
1511 xmlnode *blocklist, *item; 1530 xmlnode *blocklist, *item;
1512 PurpleAccount *account; 1531 PurpleAccount *account;
1513 1532
1514 blocklist = xmlnode_get_child_with_namespace(packet, 1533 blocklist = xmlnode_get_child_with_namespace(packet,
1928 1947
1929 return types; 1948 return types;
1930 } 1949 }
1931 1950
1932 static void 1951 static void
1933 jabber_password_change_result_cb(JabberStream *js, xmlnode *packet, 1952 jabber_password_change_result_cb(JabberStream *js, const char *from,
1934 gpointer data) 1953 JabberIqType type, const char *id,
1935 { 1954 xmlnode *packet, gpointer data)
1936 const char *type; 1955 {
1937 1956 if (type == JABBER_IQ_RESULT) {
1938 type = xmlnode_get_attrib(packet, "type");
1939
1940 if(type && !strcmp(type, "result")) {
1941 purple_notify_info(js->gc, _("Password Changed"), _("Password Changed"), 1957 purple_notify_info(js->gc, _("Password Changed"), _("Password Changed"),
1942 _("Your password has been changed.")); 1958 _("Your password has been changed."));
1943 1959
1944 purple_account_set_password(js->gc->account, (char *)data); 1960 purple_account_set_password(js->gc->account, (char *)data);
1945 } else { 1961 } else {
2467 } 2483 }
2468 2484
2469 static PurpleCmdRet jabber_cmd_ping(PurpleConversation *conv, 2485 static PurpleCmdRet jabber_cmd_ping(PurpleConversation *conv,
2470 const char *cmd, char **args, char **error, void *data) 2486 const char *cmd, char **args, char **error, void *data)
2471 { 2487 {
2488 PurpleAccount *account;
2489 PurpleConnection *pc;
2490
2472 if(!args || !args[0]) 2491 if(!args || !args[0])
2473 return PURPLE_CMD_RET_FAILED; 2492 return PURPLE_CMD_RET_FAILED;
2474 2493
2475 if(!jabber_ping_jid(conv, args[0])) { 2494 account = purple_conversation_get_account(conv);
2495 pc = purple_account_get_connection(account);
2496
2497 if(!jabber_ping_jid(purple_connection_get_protocol_data(pc), args[0])) {
2476 *error = g_strdup_printf(_("Unable to ping user %s"), args[0]); 2498 *error = g_strdup_printf(_("Unable to ping user %s"), args[0]);
2477 return PURPLE_CMD_RET_FAILED; 2499 return PURPLE_CMD_RET_FAILED;
2478 } 2500 }
2479 2501
2480 return PURPLE_CMD_RET_OK; 2502 return PURPLE_CMD_RET_OK;
2600 2622
2601 2623
2602 gboolean jabber_offline_message(const PurpleBuddy *buddy) 2624 gboolean jabber_offline_message(const PurpleBuddy *buddy)
2603 { 2625 {
2604 return TRUE; 2626 return TRUE;
2627 }
2628
2629 #ifdef USE_VV
2630 typedef struct {
2631 PurpleConnection *pc;
2632 gchar *who;
2633 PurpleMediaSessionType type;
2634
2635 } JabberMediaRequest;
2636
2637 static void
2638 jabber_media_cancel_cb(JabberMediaRequest *request,
2639 PurpleRequestFields *fields)
2640 {
2641 g_free(request->who);
2642 g_free(request);
2643 }
2644
2645 static void
2646 jabber_media_ok_cb(JabberMediaRequest *request, PurpleRequestFields *fields)
2647 {
2648 PurpleRequestField *field =
2649 purple_request_fields_get_field(fields, "resource");
2650 int selected_id = purple_request_field_choice_get_value(field);
2651 GList *labels = purple_request_field_choice_get_labels(field);
2652 gchar *who = g_strdup_printf("%s/%s", request->who,
2653 (gchar*)g_list_nth_data(labels, selected_id));
2654 jabber_initiate_media(request->pc, who, request->type);
2655
2656 g_free(who);
2657 g_free(request->who);
2658 g_free(request);
2659 }
2660 #endif
2661
2662 gboolean
2663 jabber_initiate_media(PurpleConnection *gc, const char *who,
2664 PurpleMediaSessionType type)
2665 {
2666 #ifdef USE_VV
2667 JabberStream *js = (JabberStream *) gc->proto_data;
2668 JabberBuddy *jb;
2669 JabberBuddyResource *jbr = NULL;
2670 char *resource;
2671
2672 if (!js) {
2673 purple_debug_error("jabber",
2674 "jabber_initiate_media: NULL stream\n");
2675 return FALSE;
2676 }
2677
2678
2679 if((resource = jabber_get_resource(who)) != NULL) {
2680 /* they've specified a resource, no need to ask or
2681 * default or anything, just do it */
2682
2683 jb = jabber_buddy_find(js, who, FALSE);
2684 jbr = jabber_buddy_find_resource(jb, resource);
2685 g_free(resource);
2686
2687 if (type & PURPLE_MEDIA_AUDIO &&
2688 !jabber_resource_has_capability(jbr,
2689 JINGLE_APP_RTP_SUPPORT_AUDIO) &&
2690 jabber_resource_has_capability(jbr,
2691 GOOGLE_VOICE_CAP))
2692 return jabber_google_session_initiate(
2693 gc->proto_data, who, type);
2694 else
2695 return jingle_rtp_initiate_media(
2696 gc->proto_data, who, type);
2697 }
2698
2699 jb = jabber_buddy_find(js, who, FALSE);
2700
2701 if(!jb || !jb->resources) {
2702 /* no resources online, we're trying to initiate with someone
2703 * whose presence we're not subscribed to, or
2704 * someone who is offline. Let's inform the user */
2705 char *msg;
2706
2707 if(!jb) {
2708 msg = g_strdup_printf(_("Unable to initiate media with %s: invalid JID"), who);
2709 } else if(jb->subscription & JABBER_SUB_TO) {
2710 msg = g_strdup_printf(_("Unable to initiate media with %s: user is not online"), who);
2711 } else {
2712 msg = g_strdup_printf(_("Unable to initiate media with %s: not subscribed to user presence"), who);
2713 }
2714
2715 purple_notify_error(js->gc, _("Media Initiation Failed"),
2716 _("Media Initiation Failed"), msg);
2717 g_free(msg);
2718 return FALSE;
2719 } else if(!jb->resources->next) {
2720 /* only 1 resource online (probably our most common case)
2721 * so no need to ask who to initiate with */
2722 gchar *name;
2723 gboolean result;
2724 jbr = jb->resources->data;
2725 name = g_strdup_printf("%s/%s", who, jbr->name);
2726 result = jabber_initiate_media(gc, name, type);
2727 g_free(name);
2728 return result;
2729 } else {
2730 /* we've got multiple resources,
2731 * we need to pick one to initiate with */
2732 GList *l;
2733 char *msg;
2734 PurpleRequestFields *fields;
2735 PurpleRequestField *field = purple_request_field_choice_new(
2736 "resource", _("Resource"), 0);
2737 PurpleRequestFieldGroup *group;
2738 JabberMediaRequest *request;
2739
2740 for(l = jb->resources; l; l = l->next)
2741 {
2742 JabberBuddyResource *ljbr = l->data;
2743 PurpleMediaCaps caps;
2744 gchar *name;
2745 name = g_strdup_printf("%s/%s", who, ljbr->name);
2746 caps = jabber_get_media_caps(gc, name);
2747 g_free(name);
2748
2749 if ((type & PURPLE_MEDIA_AUDIO) &&
2750 (type & PURPLE_MEDIA_VIDEO)) {
2751 if (caps & PURPLE_MEDIA_CAPS_AUDIO_VIDEO) {
2752 jbr = ljbr;
2753 purple_request_field_choice_add(
2754 field, jbr->name);
2755 }
2756 } else if (type & (PURPLE_MEDIA_AUDIO) &&
2757 (caps & PURPLE_MEDIA_CAPS_AUDIO)) {
2758 jbr = ljbr;
2759 purple_request_field_choice_add(
2760 field, jbr->name);
2761 }else if (type & (PURPLE_MEDIA_VIDEO) &&
2762 (caps & PURPLE_MEDIA_CAPS_VIDEO)) {
2763 jbr = ljbr;
2764 purple_request_field_choice_add(
2765 field, jbr->name);
2766 }
2767 }
2768
2769 if (jbr == NULL) {
2770 purple_debug_error("jabber",
2771 "No resources available\n");
2772 return FALSE;
2773 }
2774
2775 if (g_list_length(purple_request_field_choice_get_labels(
2776 field)) <= 1) {
2777 gchar *name;
2778 gboolean result;
2779 purple_request_field_destroy(field);
2780 name = g_strdup_printf("%s/%s", who, jbr->name);
2781 result = jabber_initiate_media(gc, name, type);
2782 g_free(name);
2783 return result;
2784 }
2785
2786 msg = g_strdup_printf(_("Please select the resource of %s with which you would like to start a media session."), who);
2787 fields = purple_request_fields_new();
2788 group = purple_request_field_group_new(NULL);
2789 request = g_new0(JabberMediaRequest, 1);
2790 request->pc = gc;
2791 request->who = g_strdup(who);
2792 request->type = type;
2793
2794 purple_request_field_group_add_field(group, field);
2795 purple_request_fields_add_group(fields, group);
2796 purple_request_fields(gc, _("Select a Resource"), msg, NULL,
2797 fields, _("Initiate Media"),
2798 G_CALLBACK(jabber_media_ok_cb), _("Cancel"),
2799 G_CALLBACK(jabber_media_cancel_cb),
2800 gc->account, who, NULL, request);
2801
2802 g_free(msg);
2803 return TRUE;
2804 }
2805 #endif
2806 return FALSE;
2807 }
2808
2809 PurpleMediaCaps jabber_get_media_caps(PurpleConnection *gc, const char *who)
2810 {
2811 #ifdef USE_VV
2812 JabberStream *js = (JabberStream *) gc->proto_data;
2813 JabberBuddy *jb;
2814 JabberBuddyResource *jbr;
2815 PurpleMediaCaps caps = PURPLE_MEDIA_CAPS_NONE;
2816 gchar *resource;
2817
2818 if (!js) {
2819 purple_debug_info("jabber",
2820 "jabber_can_do_media: NULL stream\n");
2821 return FALSE;
2822 }
2823
2824 if ((resource = jabber_get_resource(who)) != NULL) {
2825 /* they've specified a resource, no need to ask or
2826 * default or anything, just do it */
2827
2828 jb = jabber_buddy_find(js, who, FALSE);
2829 jbr = jabber_buddy_find_resource(jb, resource);
2830 g_free(resource);
2831
2832 if (!jbr) {
2833 purple_debug_error("jabber", "jabber_get_media_caps:"
2834 " Can't find resource %s\n", who);
2835 return caps;
2836 }
2837
2838 if (jabber_resource_has_capability(jbr,
2839 JINGLE_APP_RTP_SUPPORT_AUDIO))
2840 caps |= PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION |
2841 PURPLE_MEDIA_CAPS_AUDIO;
2842 if (jabber_resource_has_capability(jbr,
2843 JINGLE_APP_RTP_SUPPORT_VIDEO))
2844 caps |= PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION |
2845 PURPLE_MEDIA_CAPS_VIDEO;
2846 if (caps & PURPLE_MEDIA_CAPS_AUDIO && caps &
2847 PURPLE_MEDIA_CAPS_VIDEO)
2848 caps |= PURPLE_MEDIA_CAPS_AUDIO_VIDEO;
2849 if (caps != PURPLE_MEDIA_CAPS_NONE) {
2850 if (!jabber_resource_has_capability(jbr,
2851 JINGLE_TRANSPORT_ICEUDP) &&
2852 !jabber_resource_has_capability(jbr,
2853 JINGLE_TRANSPORT_RAWUDP)) {
2854 purple_debug_info("jingle-rtp", "Buddy doesn't "
2855 "support the same transport types\n");
2856 caps = PURPLE_MEDIA_CAPS_NONE;
2857 } else
2858 caps |= PURPLE_MEDIA_CAPS_MODIFY_SESSION |
2859 PURPLE_MEDIA_CAPS_CHANGE_DIRECTION;
2860 }
2861 if (jabber_resource_has_capability(jbr, GOOGLE_VOICE_CAP))
2862 caps |= PURPLE_MEDIA_CAPS_AUDIO;
2863 return caps;
2864 }
2865
2866 jb = jabber_buddy_find(js, who, FALSE);
2867
2868 if(!jb || !jb->resources) {
2869 /* no resources online, we're trying to get caps for someone
2870 * whose presence we're not subscribed to, or
2871 * someone who is offline. */
2872 return caps;
2873 } else if(!jb->resources->next) {
2874 /* only 1 resource online (probably our most common case) */
2875 gchar *name;
2876 jbr = jb->resources->data;
2877 name = g_strdup_printf("%s/%s", who, jbr->name);
2878 caps = jabber_get_media_caps(gc, name);
2879 g_free(name);
2880 } else {
2881 /* we've got multiple resources, combine their caps */
2882 GList *l;
2883
2884 for(l = jb->resources; l; l = l->next)
2885 {
2886 gchar *name;
2887 jbr = l->data;
2888 name = g_strdup_printf("%s/%s", who, jbr->name);
2889 caps |= jabber_get_media_caps(gc, name);
2890 g_free(name);
2891 }
2892 }
2893
2894 return caps;
2895 #else
2896 return PURPLE_MEDIA_CAPS_NONE;
2897 #endif
2605 } 2898 }
2606 2899
2607 void jabber_register_commands(void) 2900 void jabber_register_commands(void)
2608 { 2901 {
2609 purple_cmd_register("config", "", PURPLE_CMD_P_PRPL, 2902 purple_cmd_register("config", "", PURPLE_CMD_P_PRPL,