4 * purple - Jabber Protocol Plugin
6 * Purple is the legal property of its developers, whose names are too numerous
7 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
27 #include "glibcompat.h"
33 #include "google/google_p2p.h"
45 jingle_get_type(const gchar
*type
)
50 if (purple_strequal(type
, JINGLE_TRANSPORT_RAWUDP
))
51 return JINGLE_TYPE_RAWUDP
;
52 else if (purple_strequal(type
, JINGLE_TRANSPORT_ICEUDP
))
53 return JINGLE_TYPE_ICEUDP
;
55 else if (purple_strequal(type
, JINGLE_APP_RTP
))
56 return JINGLE_TYPE_RTP
;
57 else if (!strcmp(type
, NS_GOOGLE_TRANSPORT_P2P
))
58 return JINGLE_TYPE_GOOGLE_P2P
;
65 jingle_handle_unknown_type(JingleSession
*session
, PurpleXmlNode
*jingle
)
71 jingle_handle_content_accept(JingleSession
*session
, PurpleXmlNode
*jingle
)
73 PurpleXmlNode
*content
= purple_xmlnode_get_child(jingle
, "content");
74 jabber_iq_send(jingle_session_create_ack(session
, jingle
));
76 for (; content
; content
= purple_xmlnode_get_next_twin(content
)) {
77 const gchar
*name
= purple_xmlnode_get_attrib(content
, "name");
78 const gchar
*creator
= purple_xmlnode_get_attrib(content
, "creator");
79 jingle_session_accept_content(session
, name
, creator
);
85 jingle_handle_content_add(JingleSession
*session
, PurpleXmlNode
*jingle
)
87 PurpleXmlNode
*content
= purple_xmlnode_get_child(jingle
, "content");
88 jabber_iq_send(jingle_session_create_ack(session
, jingle
));
90 for (; content
; content
= purple_xmlnode_get_next_twin(content
)) {
91 JingleContent
*pending_content
=
92 jingle_content_parse(content
);
93 if (pending_content
== NULL
) {
94 purple_debug_error("jingle",
95 "Error parsing \"content-add\" content.\n");
96 jabber_iq_send(jingle_session_terminate_packet(session
,
97 "unsupported-applications"));
99 jingle_session_add_pending_content(session
,
104 /* XXX: signal here */
108 jingle_handle_content_modify(JingleSession
*session
, PurpleXmlNode
*jingle
)
110 PurpleXmlNode
*content
= purple_xmlnode_get_child(jingle
, "content");
111 jabber_iq_send(jingle_session_create_ack(session
, jingle
));
113 for (; content
; content
= purple_xmlnode_get_next_twin(content
)) {
114 const gchar
*name
= purple_xmlnode_get_attrib(content
, "name");
115 const gchar
*creator
= purple_xmlnode_get_attrib(content
, "creator");
116 JingleContent
*local_content
= jingle_session_find_content(session
, name
, creator
);
118 if (local_content
!= NULL
) {
119 const gchar
*senders
= purple_xmlnode_get_attrib(content
, "senders");
120 gchar
*local_senders
= jingle_content_get_senders(local_content
);
121 if (!purple_strequal(senders
, local_senders
))
122 jingle_content_modify(local_content
, senders
);
123 g_free(local_senders
);
125 purple_debug_error("jingle", "content_modify: unknown content\n");
126 jabber_iq_send(jingle_session_terminate_packet(session
,
127 "unknown-applications"));
133 jingle_handle_content_reject(JingleSession
*session
, PurpleXmlNode
*jingle
)
135 PurpleXmlNode
*content
= purple_xmlnode_get_child(jingle
, "content");
136 jabber_iq_send(jingle_session_create_ack(session
, jingle
));
138 for (; content
; content
= purple_xmlnode_get_next_twin(content
)) {
139 const gchar
*name
= purple_xmlnode_get_attrib(content
, "name");
140 const gchar
*creator
= purple_xmlnode_get_attrib(content
, "creator");
141 jingle_session_remove_pending_content(session
, name
, creator
);
147 jingle_handle_content_remove(JingleSession
*session
, PurpleXmlNode
*jingle
)
149 PurpleXmlNode
*content
= purple_xmlnode_get_child(jingle
, "content");
151 jabber_iq_send(jingle_session_create_ack(session
, jingle
));
153 for (; content
; content
= purple_xmlnode_get_next_twin(content
)) {
154 const gchar
*name
= purple_xmlnode_get_attrib(content
, "name");
155 const gchar
*creator
= purple_xmlnode_get_attrib(content
, "creator");
156 jingle_session_remove_content(session
, name
, creator
);
161 jingle_handle_description_info(JingleSession
*session
, PurpleXmlNode
*jingle
)
163 PurpleXmlNode
*content
= purple_xmlnode_get_child(jingle
, "content");
165 jabber_iq_send(jingle_session_create_ack(session
, jingle
));
167 jingle_session_accept_session(session
);
169 for (; content
; content
= purple_xmlnode_get_next_twin(content
)) {
170 const gchar
*name
= purple_xmlnode_get_attrib(content
, "name");
171 const gchar
*creator
= purple_xmlnode_get_attrib(content
, "creator");
172 JingleContent
*parsed_content
=
173 jingle_session_find_content(session
, name
, creator
);
174 if (parsed_content
== NULL
) {
175 purple_debug_error("jingle", "Error parsing content\n");
176 jabber_iq_send(jingle_session_terminate_packet(session
,
177 "unsupported-applications"));
179 jingle_content_handle_action(parsed_content
, content
,
180 JINGLE_DESCRIPTION_INFO
);
186 jingle_handle_security_info(JingleSession
*session
, PurpleXmlNode
*jingle
)
188 jabber_iq_send(jingle_session_create_ack(session
, jingle
));
192 jingle_handle_session_accept(JingleSession
*session
, PurpleXmlNode
*jingle
)
194 PurpleXmlNode
*content
= purple_xmlnode_get_child(jingle
, "content");
196 jabber_iq_send(jingle_session_create_ack(session
, jingle
));
198 jingle_session_accept_session(session
);
200 for (; content
; content
= purple_xmlnode_get_next_twin(content
)) {
201 const gchar
*name
= purple_xmlnode_get_attrib(content
, "name");
202 const gchar
*creator
= purple_xmlnode_get_attrib(content
, "creator");
203 JingleContent
*parsed_content
=
204 jingle_session_find_content(session
, name
, creator
);
205 if (parsed_content
== NULL
) {
206 purple_debug_error("jingle", "Error parsing content\n");
207 jabber_iq_send(jingle_session_terminate_packet(session
,
208 "unsupported-applications"));
210 jingle_content_handle_action(parsed_content
, content
,
211 JINGLE_SESSION_ACCEPT
);
217 jingle_handle_session_info(JingleSession
*session
, PurpleXmlNode
*jingle
)
219 jabber_iq_send(jingle_session_create_ack(session
, jingle
));
220 /* XXX: call signal */
224 jingle_handle_session_initiate(JingleSession
*session
, PurpleXmlNode
*jingle
)
226 PurpleXmlNode
*content
= purple_xmlnode_get_child(jingle
, "content");
228 for (; content
; content
= purple_xmlnode_get_next_twin(content
)) {
229 JingleContent
*parsed_content
= jingle_content_parse(content
);
230 if (parsed_content
== NULL
) {
231 purple_debug_error("jingle", "Error parsing content\n");
232 jabber_iq_send(jingle_session_terminate_packet(session
,
233 "unsupported-applications"));
235 jingle_session_add_content(session
, parsed_content
);
236 jingle_content_handle_action(parsed_content
, content
,
237 JINGLE_SESSION_INITIATE
);
241 jabber_iq_send(jingle_session_create_ack(session
, jingle
));
245 jingle_handle_session_terminate(JingleSession
*session
, PurpleXmlNode
*jingle
)
247 jabber_iq_send(jingle_session_create_ack(session
, jingle
));
249 jingle_session_handle_action(session
, jingle
,
250 JINGLE_SESSION_TERMINATE
);
251 /* display reason? */
252 g_object_unref(session
);
256 jingle_handle_transport_accept(JingleSession
*session
, PurpleXmlNode
*jingle
)
258 PurpleXmlNode
*content
= purple_xmlnode_get_child(jingle
, "content");
260 jabber_iq_send(jingle_session_create_ack(session
, jingle
));
262 for (; content
; content
= purple_xmlnode_get_next_twin(content
)) {
263 const gchar
*name
= purple_xmlnode_get_attrib(content
, "name");
264 const gchar
*creator
= purple_xmlnode_get_attrib(content
, "creator");
265 JingleContent
*content
= jingle_session_find_content(session
, name
, creator
);
266 jingle_content_accept_transport(content
);
271 jingle_handle_transport_info(JingleSession
*session
, PurpleXmlNode
*jingle
)
273 PurpleXmlNode
*content
= purple_xmlnode_get_child(jingle
, "content");
275 jabber_iq_send(jingle_session_create_ack(session
, jingle
));
277 for (; content
; content
= purple_xmlnode_get_next_twin(content
)) {
278 const gchar
*name
= purple_xmlnode_get_attrib(content
, "name");
279 const gchar
*creator
= purple_xmlnode_get_attrib(content
, "creator");
280 JingleContent
*parsed_content
=
281 jingle_session_find_content(session
, name
, creator
);
282 if (parsed_content
== NULL
) {
283 purple_debug_error("jingle", "Error parsing content\n");
284 jabber_iq_send(jingle_session_terminate_packet(session
,
285 "unsupported-applications"));
287 jingle_content_handle_action(parsed_content
, content
,
288 JINGLE_TRANSPORT_INFO
);
294 jingle_handle_transport_reject(JingleSession
*session
, PurpleXmlNode
*jingle
)
296 PurpleXmlNode
*content
= purple_xmlnode_get_child(jingle
, "content");
298 jabber_iq_send(jingle_session_create_ack(session
, jingle
));
300 for (; content
; content
= purple_xmlnode_get_next_twin(content
)) {
301 const gchar
*name
= purple_xmlnode_get_attrib(content
, "name");
302 const gchar
*creator
= purple_xmlnode_get_attrib(content
, "creator");
303 JingleContent
*content
= jingle_session_find_content(session
, name
, creator
);
304 jingle_content_remove_pending_transport(content
);
309 jingle_handle_transport_replace(JingleSession
*session
, PurpleXmlNode
*jingle
)
311 PurpleXmlNode
*content
= purple_xmlnode_get_child(jingle
, "content");
313 jabber_iq_send(jingle_session_create_ack(session
, jingle
));
315 for (; content
; content
= purple_xmlnode_get_next_twin(content
)) {
316 const gchar
*name
= purple_xmlnode_get_attrib(content
, "name");
317 const gchar
*creator
= purple_xmlnode_get_attrib(content
, "creator");
318 PurpleXmlNode
*xmltransport
= purple_xmlnode_get_child(content
, "transport");
319 JingleTransport
*transport
= jingle_transport_parse(xmltransport
);
320 JingleContent
*content
= jingle_session_find_content(session
, name
, creator
);
322 jingle_content_set_pending_transport(content
, transport
);
328 void (*handler
)(JingleSession
*, PurpleXmlNode
*);
331 static const JingleAction jingle_actions
[] = {
332 {"unknown-type", jingle_handle_unknown_type
},
333 {"content-accept", jingle_handle_content_accept
},
334 {"content-add", jingle_handle_content_add
},
335 {"content-modify", jingle_handle_content_modify
},
336 {"content-reject", jingle_handle_content_reject
},
337 {"content-remove", jingle_handle_content_remove
},
338 {"description-info", jingle_handle_description_info
},
339 {"security-info", jingle_handle_security_info
},
340 {"session-accept", jingle_handle_session_accept
},
341 {"session-info", jingle_handle_session_info
},
342 {"session-initiate", jingle_handle_session_initiate
},
343 {"session-terminate", jingle_handle_session_terminate
},
344 {"transport-accept", jingle_handle_transport_accept
},
345 {"transport-info", jingle_handle_transport_info
},
346 {"transport-reject", jingle_handle_transport_reject
},
347 {"transport-replace", jingle_handle_transport_replace
},
351 jingle_get_action_name(JingleActionType action
)
353 return jingle_actions
[action
].name
;
357 jingle_get_action_type(const gchar
*action
)
359 static const int num_actions
=
360 sizeof(jingle_actions
)/sizeof(JingleAction
);
361 /* Start at 1 to skip the unknown-action type */
363 for (; i
< num_actions
; ++i
) {
364 if (purple_strequal(action
, jingle_actions
[i
].name
))
367 return JINGLE_UNKNOWN_TYPE
;
371 jingle_parse(JabberStream
*js
, const char *from
, JabberIqType type
,
372 const char *id
, PurpleXmlNode
*jingle
)
376 JingleActionType action_type
;
377 JingleSession
*session
;
379 if (type
!= JABBER_IQ_SET
) {
380 /* TODO: send iq error here */
384 if (!(action
= purple_xmlnode_get_attrib(jingle
, "action"))) {
385 /* TODO: send iq error here */
389 action_type
= jingle_get_action_type(action
);
391 purple_debug_info("jabber", "got Jingle package action = %s\n",
394 if (!(sid
= purple_xmlnode_get_attrib(jingle
, "sid"))) {
395 /* send iq error here */
399 if (!(session
= jingle_session_find_by_sid(js
, sid
))
400 && !purple_strequal(action
, "session-initiate")) {
401 purple_debug_error("jingle", "jabber_jingle_session_parse couldn't find session\n");
402 /* send iq error here */
406 if (action_type
== JINGLE_SESSION_INITIATE
) {
408 /* This should only happen if you start a session with yourself */
409 purple_debug_error("jingle", "Jingle session with "
410 "id={%s} already exists\n", sid
);
414 char *own_jid
= g_strdup_printf("%s@%s/%s", js
->user
->node
,
415 js
->user
->domain
, js
->user
->resource
);
416 session
= jingle_session_create(js
, sid
, own_jid
, from
, FALSE
);
421 jingle_actions
[action_type
].handler(session
, jingle
);
425 jingle_terminate_sessions(JabberStream
*js
)
428 GList
*list
= g_hash_table_get_values(js
->sessions
);
429 for (; list
; list
= g_list_delete_link(list
, list
))
430 g_object_unref(list
->data
);
436 jingle_create_relay_info(const gchar
*ip
, guint port
, const gchar
*username
,
437 const gchar
*password
, const gchar
*relay_type
, GValueArray
*relay_info
)
440 GstStructure
*turn_setup
= gst_structure_new("relay-info",
441 "ip", G_TYPE_STRING
, ip
,
442 "port", G_TYPE_UINT
, port
,
443 "username", G_TYPE_STRING
, username
,
444 "password", G_TYPE_STRING
, password
,
445 "relay-type", G_TYPE_STRING
, relay_type
,
447 purple_debug_info("jabber", "created gst_structure %p\n", turn_setup
);
449 memset(&value
, 0, sizeof(GValue
));
450 g_value_init(&value
, GST_TYPE_STRUCTURE
);
451 gst_value_set_structure(&value
, turn_setup
);
452 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
453 relay_info
= g_value_array_append(relay_info
, &value
);
454 G_GNUC_END_IGNORE_DEPRECATIONS
455 gst_structure_free(turn_setup
);
461 jingle_get_params(JabberStream
*js
, const gchar
*relay_ip
, guint relay_udp
,
462 guint relay_tcp
, guint relay_ssltcp
, const gchar
*relay_username
,
463 const gchar
*relay_password
, guint
*num
)
465 /* don't set a STUN server if one is set globally in prefs, in that case
466 this will be handled in media.c */
467 gboolean has_account_stun
= js
->stun_ip
&& !purple_network_get_stun_ip();
468 guint num_params
= has_account_stun
?
469 (relay_ip
? 3 : 2) : (relay_ip
? 1 : 0);
470 GParameter
*params
= NULL
;
473 if (num_params
> 0) {
474 params
= g_new0(GParameter
, num_params
);
476 if (has_account_stun
) {
477 purple_debug_info("jabber",
478 "setting param stun-ip for stream using Google auto-config: %s\n",
480 params
[next_index
].name
= "stun-ip";
481 g_value_init(¶ms
[next_index
].value
, G_TYPE_STRING
);
482 g_value_set_string(¶ms
[next_index
].value
, js
->stun_ip
);
483 purple_debug_info("jabber",
484 "setting param stun-port for stream using Google auto-config: %d\n",
487 params
[next_index
].name
= "stun-port";
488 g_value_init(¶ms
[next_index
].value
, G_TYPE_UINT
);
489 g_value_set_uint(¶ms
[next_index
].value
, js
->stun_port
);
494 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
495 GValueArray
*relay_info
= g_value_array_new(0);
496 G_GNUC_END_IGNORE_DEPRECATIONS
500 jingle_create_relay_info(relay_ip
, relay_udp
, relay_username
,
501 relay_password
, "udp", relay_info
);
505 jingle_create_relay_info(relay_ip
, relay_tcp
, relay_username
,
506 relay_password
, "tcp", relay_info
);
510 jingle_create_relay_info(relay_ip
, relay_ssltcp
, relay_username
,
511 relay_password
, "tls", relay_info
);
513 params
[next_index
].name
= "relay-info";
514 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
515 g_value_init(¶ms
[next_index
].value
, G_TYPE_VALUE_ARRAY
);
516 g_value_set_boxed(¶ms
[next_index
].value
, relay_info
);
517 g_value_array_free(relay_info
);
518 G_GNUC_END_IGNORE_DEPRECATIONS