Use g_list_free_full instead of manual iterations
[pidgin-git.git] / libpurple / protocols / jabber / jingle / session.c
blob893a8314e7b8719adcc9258f26fc035a91ab3509
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"
26 #include "glibcompat.h"
28 #include "content.h"
29 #include "debug.h"
30 #include "session.h"
31 #include "jingle.h"
33 #include <string.h>
35 struct _JingleSession
37 GObject parent;
40 typedef struct
42 gchar *sid;
43 JabberStream *js;
44 gchar *remote_jid;
45 gchar *local_jid;
46 gboolean is_initiator;
47 gboolean state;
48 GList *contents;
49 GList *pending_contents;
50 } JingleSessionPrivate;
52 enum {
53 PROP_0,
54 PROP_SID,
55 PROP_JS,
56 PROP_REMOTE_JID,
57 PROP_LOCAL_JID,
58 PROP_IS_INITIATOR,
59 PROP_STATE,
60 PROP_CONTENTS,
61 PROP_PENDING_CONTENTS,
62 PROP_LAST
65 static GParamSpec *properties[PROP_LAST];
67 G_DEFINE_DYNAMIC_TYPE_EXTENDED(
68 JingleSession,
69 jingle_session,
70 G_TYPE_OBJECT,
72 G_ADD_PRIVATE_DYNAMIC(JingleSession)
75 /******************************************************************************
76 * Helpers
77 *****************************************************************************/
78 static gboolean find_by_jid_ghr(gpointer key,
79 gpointer value, gpointer user_data)
81 JingleSession *session = (JingleSession *)value;
82 const gchar *jid = user_data;
83 gboolean use_bare = strchr(jid, '/') == NULL;
84 gchar *remote_jid = jingle_session_get_remote_jid(session);
85 gchar *cmp_jid = use_bare ? jabber_get_bare_jid(remote_jid)
86 : g_strdup(remote_jid);
87 g_free(remote_jid);
88 if (purple_strequal(jid, cmp_jid)) {
89 g_free(cmp_jid);
90 return TRUE;
92 g_free(cmp_jid);
94 return FALSE;
97 static PurpleXmlNode *
98 jingle_add_jingle_packet(JingleSession *session,
99 JabberIq *iq, JingleActionType action)
101 PurpleXmlNode *jingle = iq ?
102 purple_xmlnode_new_child(iq->node, "jingle") :
103 purple_xmlnode_new("jingle");
104 gchar *local_jid = jingle_session_get_local_jid(session);
105 gchar *remote_jid = jingle_session_get_remote_jid(session);
106 gchar *sid = jingle_session_get_sid(session);
108 purple_xmlnode_set_namespace(jingle, JINGLE);
109 purple_xmlnode_set_attrib(jingle, "action", jingle_get_action_name(action));
111 if (jingle_session_is_initiator(session)) {
112 purple_xmlnode_set_attrib(jingle, "initiator", local_jid);
113 purple_xmlnode_set_attrib(jingle, "responder", remote_jid);
114 } else {
115 purple_xmlnode_set_attrib(jingle, "initiator", remote_jid);
116 purple_xmlnode_set_attrib(jingle, "responder", local_jid);
119 purple_xmlnode_set_attrib(jingle, "sid", sid);
121 g_free(local_jid);
122 g_free(remote_jid);
123 g_free(sid);
125 return jingle;
128 static JabberIq *
129 jingle_create_iq(JingleSession *session)
131 JabberStream *js = jingle_session_get_js(session);
132 JabberIq *result = jabber_iq_new(js, JABBER_IQ_SET);
133 gchar *from = jingle_session_get_local_jid(session);
134 gchar *to = jingle_session_get_remote_jid(session);
136 purple_xmlnode_set_attrib(result->node, "from", from);
137 purple_xmlnode_set_attrib(result->node, "to", to);
139 g_free(from);
140 g_free(to);
141 return result;
144 /******************************************************************************
145 * GObject Implementation
146 *****************************************************************************/
147 static void
148 jingle_session_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
150 JingleSession *session = JINGLE_SESSION(object);
151 JingleSessionPrivate *priv = jingle_session_get_instance_private(session);
153 switch (prop_id) {
154 case PROP_SID:
155 g_free(priv->sid);
156 priv->sid = g_value_dup_string(value);
157 break;
158 case PROP_JS:
159 priv->js = g_value_get_pointer(value);
160 break;
161 case PROP_REMOTE_JID:
162 g_free(priv->remote_jid);
163 priv->remote_jid = g_value_dup_string(value);
164 break;
165 case PROP_LOCAL_JID:
166 g_free(priv->local_jid);
167 priv->local_jid = g_value_dup_string(value);
168 break;
169 case PROP_IS_INITIATOR:
170 priv->is_initiator = g_value_get_boolean(value);
171 break;
172 case PROP_STATE:
173 priv->state = g_value_get_boolean(value);
174 break;
175 case PROP_CONTENTS:
176 priv->contents = g_value_get_pointer(value);
177 break;
178 case PROP_PENDING_CONTENTS:
179 priv->pending_contents = g_value_get_pointer(value);
180 break;
181 default:
182 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
183 break;
187 static void
188 jingle_session_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
190 JingleSession *session = JINGLE_SESSION(object);
191 JingleSessionPrivate *priv = jingle_session_get_instance_private(session);
193 switch (prop_id) {
194 case PROP_SID:
195 g_value_set_string(value, priv->sid);
196 break;
197 case PROP_JS:
198 g_value_set_pointer(value, priv->js);
199 break;
200 case PROP_REMOTE_JID:
201 g_value_set_string(value, priv->remote_jid);
202 break;
203 case PROP_LOCAL_JID:
204 g_value_set_string(value, priv->local_jid);
205 break;
206 case PROP_IS_INITIATOR:
207 g_value_set_boolean(value, priv->is_initiator);
208 break;
209 case PROP_STATE:
210 g_value_set_boolean(value, priv->state);
211 break;
212 case PROP_CONTENTS:
213 g_value_set_pointer(value, priv->contents);
214 break;
215 case PROP_PENDING_CONTENTS:
216 g_value_set_pointer(value, priv->pending_contents);
217 break;
218 default:
219 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
220 break;
224 static void
225 jingle_session_init (JingleSession *session)
229 static void
230 jingle_session_finalize (GObject *session)
232 JingleSessionPrivate *priv = jingle_session_get_instance_private(JINGLE_SESSION(session));
233 purple_debug_info("jingle","jingle_session_finalize\n");
235 g_hash_table_remove(priv->js->sessions, priv->sid);
237 g_free(priv->sid);
238 g_free(priv->remote_jid);
239 g_free(priv->local_jid);
241 g_list_free_full(priv->contents, g_object_unref);
242 g_list_free_full(priv->pending_contents, g_object_unref);
244 G_OBJECT_CLASS(jingle_session_parent_class)->finalize(session);
247 static void
248 jingle_session_class_finalize (JingleSessionClass *klass)
252 static void
253 jingle_session_class_init (JingleSessionClass *klass)
255 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
257 obj_class->finalize = jingle_session_finalize;
258 obj_class->set_property = jingle_session_set_property;
259 obj_class->get_property = jingle_session_get_property;
261 properties[PROP_SID] = g_param_spec_string("sid",
262 "Session ID",
263 "The unique session ID of the Jingle Session.",
264 NULL,
265 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
267 properties[PROP_JS] = g_param_spec_pointer("js",
268 "JabberStream",
269 "The Jabber stream associated with this session.",
270 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
272 properties[PROP_REMOTE_JID] = g_param_spec_string("remote-jid",
273 "Remote JID",
274 "The JID of the remote participant.",
275 NULL,
276 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
278 properties[PROP_LOCAL_JID] = g_param_spec_string("local-jid",
279 "Local JID",
280 "The JID of the local participant.",
281 NULL,
282 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
284 properties[PROP_IS_INITIATOR] = g_param_spec_boolean("is-initiator",
285 "Is Initiator",
286 "Whether or not the local JID is the initiator of the session.",
287 FALSE,
288 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
290 properties[PROP_STATE] = g_param_spec_boolean("state",
291 "State",
292 "The state of the session (PENDING=FALSE, ACTIVE=TRUE).",
293 FALSE,
294 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
296 properties[PROP_CONTENTS] = g_param_spec_pointer("contents",
297 "Contents",
298 "The active contents contained within this session",
299 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
301 properties[PROP_PENDING_CONTENTS] = g_param_spec_pointer("pending-contents",
302 "Pending contents",
303 "The pending contents contained within this session",
304 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
306 g_object_class_install_properties(obj_class, PROP_LAST, properties);
309 /******************************************************************************
310 * Public API
311 *****************************************************************************/
312 void
313 jingle_session_register(PurplePlugin *plugin) {
314 jingle_session_register_type(G_TYPE_MODULE(plugin));
317 JingleSession *
318 jingle_session_create(JabberStream *js, const gchar *sid,
319 const gchar *local_jid, const gchar *remote_jid,
320 gboolean is_initiator)
322 JingleSession *session = g_object_new(jingle_session_get_type(),
323 "js", js,
324 "sid", sid,
325 "local-jid", local_jid,
326 "remote-jid", remote_jid,
327 "is_initiator", is_initiator,
328 NULL);
330 /* insert it into the hash table */
331 if (!js->sessions) {
332 purple_debug_info("jingle",
333 "Creating hash table for sessions\n");
334 js->sessions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
336 purple_debug_info("jingle",
337 "inserting session with key: %s into table\n", sid);
338 g_hash_table_insert(js->sessions, g_strdup(sid), session);
340 return session;
343 JabberStream *
344 jingle_session_get_js(JingleSession *session)
346 JabberStream *js;
347 g_object_get(session, "js", &js, NULL);
348 return js;
351 gchar *
352 jingle_session_get_sid(JingleSession *session)
354 gchar *sid;
355 g_object_get(session, "sid", &sid, NULL);
356 return sid;
359 gchar *
360 jingle_session_get_local_jid(JingleSession *session)
362 gchar *local_jid;
363 g_object_get(session, "local-jid", &local_jid, NULL);
364 return local_jid;
367 gchar *
368 jingle_session_get_remote_jid(JingleSession *session)
370 gchar *remote_jid;
371 g_object_get(session, "remote-jid", &remote_jid, NULL);
372 return remote_jid;
375 gboolean
376 jingle_session_is_initiator(JingleSession *session)
378 gboolean is_initiator;
379 g_object_get(session, "is-initiator", &is_initiator, NULL);
380 return is_initiator;
383 gboolean
384 jingle_session_get_state(JingleSession *session)
386 gboolean state;
387 g_object_get(session, "state", &state, NULL);
388 return state;
391 GList *
392 jingle_session_get_contents(JingleSession *session)
394 GList *contents;
395 g_object_get(session, "contents", &contents, NULL);
396 return contents;
399 GList *
400 jingle_session_get_pending_contents(JingleSession *session)
402 GList *pending_contents;
403 g_object_get(session, "pending-contents", &pending_contents, NULL);
404 return pending_contents;
407 JingleSession *
408 jingle_session_find_by_sid(JabberStream *js, const gchar *sid)
410 JingleSession *session = NULL;
412 if (js->sessions)
413 session = g_hash_table_lookup(js->sessions, sid);
415 purple_debug_info("jingle", "find_by_id %s\n", sid);
416 purple_debug_info("jingle", "lookup: %p\n", session);
418 return session;
421 JingleSession *
422 jingle_session_find_by_jid(JabberStream *js, const gchar *jid)
424 return js->sessions != NULL ?
425 g_hash_table_find(js->sessions,
426 find_by_jid_ghr, (gpointer)jid) : NULL;
429 JabberIq *
430 jingle_session_create_ack(JingleSession *session, const PurpleXmlNode *jingle)
432 JabberIq *result = jabber_iq_new(
433 jingle_session_get_js(session),
434 JABBER_IQ_RESULT);
435 PurpleXmlNode *packet = purple_xmlnode_get_parent(jingle);
436 jabber_iq_set_id(result, purple_xmlnode_get_attrib(packet, "id"));
437 purple_xmlnode_set_attrib(result->node, "from", purple_xmlnode_get_attrib(packet, "to"));
438 purple_xmlnode_set_attrib(result->node, "to", purple_xmlnode_get_attrib(packet, "from"));
439 return result;
442 PurpleXmlNode *
443 jingle_session_to_xml(JingleSession *session, PurpleXmlNode *jingle, JingleActionType action)
445 if (action != JINGLE_SESSION_INFO && action != JINGLE_SESSION_TERMINATE) {
446 GList *iter;
447 if (action == JINGLE_CONTENT_ACCEPT
448 || action == JINGLE_CONTENT_ADD
449 || action == JINGLE_CONTENT_REMOVE)
450 iter = jingle_session_get_pending_contents(session);
451 else
452 iter = jingle_session_get_contents(session);
454 for (; iter; iter = g_list_next(iter)) {
455 jingle_content_to_xml(iter->data, jingle, action);
458 return jingle;
461 JabberIq *
462 jingle_session_to_packet(JingleSession *session, JingleActionType action)
464 JabberIq *iq = jingle_create_iq(session);
465 PurpleXmlNode *jingle = jingle_add_jingle_packet(session, iq, action);
466 jingle_session_to_xml(session, jingle, action);
467 return iq;
470 void jingle_session_handle_action(JingleSession *session, PurpleXmlNode *jingle, JingleActionType action)
472 GList *iter;
473 if (action == JINGLE_CONTENT_ADD || action == JINGLE_CONTENT_REMOVE)
474 iter = jingle_session_get_pending_contents(session);
475 else
476 iter = jingle_session_get_contents(session);
478 for (; iter; iter = g_list_next(iter)) {
479 jingle_content_handle_action(iter->data, jingle, action);
483 JingleContent *
484 jingle_session_find_content(JingleSession *session, const gchar *name, const gchar *creator)
486 JingleSessionPrivate *priv = NULL;
487 GList *iter;
489 g_return_val_if_fail(JINGLE_IS_SESSION(session), NULL);
491 if (name == NULL)
492 return NULL;
494 priv = jingle_session_get_instance_private(session);
496 iter = priv->contents;
497 for (; iter; iter = g_list_next(iter)) {
498 JingleContent *content = iter->data;
499 gchar *cname = jingle_content_get_name(content);
500 gboolean result = purple_strequal(name, cname);
501 g_free(cname);
503 if (creator != NULL) {
504 gchar *ccreator = jingle_content_get_creator(content);
505 result = (result && purple_strequal(creator, ccreator));
506 g_free(ccreator);
509 if (result == TRUE)
510 return content;
512 return NULL;
515 JingleContent *
516 jingle_session_find_pending_content(JingleSession *session, const gchar *name, const gchar *creator)
518 JingleSessionPrivate *priv = NULL;
519 GList *iter;
521 g_return_val_if_fail(JINGLE_IS_SESSION(session), NULL);
523 if (name == NULL)
524 return NULL;
526 priv = jingle_session_get_instance_private(session);
528 iter = priv->pending_contents;
529 for (; iter; iter = g_list_next(iter)) {
530 JingleContent *content = iter->data;
531 gchar *cname = jingle_content_get_name(content);
532 gboolean result = purple_strequal(name, cname);
533 g_free(cname);
535 if (creator != NULL) {
536 gchar *ccreator = jingle_content_get_creator(content);
537 result = (result && purple_strequal(creator, ccreator));
538 g_free(ccreator);
541 if (result == TRUE)
542 return content;
544 return NULL;
547 void
548 jingle_session_add_content(JingleSession *session, JingleContent* content)
550 JingleSessionPrivate *priv = NULL;
552 g_return_if_fail(JINGLE_IS_SESSION(session));
554 priv = jingle_session_get_instance_private(session);
556 priv->contents = g_list_append(priv->contents, content);
557 jingle_content_set_session(content, session);
559 g_object_notify_by_pspec(G_OBJECT(session), properties[PROP_CONTENTS]);
562 void
563 jingle_session_remove_content(JingleSession *session, const gchar *name, const gchar *creator)
565 JingleSessionPrivate *priv = NULL;
566 JingleContent *content = NULL;
568 g_return_if_fail(JINGLE_IS_SESSION(session));
570 priv = jingle_session_get_instance_private(session);
571 content = jingle_session_find_content(session, name, creator);
573 if (content) {
574 priv->contents = g_list_remove(priv->contents, content);
575 g_object_unref(content);
577 g_object_notify_by_pspec(G_OBJECT(session), properties[PROP_CONTENTS]);
581 void
582 jingle_session_add_pending_content(JingleSession *session, JingleContent* content)
584 JingleSessionPrivate *priv = NULL;
586 g_return_if_fail(JINGLE_IS_SESSION(session));
588 priv = jingle_session_get_instance_private(session);
590 priv->pending_contents = g_list_append(priv->pending_contents, content);
591 jingle_content_set_session(content, session);
593 g_object_notify_by_pspec(G_OBJECT(session), properties[PROP_PENDING_CONTENTS]);
596 void
597 jingle_session_remove_pending_content(JingleSession *session, const gchar *name, const gchar *creator)
599 JingleSessionPrivate *priv = NULL;
600 JingleContent *content = NULL;
602 g_return_if_fail(JINGLE_IS_SESSION(session));
604 priv = jingle_session_get_instance_private(session);
605 content = jingle_session_find_pending_content(session, name, creator);
607 if (content) {
608 priv->pending_contents = g_list_remove(priv->pending_contents, content);
609 g_object_unref(content);
611 g_object_notify_by_pspec(G_OBJECT(session), properties[PROP_PENDING_CONTENTS]);
615 void
616 jingle_session_accept_content(JingleSession *session, const gchar *name, const gchar *creator)
618 JingleContent *content = NULL;
620 g_return_if_fail(JINGLE_IS_SESSION(session));
622 content = jingle_session_find_pending_content(session, name, creator);
623 if (content) {
624 g_object_ref(content);
625 jingle_session_remove_pending_content(session, name, creator);
626 jingle_session_add_content(session, content);
630 void
631 jingle_session_accept_session(JingleSession *session)
633 JingleSessionPrivate *priv = NULL;
635 g_return_if_fail(JINGLE_IS_SESSION(session));
637 priv = jingle_session_get_instance_private(session);
639 priv->state = TRUE;
641 g_object_notify_by_pspec(G_OBJECT(session), properties[PROP_STATE]);
644 JabberIq *
645 jingle_session_terminate_packet(JingleSession *session, const gchar *reason)
647 JabberIq *iq = NULL;
649 g_return_val_if_fail(JINGLE_IS_SESSION(session), NULL);
651 iq = jingle_session_to_packet(session, JINGLE_SESSION_TERMINATE);
653 if (reason != NULL) {
654 PurpleXmlNode *reason_node;
655 PurpleXmlNode *jingle = purple_xmlnode_get_child(iq->node, "jingle");
656 reason_node = purple_xmlnode_new_child(jingle, "reason");
657 purple_xmlnode_new_child(reason_node, reason);
660 return iq;
663 JabberIq *
664 jingle_session_redirect_packet(JingleSession *session, const gchar *sid)
666 JabberIq *iq = NULL;
667 PurpleXmlNode *alt_session;
669 g_return_val_if_fail(JINGLE_IS_SESSION(session), NULL);
671 iq = jingle_session_terminate_packet(session, "alternative-session");
673 if (sid == NULL)
674 return iq;
676 alt_session = purple_xmlnode_get_child(iq->node,
677 "jingle/reason/alternative-session");
679 if (alt_session != NULL) {
680 PurpleXmlNode *sid_node = purple_xmlnode_new_child(alt_session, "sid");
681 purple_xmlnode_insert_data(sid_node, sid, -1);
684 return iq;