2 * Purple is the legal property of its developers, whose names are too numerous
3 * to list here. Please refer to the COPYRIGHT file distributed with this
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
23 #include "google_session.h"
26 #include "jingle/jingle.h"
33 GList
*remote_audio_candidates
; /* list of PurpleMediaCandidate */
34 GList
*remote_video_candidates
; /* list of PurpleMediaCandidate */
35 gboolean added_streams
; /* this indicates if the streams have been
36 to media (ie. after getting relay credentials */
37 } GoogleAVSessionData
;
40 google_session_id_equal(gconstpointer a
, gconstpointer b
)
42 GoogleSessionId
*c
= (GoogleSessionId
*)a
;
43 GoogleSessionId
*d
= (GoogleSessionId
*)b
;
45 return !strcmp(c
->id
, d
->id
) && !strcmp(c
->initiator
, d
->initiator
);
49 google_session_destroy(GoogleSession
*session
)
51 GoogleAVSessionData
*session_data
=
52 (GoogleAVSessionData
*) session
->session_data
;
53 g_free(session
->id
.id
);
54 g_free(session
->id
.initiator
);
55 g_free(session
->remote_jid
);
57 if (session_data
->remote_audio_candidates
)
58 purple_media_candidate_list_free(session_data
->remote_audio_candidates
);
60 if (session_data
->remote_video_candidates
)
61 purple_media_candidate_list_free(session_data
->remote_video_candidates
);
63 if (session
->description
)
64 purple_xmlnode_free(session
->description
);
66 g_free(session
->session_data
);
70 static PurpleXmlNode
*
71 google_session_create_xmlnode(GoogleSession
*session
, const char *type
)
73 PurpleXmlNode
*node
= purple_xmlnode_new("session");
74 purple_xmlnode_set_namespace(node
, NS_GOOGLE_SESSION
);
75 purple_xmlnode_set_attrib(node
, "id", session
->id
.id
);
76 purple_xmlnode_set_attrib(node
, "initiator", session
->id
.initiator
);
77 purple_xmlnode_set_attrib(node
, "type", type
);
82 google_session_send_candidates(PurpleMedia
*media
, gchar
*session_id
,
83 gchar
*participant
, GoogleSession
*session
)
85 PurpleMedia
*session_media
=
86 ((GoogleAVSessionData
*) session
->session_data
)->media
;
88 purple_media_get_local_candidates(session_media
, session_id
,
91 PurpleMediaCandidate
*transport
;
92 gboolean video
= FALSE
;
94 if (!strcmp(session_id
, "google-video"))
97 for (iter
= candidates
; iter
; iter
= iter
->next
) {
99 gchar
*ip
, *port
, *username
, *password
;
101 PurpleMediaCandidateType type
;
103 PurpleXmlNode
*candidate
;
105 transport
= PURPLE_MEDIA_CANDIDATE(iter
->data
);
106 component_id
= purple_media_candidate_get_component_id(
109 iq
= jabber_iq_new(session
->js
, JABBER_IQ_SET
);
110 sess
= google_session_create_xmlnode(session
, "candidates");
111 purple_xmlnode_insert_child(iq
->node
, sess
);
112 purple_xmlnode_set_attrib(iq
->node
, "to", session
->remote_jid
);
114 candidate
= purple_xmlnode_new("candidate");
116 ip
= purple_media_candidate_get_ip(transport
);
117 port
= g_strdup_printf("%d",
118 purple_media_candidate_get_port(transport
));
119 g_ascii_dtostr(pref
, 16,
120 purple_media_candidate_get_priority(transport
) / 1000.0);
121 username
= purple_media_candidate_get_username(transport
);
122 password
= purple_media_candidate_get_password(transport
);
123 type
= purple_media_candidate_get_candidate_type(transport
);
125 purple_xmlnode_set_attrib(candidate
, "address", ip
);
126 purple_xmlnode_set_attrib(candidate
, "port", port
);
127 purple_xmlnode_set_attrib(candidate
, "name",
128 component_id
== PURPLE_MEDIA_COMPONENT_RTP
?
129 video
? "video_rtp" : "rtp" :
130 component_id
== PURPLE_MEDIA_COMPONENT_RTCP
?
131 video
? "video_rtcp" : "rtcp" : "none");
132 purple_xmlnode_set_attrib(candidate
, "username", username
);
134 * As of this writing, Farsight 2 in Google compatibility
135 * mode doesn't provide a password. The Gmail client
136 * requires this to be set.
138 purple_xmlnode_set_attrib(candidate
, "password",
139 password
!= NULL
? password
: "");
140 purple_xmlnode_set_attrib(candidate
, "preference", pref
);
141 purple_xmlnode_set_attrib(candidate
, "protocol",
142 purple_media_candidate_get_protocol(transport
)
143 == PURPLE_MEDIA_NETWORK_PROTOCOL_UDP
?
145 purple_xmlnode_set_attrib(candidate
, "type", type
==
146 PURPLE_MEDIA_CANDIDATE_TYPE_HOST
? "local" :
148 PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX
? "stun" :
150 PURPLE_MEDIA_CANDIDATE_TYPE_RELAY
? "relay" :
152 purple_xmlnode_set_attrib(candidate
, "generation", "0");
153 purple_xmlnode_set_attrib(candidate
, "network", "0");
154 purple_xmlnode_insert_child(sess
, candidate
);
163 purple_media_candidate_list_free(candidates
);
167 google_session_ready(GoogleSession
*session
)
170 ((GoogleAVSessionData
*)session
->session_data
)->media
;
172 ((GoogleAVSessionData
*)session
->session_data
)->video
;
173 if (purple_media_codecs_ready(media
, NULL
) &&
174 purple_media_candidates_prepared(media
, NULL
, NULL
)) {
175 gchar
*me
= g_strdup_printf("%s@%s/%s",
176 session
->js
->user
->node
,
177 session
->js
->user
->domain
,
178 session
->js
->user
->resource
);
180 PurpleXmlNode
*sess
, *desc
, *payload
;
181 GList
*codecs
, *iter
;
182 gboolean is_initiator
= !strcmp(session
->id
.initiator
, me
);
185 !purple_media_accepted(media
, NULL
, NULL
)) {
190 iq
= jabber_iq_new(session
->js
, JABBER_IQ_SET
);
193 purple_xmlnode_set_attrib(iq
->node
, "to", session
->remote_jid
);
194 purple_xmlnode_set_attrib(iq
->node
, "from", session
->id
.initiator
);
195 sess
= google_session_create_xmlnode(session
, "initiate");
197 google_session_send_candidates(media
,
198 "google-voice", session
->remote_jid
,
200 google_session_send_candidates(media
,
201 "google-video", session
->remote_jid
,
203 purple_xmlnode_set_attrib(iq
->node
, "to", session
->remote_jid
);
204 purple_xmlnode_set_attrib(iq
->node
, "from", me
);
205 sess
= google_session_create_xmlnode(session
, "accept");
207 purple_xmlnode_insert_child(iq
->node
, sess
);
208 desc
= purple_xmlnode_new_child(sess
, "description");
210 purple_xmlnode_set_namespace(desc
, NS_GOOGLE_SESSION_VIDEO
);
212 purple_xmlnode_set_namespace(desc
, NS_GOOGLE_SESSION_PHONE
);
214 codecs
= purple_media_get_codecs(media
, "google-video");
216 for (iter
= codecs
; iter
; iter
= g_list_next(iter
)) {
217 PurpleMediaCodec
*codec
= (PurpleMediaCodec
*)iter
->data
;
218 gchar
*id
= g_strdup_printf("%d",
219 purple_media_codec_get_id(codec
));
220 gchar
*encoding_name
=
221 purple_media_codec_get_encoding_name(codec
);
222 payload
= purple_xmlnode_new_child(desc
, "payload-type");
223 purple_xmlnode_set_attrib(payload
, "id", id
);
224 purple_xmlnode_set_attrib(payload
, "name", encoding_name
);
225 purple_xmlnode_set_attrib(payload
, "width", "320");
226 purple_xmlnode_set_attrib(payload
, "height", "200");
227 purple_xmlnode_set_attrib(payload
, "framerate", "30");
228 g_free(encoding_name
);
231 purple_media_codec_list_free(codecs
);
233 codecs
= purple_media_get_codecs(media
, "google-voice");
235 for (iter
= codecs
; iter
; iter
= g_list_next(iter
)) {
236 PurpleMediaCodec
*codec
= (PurpleMediaCodec
*)iter
->data
;
237 gchar
*id
= g_strdup_printf("%d",
238 purple_media_codec_get_id(codec
));
239 gchar
*encoding_name
=
240 purple_media_codec_get_encoding_name(codec
);
241 gchar
*clock_rate
= g_strdup_printf("%d",
242 purple_media_codec_get_clock_rate(codec
));
243 payload
= purple_xmlnode_new_child(desc
, "payload-type");
245 purple_xmlnode_set_namespace(payload
, NS_GOOGLE_SESSION_PHONE
);
246 purple_xmlnode_set_attrib(payload
, "id", id
);
248 * Hack to make Gmail accept speex as the codec.
249 * It shouldn't have to be case sensitive.
251 if (purple_strequal(encoding_name
, "SPEEX"))
252 purple_xmlnode_set_attrib(payload
, "name", "speex");
254 purple_xmlnode_set_attrib(payload
, "name", encoding_name
);
255 purple_xmlnode_set_attrib(payload
, "clockrate", clock_rate
);
257 g_free(encoding_name
);
260 purple_media_codec_list_free(codecs
);
265 google_session_send_candidates(media
,
266 "google-voice", session
->remote_jid
,
268 google_session_send_candidates(media
,
269 "google-video", session
->remote_jid
,
273 g_signal_handlers_disconnect_by_func(G_OBJECT(media
),
274 G_CALLBACK(google_session_ready
), session
);
279 google_session_state_changed_cb(PurpleMedia
*media
, PurpleMediaState state
,
280 gchar
*sid
, gchar
*name
, GoogleSession
*session
)
282 if (sid
== NULL
&& name
== NULL
) {
283 if (state
== PURPLE_MEDIA_STATE_END
) {
284 google_session_destroy(session
);
290 google_session_stream_info_cb(PurpleMedia
*media
, PurpleMediaInfoType type
,
291 gchar
*sid
, gchar
*name
, gboolean local
,
292 GoogleSession
*session
)
294 if (sid
!= NULL
|| name
!= NULL
)
297 if (type
== PURPLE_MEDIA_INFO_HANGUP
) {
299 JabberIq
*iq
= jabber_iq_new(session
->js
, JABBER_IQ_SET
);
301 purple_xmlnode_set_attrib(iq
->node
, "to", session
->remote_jid
);
302 sess
= google_session_create_xmlnode(session
, "terminate");
303 purple_xmlnode_insert_child(iq
->node
, sess
);
306 } else if (type
== PURPLE_MEDIA_INFO_REJECT
) {
308 JabberIq
*iq
= jabber_iq_new(session
->js
, JABBER_IQ_SET
);
310 purple_xmlnode_set_attrib(iq
->node
, "to", session
->remote_jid
);
311 sess
= google_session_create_xmlnode(session
, "reject");
312 purple_xmlnode_insert_child(iq
->node
, sess
);
315 } else if (type
== PURPLE_MEDIA_INFO_ACCEPT
&& local
== TRUE
) {
316 google_session_ready(session
);
321 jabber_google_session_get_params(JabberStream
*js
, const gchar
*relay_ip
,
322 guint16 relay_udp
, guint16 relay_tcp
, guint16 relay_ssltcp
,
323 const gchar
*relay_username
, const gchar
*relay_password
, guint
*num
)
327 jingle_get_params(js
, relay_ip
, relay_udp
, relay_tcp
, relay_ssltcp
,
328 relay_username
, relay_password
, &num_params
);
329 GParameter
*new_params
= g_new0(GParameter
, num_params
+ 1);
331 memcpy(new_params
, params
, sizeof(GParameter
) * num_params
);
333 purple_debug_info("jabber", "setting Google jingle compatibility param\n");
334 new_params
[num_params
].name
= "compatibility-mode";
335 g_value_init(&new_params
[num_params
].value
, G_TYPE_UINT
);
336 g_value_set_uint(&new_params
[num_params
].value
, 1); /* NICE_COMPATIBILITY_GOOGLE */
339 *num
= num_params
+ 1;
345 jabber_google_relay_response_session_initiate_cb(GoogleSession
*session
,
346 const gchar
*relay_ip
, guint relay_udp
, guint relay_tcp
, guint relay_ssltcp
,
347 const gchar
*relay_username
, const gchar
*relay_password
)
351 JabberStream
*js
= session
->js
;
352 GoogleAVSessionData
*session_data
=
353 (GoogleAVSessionData
*) session
->session_data
;
355 session_data
->media
= purple_media_manager_create_media(
356 purple_media_manager_get(),
357 purple_connection_get_account(js
->gc
),
358 "fsrtpconference", session
->remote_jid
, TRUE
);
360 purple_media_set_protocol_data(session_data
->media
, session
);
362 g_signal_connect_swapped(G_OBJECT(session_data
->media
),
363 "candidates-prepared",
364 G_CALLBACK(google_session_ready
), session
);
365 g_signal_connect_swapped(G_OBJECT(session_data
->media
), "codecs-changed",
366 G_CALLBACK(google_session_ready
), session
);
367 g_signal_connect(G_OBJECT(session_data
->media
), "state-changed",
368 G_CALLBACK(google_session_state_changed_cb
), session
);
369 g_signal_connect(G_OBJECT(session_data
->media
), "stream-info",
370 G_CALLBACK(google_session_stream_info_cb
), session
);
373 jabber_google_session_get_params(js
, relay_ip
, relay_udp
, relay_tcp
,
374 relay_ssltcp
, relay_username
, relay_password
, &num_params
);
376 if (purple_media_add_stream(session_data
->media
, "google-voice",
377 session
->remote_jid
, PURPLE_MEDIA_AUDIO
,
378 TRUE
, "nice", num_params
, params
) == FALSE
||
379 (session_data
->video
&& purple_media_add_stream(
380 session_data
->media
, "google-video",
381 session
->remote_jid
, PURPLE_MEDIA_VIDEO
,
382 TRUE
, "nice", num_params
, params
) == FALSE
)) {
383 purple_media_error(session_data
->media
, "Error adding stream.");
384 purple_media_end(session_data
->media
, NULL
, NULL
);
386 session_data
->added_streams
= TRUE
;
394 jabber_google_session_initiate(JabberStream
*js
, const gchar
*who
, PurpleMediaSessionType type
)
396 GoogleSession
*session
;
398 JabberBuddyResource
*jbr
;
400 GoogleAVSessionData
*session_data
= NULL
;
402 /* construct JID to send to */
403 jb
= jabber_buddy_find(js
, who
, FALSE
);
405 purple_debug_error("jingle-rtp",
406 "Could not find Jabber buddy\n");
409 jbr
= jabber_buddy_find_resource(jb
, NULL
);
411 purple_debug_error("jingle-rtp",
412 "Could not find buddy's resource\n");
415 if ((strchr(who
, '/') == NULL
) && jbr
&& (jbr
->name
!= NULL
)) {
416 jid
= g_strdup_printf("%s/%s", who
, jbr
->name
);
421 session
= g_new0(GoogleSession
, 1);
422 session
->id
.id
= jabber_get_next_id(js
);
423 session
->id
.initiator
= g_strdup_printf("%s@%s/%s", js
->user
->node
,
424 js
->user
->domain
, js
->user
->resource
);
425 session
->state
= SENT_INITIATE
;
427 session
->remote_jid
= jid
;
428 session_data
= g_new0(GoogleAVSessionData
, 1);
429 session
->session_data
= session_data
;
431 if (type
& PURPLE_MEDIA_VIDEO
)
432 session_data
->video
= TRUE
;
434 /* if we got a relay token and relay host in google:jingleinfo, issue an
435 HTTP request to get that data */
436 if (js
->google_relay_host
&& js
->google_relay_token
) {
437 jabber_google_do_relay_request(js
, session
,
438 jabber_google_relay_response_session_initiate_cb
);
440 jabber_google_relay_response_session_initiate_cb(session
, NULL
, 0, 0, 0,
444 /* we don't actually know yet wether it succeeded... maybe this is very
450 jabber_google_relay_response_session_handle_initiate_cb(GoogleSession
*session
,
451 const gchar
*relay_ip
, guint relay_udp
, guint relay_tcp
, guint relay_ssltcp
,
452 const gchar
*relay_username
, const gchar
*relay_password
)
456 JabberStream
*js
= session
->js
;
457 PurpleXmlNode
*codec_element
;
459 PurpleMediaCodec
*codec
;
460 GList
*video_codecs
= NULL
;
461 GList
*codecs
= NULL
;
463 GoogleAVSessionData
*session_data
=
464 (GoogleAVSessionData
*) session
->session_data
;
467 jabber_google_session_get_params(js
, relay_ip
, relay_udp
, relay_tcp
,
468 relay_ssltcp
, relay_username
, relay_password
, &num_params
);
470 if (purple_media_add_stream(session_data
->media
, "google-voice",
471 session
->remote_jid
, PURPLE_MEDIA_AUDIO
, FALSE
,
472 "nice", num_params
, params
) == FALSE
||
473 (session_data
->video
&& purple_media_add_stream(
474 session_data
->media
, "google-video",
475 session
->remote_jid
, PURPLE_MEDIA_VIDEO
,
476 FALSE
, "nice", num_params
, params
) == FALSE
)) {
477 purple_media_error(session_data
->media
, "Error adding stream.");
478 purple_media_stream_info(session_data
->media
,
479 PURPLE_MEDIA_INFO_REJECT
, NULL
, NULL
, TRUE
);
481 /* successfully added stream(s) */
482 session_data
->added_streams
= TRUE
;
484 if (session_data
->remote_audio_candidates
) {
485 purple_media_add_remote_candidates(session_data
->media
,
486 "google-voice", session
->remote_jid
,
487 session_data
->remote_audio_candidates
);
488 purple_media_candidate_list_free(session_data
->remote_audio_candidates
);
489 session_data
->remote_audio_candidates
= NULL
;
491 if (session_data
->remote_video_candidates
) {
492 purple_media_add_remote_candidates(session_data
->media
,
493 "google-video", session
->remote_jid
,
494 session_data
->remote_video_candidates
);
495 purple_media_candidate_list_free(session_data
->remote_video_candidates
);
496 session_data
->remote_video_candidates
= NULL
;
502 for (codec_element
= purple_xmlnode_get_child(session
->description
, "payload-type");
503 codec_element
; codec_element
= codec_element
->next
) {
504 const char *id
, *encoding_name
, *clock_rate
;
506 if (codec_element
->name
&&
507 strcmp(codec_element
->name
, "payload-type"))
510 xmlns
= purple_xmlnode_get_namespace(codec_element
);
511 encoding_name
= purple_xmlnode_get_attrib(codec_element
, "name");
512 id
= purple_xmlnode_get_attrib(codec_element
, "id");
514 if (!session_data
->video
||
515 (xmlns
&& !strcmp(xmlns
, NS_GOOGLE_SESSION_PHONE
))) {
516 clock_rate
= purple_xmlnode_get_attrib(
517 codec_element
, "clockrate");
520 /*width = purple_xmlnode_get_attrib(codec_element, "width");
521 height = purple_xmlnode_get_attrib(codec_element, "height");
522 framerate = purple_xmlnode_get_attrib(
523 codec_element, "framerate");*/
524 clock_rate
= "90000";
529 codec
= purple_media_codec_new(atoi(id
), encoding_name
,
530 video
? PURPLE_MEDIA_VIDEO
:
532 clock_rate
? atoi(clock_rate
) : 0);
534 video_codecs
= g_list_append(
535 video_codecs
, codec
);
537 codecs
= g_list_append(codecs
, codec
);
542 purple_media_set_remote_codecs(session_data
->media
, "google-voice",
543 session
->remote_jid
, codecs
);
545 purple_media_set_remote_codecs(session_data
->media
, "google-video",
546 session
->remote_jid
, video_codecs
);
548 purple_media_codec_list_free(codecs
);
549 purple_media_codec_list_free(video_codecs
);
551 result
= jabber_iq_new(js
, JABBER_IQ_RESULT
);
552 jabber_iq_set_id(result
, session
->iq_id
);
553 purple_xmlnode_set_attrib(result
->node
, "to", session
->remote_jid
);
554 jabber_iq_send(result
);
558 google_session_handle_initiate(JabberStream
*js
, GoogleSession
*session
, PurpleXmlNode
*sess
, const char *iq_id
)
561 GoogleAVSessionData
*session_data
=
562 (GoogleAVSessionData
*) session
->session_data
;
564 if (session
->state
!= UNINIT
) {
565 purple_debug_error("jabber", "Received initiate for active session.\n");
569 session
->description
= purple_xmlnode_copy(purple_xmlnode_get_child(sess
, "description"));
570 xmlns
= purple_xmlnode_get_namespace(session
->description
);
572 if (purple_strequal(xmlns
, NS_GOOGLE_SESSION_PHONE
))
573 session_data
->video
= FALSE
;
574 else if (purple_strequal(xmlns
, NS_GOOGLE_SESSION_VIDEO
))
575 session_data
->video
= TRUE
;
577 purple_debug_error("jabber", "Received initiate with "
578 "invalid namespace %s.\n", xmlns
);
582 session_data
->media
= purple_media_manager_create_media(
583 purple_media_manager_get(),
584 purple_connection_get_account(js
->gc
),
585 "fsrtpconference", session
->remote_jid
, FALSE
);
587 purple_media_set_protocol_data(session_data
->media
, session
);
589 g_signal_connect_swapped(G_OBJECT(session_data
->media
),
590 "candidates-prepared",
591 G_CALLBACK(google_session_ready
), session
);
592 g_signal_connect_swapped(G_OBJECT(session_data
->media
), "codecs-changed",
593 G_CALLBACK(google_session_ready
), session
);
594 g_signal_connect(G_OBJECT(session_data
->media
), "state-changed",
595 G_CALLBACK(google_session_state_changed_cb
), session
);
596 g_signal_connect(G_OBJECT(session_data
->media
), "stream-info",
597 G_CALLBACK(google_session_stream_info_cb
), session
);
599 session
->iq_id
= g_strdup(iq_id
);
601 if (js
->google_relay_host
&& js
->google_relay_token
) {
602 jabber_google_do_relay_request(js
, session
,
603 jabber_google_relay_response_session_handle_initiate_cb
);
605 jabber_google_relay_response_session_handle_initiate_cb(session
, NULL
,
606 0, 0, 0, NULL
, NULL
);
614 google_session_handle_candidates(JabberStream
*js
, GoogleSession
*session
, PurpleXmlNode
*sess
, const char *iq_id
)
617 GList
*list
= NULL
, *video_list
= NULL
;
621 GoogleAVSessionData
*session_data
=
622 (GoogleAVSessionData
*) session
->session_data
;
624 for (cand
= purple_xmlnode_get_child(sess
, "candidate"); cand
;
625 cand
= purple_xmlnode_get_next_twin(cand
)) {
626 PurpleMediaCandidate
*info
;
627 const gchar
*cname
= purple_xmlnode_get_attrib(cand
, "name");
628 const gchar
*type
= purple_xmlnode_get_attrib(cand
, "type");
629 const gchar
*protocol
= purple_xmlnode_get_attrib(cand
, "protocol");
630 const gchar
*address
= purple_xmlnode_get_attrib(cand
, "address");
631 const gchar
*port
= purple_xmlnode_get_attrib(cand
, "port");
632 const gchar
*preference
= purple_xmlnode_get_attrib(cand
, "preference");
635 if (cname
&& type
&& address
&& port
) {
636 PurpleMediaCandidateType candidate_type
;
637 guint prio
= preference
? g_ascii_strtod(preference
, NULL
) * 1000 : 0;
639 g_snprintf(n
, sizeof(n
), "S%d", name
++);
641 if (g_str_equal(type
, "local"))
642 candidate_type
= PURPLE_MEDIA_CANDIDATE_TYPE_HOST
;
643 else if (g_str_equal(type
, "stun"))
644 candidate_type
= PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX
;
645 else if (g_str_equal(type
, "relay"))
646 candidate_type
= PURPLE_MEDIA_CANDIDATE_TYPE_RELAY
;
648 candidate_type
= PURPLE_MEDIA_CANDIDATE_TYPE_HOST
;
650 if (purple_strequal(cname
, "rtcp") ||
651 purple_strequal(cname
, "video_rtcp"))
652 component_id
= PURPLE_MEDIA_COMPONENT_RTCP
;
654 component_id
= PURPLE_MEDIA_COMPONENT_RTP
;
656 info
= purple_media_candidate_new(n
, component_id
,
658 purple_strequal(protocol
, "udp") ?
659 PURPLE_MEDIA_NETWORK_PROTOCOL_UDP
:
660 PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE
,
663 g_object_set(info
, "username", purple_xmlnode_get_attrib(cand
, "username"),
664 "password", purple_xmlnode_get_attrib(cand
, "password"),
665 "priority", prio
, NULL
);
666 if (!strncmp(cname
, "video_", 6)) {
667 if (session_data
->added_streams
) {
668 video_list
= g_list_append(video_list
, info
);
670 session_data
->remote_video_candidates
=
671 g_list_append(session_data
->remote_video_candidates
,
675 if (session_data
->added_streams
) {
676 list
= g_list_append(list
, info
);
678 session_data
->remote_audio_candidates
=
679 g_list_append(session_data
->remote_audio_candidates
,
687 purple_media_add_remote_candidates(session_data
->media
, "google-voice",
688 session
->remote_jid
, list
);
689 purple_media_candidate_list_free(list
);
692 purple_media_add_remote_candidates(session_data
->media
, "google-video",
693 session
->remote_jid
, video_list
);
694 purple_media_candidate_list_free(video_list
);
697 result
= jabber_iq_new(js
, JABBER_IQ_RESULT
);
698 jabber_iq_set_id(result
, iq_id
);
699 purple_xmlnode_set_attrib(result
->node
, "to", session
->remote_jid
);
700 jabber_iq_send(result
);
704 google_session_handle_accept(JabberStream
*js
, GoogleSession
*session
, PurpleXmlNode
*sess
, const char *iq_id
)
706 PurpleXmlNode
*desc_element
= purple_xmlnode_get_child(sess
, "description");
707 PurpleXmlNode
*codec_element
= purple_xmlnode_get_child(
708 desc_element
, "payload-type");
709 GList
*codecs
= NULL
, *video_codecs
= NULL
;
710 JabberIq
*result
= NULL
;
711 const gchar
*xmlns
= purple_xmlnode_get_namespace(desc_element
);
712 gboolean video
= (xmlns
&& !strcmp(xmlns
, NS_GOOGLE_SESSION_VIDEO
));
713 GoogleAVSessionData
*session_data
=
714 (GoogleAVSessionData
*) session
->session_data
;
716 for (; codec_element
; codec_element
= codec_element
->next
) {
717 const gchar
*xmlns
, *encoding_name
, *id
,
719 gboolean video_codec
= FALSE
;
721 if (!purple_strequal(codec_element
->name
, "payload-type"))
724 xmlns
= purple_xmlnode_get_namespace(codec_element
);
725 encoding_name
= purple_xmlnode_get_attrib(codec_element
, "name");
726 id
= purple_xmlnode_get_attrib(codec_element
, "id");
728 if (!video
|| purple_strequal(xmlns
, NS_GOOGLE_SESSION_PHONE
))
729 clock_rate
= purple_xmlnode_get_attrib(
730 codec_element
, "clockrate");
732 clock_rate
= "90000";
733 /*width = purple_xmlnode_get_attrib(codec_element, "width");
734 height = purple_xmlnode_get_attrib(codec_element, "height");
735 framerate = purple_xmlnode_get_attrib(
736 codec_element, "framerate");*/
740 if (id
&& encoding_name
) {
741 PurpleMediaCodec
*codec
= purple_media_codec_new(
742 atoi(id
), encoding_name
,
743 video_codec
? PURPLE_MEDIA_VIDEO
:
745 clock_rate
? atoi(clock_rate
) : 0);
747 video_codecs
= g_list_append(
748 video_codecs
, codec
);
750 codecs
= g_list_append(codecs
, codec
);
755 purple_media_set_remote_codecs(session_data
->media
, "google-voice",
756 session
->remote_jid
, codecs
);
758 purple_media_set_remote_codecs(session_data
->media
, "google-video",
759 session
->remote_jid
, video_codecs
);
761 purple_media_stream_info(session_data
->media
, PURPLE_MEDIA_INFO_ACCEPT
,
764 result
= jabber_iq_new(js
, JABBER_IQ_RESULT
);
765 jabber_iq_set_id(result
, iq_id
);
766 purple_xmlnode_set_attrib(result
->node
, "to", session
->remote_jid
);
767 jabber_iq_send(result
);
771 google_session_handle_reject(JabberStream
*js
, GoogleSession
*session
, PurpleXmlNode
*sess
)
773 GoogleAVSessionData
*session_data
=
774 (GoogleAVSessionData
*) session
->session_data
;
775 purple_media_end(session_data
->media
, NULL
, NULL
);
779 google_session_handle_terminate(JabberStream
*js
, GoogleSession
*session
, PurpleXmlNode
*sess
)
781 GoogleAVSessionData
*session_data
=
782 (GoogleAVSessionData
*) session
->session_data
;
783 purple_media_end(session_data
->media
, NULL
, NULL
);
787 google_session_parse_iq(JabberStream
*js
, GoogleSession
*session
, PurpleXmlNode
*sess
, const char *iq_id
)
789 const char *type
= purple_xmlnode_get_attrib(sess
, "type");
791 if (!strcmp(type
, "initiate")) {
792 google_session_handle_initiate(js
, session
, sess
, iq_id
);
793 } else if (!strcmp(type
, "accept")) {
794 google_session_handle_accept(js
, session
, sess
, iq_id
);
795 } else if (!strcmp(type
, "reject")) {
796 google_session_handle_reject(js
, session
, sess
);
797 } else if (!strcmp(type
, "terminate")) {
798 google_session_handle_terminate(js
, session
, sess
);
799 } else if (!strcmp(type
, "candidates")) {
800 google_session_handle_candidates(js
, session
, sess
, iq_id
);
805 jabber_google_session_parse(JabberStream
*js
, const char *from
,
806 JabberIqType type
, const char *iq_id
,
807 PurpleXmlNode
*session_node
)
809 GoogleSession
*session
= NULL
;
812 PurpleXmlNode
*desc_node
;
816 if (type
!= JABBER_IQ_SET
)
819 id
.id
= (gchar
*)purple_xmlnode_get_attrib(session_node
, "id");
823 id
.initiator
= (gchar
*)purple_xmlnode_get_attrib(session_node
, "initiator");
827 iter
= purple_media_manager_get_media_by_account(
828 purple_media_manager_get(),
829 purple_connection_get_account(js
->gc
));
830 for (; iter
; iter
= g_list_delete_link(iter
, iter
)) {
831 GoogleSession
*gsession
=
832 purple_media_get_protocol_data(iter
->data
);
833 if (google_session_id_equal(&(gsession
->id
), &id
)) {
843 google_session_parse_iq(js
, session
, session_node
, iq_id
);
847 /* If the session doesn't exist, this has to be an initiate message */
848 if (strcmp(purple_xmlnode_get_attrib(session_node
, "type"), "initiate"))
850 desc_node
= purple_xmlnode_get_child(session_node
, "description");
853 session
= g_new0(GoogleSession
, 1);
854 session
->id
.id
= g_strdup(id
.id
);
855 session
->id
.initiator
= g_strdup(id
.initiator
);
856 session
->state
= UNINIT
;
858 session
->remote_jid
= g_strdup(session
->id
.initiator
);
859 session
->session_data
= g_new0(GoogleAVSessionData
, 1);
861 google_session_handle_initiate(js
, session
, session_node
, iq_id
);