Fix crashes and memory leaks when receiving malformed voice
[pidgin-git.git] / libpurple / protocols / jabber / jingle / session.c
blob5d612aa476589a2567c5b3a9f228ac5d14a20454
1 /**
2 * @file session.c
4 * purple
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
8 * source distribution.
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
25 #include "internal.h"
27 #include "content.h"
28 #include "debug.h"
29 #include "session.h"
30 #include "jingle.h"
32 #include <string.h>
34 struct _JingleSessionPrivate
36 gchar *sid;
37 JabberStream *js;
38 gchar *remote_jid;
39 gchar *local_jid;
40 gboolean is_initiator;
41 gboolean state;
42 GList *contents;
43 GList *pending_contents;
46 #define JINGLE_SESSION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), JINGLE_TYPE_SESSION, JingleSessionPrivate))
48 static void jingle_session_class_init (JingleSessionClass *klass);
49 static void jingle_session_init (JingleSession *session);
50 static void jingle_session_finalize (GObject *object);
51 static void jingle_session_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
52 static void jingle_session_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
54 static GObjectClass *parent_class = NULL;
56 enum {
57 PROP_0,
58 PROP_SID,
59 PROP_JS,
60 PROP_REMOTE_JID,
61 PROP_LOCAL_JID,
62 PROP_IS_INITIATOR,
63 PROP_STATE,
64 PROP_CONTENTS,
65 PROP_PENDING_CONTENTS,
68 GType
69 jingle_session_get_type()
71 static GType type = 0;
73 if (type == 0) {
74 static const GTypeInfo info = {
75 sizeof(JingleSessionClass),
76 NULL,
77 NULL,
78 (GClassInitFunc) jingle_session_class_init,
79 NULL,
80 NULL,
81 sizeof(JingleSession),
83 (GInstanceInitFunc) jingle_session_init,
84 NULL
86 type = g_type_register_static(G_TYPE_OBJECT, "JingleSession", &info, 0);
88 return type;
91 static void
92 jingle_session_class_init (JingleSessionClass *klass)
94 GObjectClass *gobject_class = (GObjectClass*)klass;
95 parent_class = g_type_class_peek_parent(klass);
97 gobject_class->finalize = jingle_session_finalize;
98 gobject_class->set_property = jingle_session_set_property;
99 gobject_class->get_property = jingle_session_get_property;
101 g_object_class_install_property(gobject_class, PROP_SID,
102 g_param_spec_string("sid",
103 "Session ID",
104 "The unique session ID of the Jingle Session.",
105 NULL,
106 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
108 g_object_class_install_property(gobject_class, PROP_JS,
109 g_param_spec_pointer("js",
110 "JabberStream",
111 "The Jabber stream associated with this session.",
112 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
114 g_object_class_install_property(gobject_class, PROP_REMOTE_JID,
115 g_param_spec_string("remote-jid",
116 "Remote JID",
117 "The JID of the remote participant.",
118 NULL,
119 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
121 g_object_class_install_property(gobject_class, PROP_LOCAL_JID,
122 g_param_spec_string("local-jid",
123 "Local JID",
124 "The JID of the local participant.",
125 NULL,
126 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
128 g_object_class_install_property(gobject_class, PROP_IS_INITIATOR,
129 g_param_spec_boolean("is-initiator",
130 "Is Initiator",
131 "Whether or not the local JID is the initiator of the session.",
132 FALSE,
133 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
135 g_object_class_install_property(gobject_class, PROP_STATE,
136 g_param_spec_boolean("state",
137 "State",
138 "The state of the session (PENDING=FALSE, ACTIVE=TRUE).",
139 FALSE,
140 G_PARAM_READABLE));
142 g_object_class_install_property(gobject_class, PROP_CONTENTS,
143 g_param_spec_pointer("contents",
144 "Contents",
145 "The active contents contained within this session",
146 G_PARAM_READABLE));
148 g_object_class_install_property(gobject_class, PROP_PENDING_CONTENTS,
149 g_param_spec_pointer("pending-contents",
150 "Pending contents",
151 "The pending contents contained within this session",
152 G_PARAM_READABLE));
154 g_type_class_add_private(klass, sizeof(JingleSessionPrivate));
157 static void
158 jingle_session_init (JingleSession *session)
160 session->priv = JINGLE_SESSION_GET_PRIVATE(session);
161 memset(session->priv, 0, sizeof(*session->priv));
164 static void
165 jingle_session_finalize (GObject *session)
167 JingleSessionPrivate *priv = JINGLE_SESSION_GET_PRIVATE(session);
168 purple_debug_info("jingle","jingle_session_finalize\n");
170 g_hash_table_remove(priv->js->sessions, priv->sid);
172 g_free(priv->sid);
173 g_free(priv->remote_jid);
174 g_free(priv->local_jid);
176 for (; priv->contents; priv->contents =
177 g_list_delete_link(priv->contents, priv->contents)) {
178 g_object_unref(priv->contents->data);
180 for (; priv->pending_contents; priv->pending_contents =
181 g_list_delete_link(priv->pending_contents, priv->pending_contents)) {
182 g_object_unref(priv->pending_contents->data);
185 parent_class->finalize(session);
188 static void
189 jingle_session_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
191 JingleSession *session;
193 g_return_if_fail(object != NULL);
194 g_return_if_fail(JINGLE_IS_SESSION(object));
196 session = JINGLE_SESSION(object);
198 switch (prop_id) {
199 case PROP_SID:
200 g_free(session->priv->sid);
201 session->priv->sid = g_value_dup_string(value);
202 break;
203 case PROP_JS:
204 session->priv->js = g_value_get_pointer(value);
205 break;
206 case PROP_REMOTE_JID:
207 g_free(session->priv->remote_jid);
208 session->priv->remote_jid = g_value_dup_string(value);
209 break;
210 case PROP_LOCAL_JID:
211 g_free(session->priv->local_jid);
212 session->priv->local_jid = g_value_dup_string(value);
213 break;
214 case PROP_IS_INITIATOR:
215 session->priv->is_initiator = g_value_get_boolean(value);
216 break;
217 case PROP_STATE:
218 session->priv->state = g_value_get_boolean(value);
219 break;
220 case PROP_CONTENTS:
221 session->priv->contents = g_value_get_pointer(value);
222 break;
223 case PROP_PENDING_CONTENTS:
224 session->priv->pending_contents = g_value_get_pointer(value);
225 break;
226 default:
227 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
228 break;
232 static void
233 jingle_session_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
235 JingleSession *session;
237 g_return_if_fail(object != NULL);
238 g_return_if_fail(JINGLE_IS_SESSION(object));
240 session = JINGLE_SESSION(object);
242 switch (prop_id) {
243 case PROP_SID:
244 g_value_set_string(value, session->priv->sid);
245 break;
246 case PROP_JS:
247 g_value_set_pointer(value, session->priv->js);
248 break;
249 case PROP_REMOTE_JID:
250 g_value_set_string(value, session->priv->remote_jid);
251 break;
252 case PROP_LOCAL_JID:
253 g_value_set_string(value, session->priv->local_jid);
254 break;
255 case PROP_IS_INITIATOR:
256 g_value_set_boolean(value, session->priv->is_initiator);
257 break;
258 case PROP_STATE:
259 g_value_set_boolean(value, session->priv->state);
260 break;
261 case PROP_CONTENTS:
262 g_value_set_pointer(value, session->priv->contents);
263 break;
264 case PROP_PENDING_CONTENTS:
265 g_value_set_pointer(value, session->priv->pending_contents);
266 break;
267 default:
268 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
269 break;
274 JingleSession *
275 jingle_session_create(JabberStream *js, const gchar *sid,
276 const gchar *local_jid, const gchar *remote_jid,
277 gboolean is_initiator)
279 JingleSession *session = g_object_new(jingle_session_get_type(),
280 "js", js,
281 "sid", sid,
282 "local-jid", local_jid,
283 "remote-jid", remote_jid,
284 "is_initiator", is_initiator,
285 NULL);
287 /* insert it into the hash table */
288 if (!js->sessions) {
289 purple_debug_info("jingle",
290 "Creating hash table for sessions\n");
291 js->sessions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
293 purple_debug_info("jingle",
294 "inserting session with key: %s into table\n", sid);
295 g_hash_table_insert(js->sessions, g_strdup(sid), session);
297 return session;
300 JabberStream *
301 jingle_session_get_js(JingleSession *session)
303 JabberStream *js;
304 g_object_get(session, "js", &js, NULL);
305 return js;
308 gchar *
309 jingle_session_get_sid(JingleSession *session)
311 gchar *sid;
312 g_object_get(session, "sid", &sid, NULL);
313 return sid;
316 gchar *
317 jingle_session_get_local_jid(JingleSession *session)
319 gchar *local_jid;
320 g_object_get(session, "local-jid", &local_jid, NULL);
321 return local_jid;
324 gchar *
325 jingle_session_get_remote_jid(JingleSession *session)
327 gchar *remote_jid;
328 g_object_get(session, "remote-jid", &remote_jid, NULL);
329 return remote_jid;
332 gboolean
333 jingle_session_is_initiator(JingleSession *session)
335 gboolean is_initiator;
336 g_object_get(session, "is-initiator", &is_initiator, NULL);
337 return is_initiator;
340 gboolean
341 jingle_session_get_state(JingleSession *session)
343 gboolean state;
344 g_object_get(session, "state", &state, NULL);
345 return state;
348 GList *
349 jingle_session_get_contents(JingleSession *session)
351 GList *contents;
352 g_object_get(session, "contents", &contents, NULL);
353 return contents;
356 GList *
357 jingle_session_get_pending_contents(JingleSession *session)
359 GList *pending_contents;
360 g_object_get(session, "pending-contents", &pending_contents, NULL);
361 return pending_contents;
364 JingleSession *
365 jingle_session_find_by_sid(JabberStream *js, const gchar *sid)
367 JingleSession *session = NULL;
369 if (js->sessions)
370 session = g_hash_table_lookup(js->sessions, sid);
372 purple_debug_info("jingle", "find_by_id %s\n", sid);
373 purple_debug_info("jingle", "lookup: %p\n", session);
375 return session;
378 static gboolean find_by_jid_ghr(gpointer key,
379 gpointer value, gpointer user_data)
381 JingleSession *session = (JingleSession *)value;
382 const gchar *jid = user_data;
383 gboolean use_bare = strchr(jid, '/') == NULL;
384 gchar *remote_jid = jingle_session_get_remote_jid(session);
385 gchar *cmp_jid = use_bare ? jabber_get_bare_jid(remote_jid)
386 : g_strdup(remote_jid);
387 g_free(remote_jid);
388 if (g_str_equal(jid, cmp_jid)) {
389 g_free(cmp_jid);
390 return TRUE;
392 g_free(cmp_jid);
394 return FALSE;
397 JingleSession *
398 jingle_session_find_by_jid(JabberStream *js, const gchar *jid)
400 return js->sessions != NULL ?
401 g_hash_table_find(js->sessions,
402 find_by_jid_ghr, (gpointer)jid) : NULL;
405 static xmlnode *
406 jingle_add_jingle_packet(JingleSession *session,
407 JabberIq *iq, JingleActionType action)
409 xmlnode *jingle = iq ?
410 xmlnode_new_child(iq->node, "jingle") :
411 xmlnode_new("jingle");
412 gchar *local_jid = jingle_session_get_local_jid(session);
413 gchar *remote_jid = jingle_session_get_remote_jid(session);
414 gchar *sid = jingle_session_get_sid(session);
416 xmlnode_set_namespace(jingle, JINGLE);
417 xmlnode_set_attrib(jingle, "action", jingle_get_action_name(action));
419 if (jingle_session_is_initiator(session)) {
420 xmlnode_set_attrib(jingle, "initiator", local_jid);
421 xmlnode_set_attrib(jingle, "responder", remote_jid);
422 } else {
423 xmlnode_set_attrib(jingle, "initiator", remote_jid);
424 xmlnode_set_attrib(jingle, "responder", local_jid);
427 xmlnode_set_attrib(jingle, "sid", sid);
429 g_free(local_jid);
430 g_free(remote_jid);
431 g_free(sid);
433 return jingle;
436 JabberIq *
437 jingle_session_create_ack(JingleSession *session, const xmlnode *jingle)
439 JabberIq *result = jabber_iq_new(
440 jingle_session_get_js(session),
441 JABBER_IQ_RESULT);
442 xmlnode *packet = xmlnode_get_parent(jingle);
443 jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id"));
444 xmlnode_set_attrib(result->node, "from", xmlnode_get_attrib(packet, "to"));
445 xmlnode_set_attrib(result->node, "to", xmlnode_get_attrib(packet, "from"));
446 return result;
449 static JabberIq *
450 jingle_create_iq(JingleSession *session)
452 JabberStream *js = jingle_session_get_js(session);
453 JabberIq *result = jabber_iq_new(js, JABBER_IQ_SET);
454 gchar *from = jingle_session_get_local_jid(session);
455 gchar *to = jingle_session_get_remote_jid(session);
457 xmlnode_set_attrib(result->node, "from", from);
458 xmlnode_set_attrib(result->node, "to", to);
460 g_free(from);
461 g_free(to);
462 return result;
465 xmlnode *
466 jingle_session_to_xml(JingleSession *session, xmlnode *jingle, JingleActionType action)
468 if (action != JINGLE_SESSION_INFO && action != JINGLE_SESSION_TERMINATE) {
469 GList *iter;
470 if (action == JINGLE_CONTENT_ACCEPT
471 || action == JINGLE_CONTENT_ADD
472 || action == JINGLE_CONTENT_REMOVE)
473 iter = jingle_session_get_pending_contents(session);
474 else
475 iter = jingle_session_get_contents(session);
477 for (; iter; iter = g_list_next(iter)) {
478 jingle_content_to_xml(iter->data, jingle, action);
481 return jingle;
484 JabberIq *
485 jingle_session_to_packet(JingleSession *session, JingleActionType action)
487 JabberIq *iq = jingle_create_iq(session);
488 xmlnode *jingle = jingle_add_jingle_packet(session, iq, action);
489 jingle_session_to_xml(session, jingle, action);
490 return iq;
493 void jingle_session_handle_action(JingleSession *session, xmlnode *jingle, JingleActionType action)
495 GList *iter;
496 if (action == JINGLE_CONTENT_ADD || action == JINGLE_CONTENT_REMOVE)
497 iter = jingle_session_get_pending_contents(session);
498 else
499 iter = jingle_session_get_contents(session);
501 for (; iter; iter = g_list_next(iter)) {
502 jingle_content_handle_action(iter->data, jingle, action);
506 JingleContent *
507 jingle_session_find_content(JingleSession *session, const gchar *name, const gchar *creator)
509 GList *iter;
511 if (name == NULL)
512 return NULL;
514 iter = session->priv->contents;
515 for (; iter; iter = g_list_next(iter)) {
516 JingleContent *content = iter->data;
517 gchar *cname = jingle_content_get_name(content);
518 gboolean result = g_str_equal(name, cname);
519 g_free(cname);
521 if (creator != NULL) {
522 gchar *ccreator = jingle_content_get_creator(content);
523 result = (result && !strcmp(creator, ccreator));
524 g_free(ccreator);
527 if (result == TRUE)
528 return content;
530 return NULL;
533 JingleContent *
534 jingle_session_find_pending_content(JingleSession *session, const gchar *name, const gchar *creator)
536 GList *iter;
538 if (name == NULL)
539 return NULL;
541 iter = session->priv->pending_contents;
542 for (; iter; iter = g_list_next(iter)) {
543 JingleContent *content = iter->data;
544 gchar *cname = jingle_content_get_name(content);
545 gboolean result = g_str_equal(name, cname);
546 g_free(cname);
548 if (creator != NULL) {
549 gchar *ccreator = jingle_content_get_creator(content);
550 result = (result && !strcmp(creator, ccreator));
551 g_free(ccreator);
554 if (result == TRUE)
555 return content;
557 return NULL;
560 void
561 jingle_session_add_content(JingleSession *session, JingleContent* content)
563 session->priv->contents =
564 g_list_append(session->priv->contents, content);
565 jingle_content_set_session(content, session);
568 void
569 jingle_session_remove_content(JingleSession *session, const gchar *name, const gchar *creator)
571 JingleContent *content =
572 jingle_session_find_content(session, name, creator);
574 if (content) {
575 session->priv->contents =
576 g_list_remove(session->priv->contents, content);
577 g_object_unref(content);
581 void
582 jingle_session_add_pending_content(JingleSession *session, JingleContent* content)
584 session->priv->pending_contents =
585 g_list_append(session->priv->pending_contents, content);
586 jingle_content_set_session(content, session);
589 void
590 jingle_session_remove_pending_content(JingleSession *session, const gchar *name, const gchar *creator)
592 JingleContent *content = jingle_session_find_pending_content(session, name, creator);
594 if (content) {
595 session->priv->pending_contents =
596 g_list_remove(session->priv->pending_contents, content);
597 g_object_unref(content);
601 void
602 jingle_session_accept_content(JingleSession *session, const gchar *name, const gchar *creator)
604 JingleContent *content = jingle_session_find_pending_content(session, name, creator);
606 if (content) {
607 g_object_ref(content);
608 jingle_session_remove_pending_content(session, name, creator);
609 jingle_session_add_content(session, content);
613 void
614 jingle_session_accept_session(JingleSession *session)
616 session->priv->state = TRUE;
619 JabberIq *
620 jingle_session_terminate_packet(JingleSession *session, const gchar *reason)
622 JabberIq *iq = jingle_session_to_packet(session,
623 JINGLE_SESSION_TERMINATE);
624 xmlnode *jingle = xmlnode_get_child(iq->node, "jingle");
626 if (reason != NULL) {
627 xmlnode *reason_node;
628 reason_node = xmlnode_new_child(jingle, "reason");
629 xmlnode_new_child(reason_node, reason);
631 return iq;
634 JabberIq *
635 jingle_session_redirect_packet(JingleSession *session, const gchar *sid)
637 JabberIq *iq = jingle_session_terminate_packet(session,
638 "alternative-session");
639 xmlnode *alt_session;
641 if (sid == NULL)
642 return iq;
644 alt_session = xmlnode_get_child(iq->node,
645 "jingle/reason/alternative-session");
647 if (alt_session != NULL) {
648 xmlnode *sid_node = xmlnode_new_child(alt_session, "sid");
649 xmlnode_insert_data(sid_node, sid, -1);
651 return iq;