rename accountopt.[ch] to purpleaccountoption.[ch]
[pidgin-git.git] / libpurple / protocols / jabber / jingle / session.c
blob2e86d39008e9dc066f49d31d7c0f47bdc6f77303
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 for (; priv->contents; priv->contents =
242 g_list_delete_link(priv->contents, priv->contents)) {
243 g_object_unref(priv->contents->data);
245 for (; priv->pending_contents; priv->pending_contents =
246 g_list_delete_link(priv->pending_contents, priv->pending_contents)) {
247 g_object_unref(priv->pending_contents->data);
250 G_OBJECT_CLASS(jingle_session_parent_class)->finalize(session);
253 static void
254 jingle_session_class_finalize (JingleSessionClass *klass)
258 static void
259 jingle_session_class_init (JingleSessionClass *klass)
261 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
263 obj_class->finalize = jingle_session_finalize;
264 obj_class->set_property = jingle_session_set_property;
265 obj_class->get_property = jingle_session_get_property;
267 properties[PROP_SID] = g_param_spec_string("sid",
268 "Session ID",
269 "The unique session ID of the Jingle Session.",
270 NULL,
271 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
273 properties[PROP_JS] = g_param_spec_pointer("js",
274 "JabberStream",
275 "The Jabber stream associated with this session.",
276 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
278 properties[PROP_REMOTE_JID] = g_param_spec_string("remote-jid",
279 "Remote JID",
280 "The JID of the remote participant.",
281 NULL,
282 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
284 properties[PROP_LOCAL_JID] = g_param_spec_string("local-jid",
285 "Local JID",
286 "The JID of the local participant.",
287 NULL,
288 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
290 properties[PROP_IS_INITIATOR] = g_param_spec_boolean("is-initiator",
291 "Is Initiator",
292 "Whether or not the local JID is the initiator of the session.",
293 FALSE,
294 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
296 properties[PROP_STATE] = g_param_spec_boolean("state",
297 "State",
298 "The state of the session (PENDING=FALSE, ACTIVE=TRUE).",
299 FALSE,
300 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
302 properties[PROP_CONTENTS] = g_param_spec_pointer("contents",
303 "Contents",
304 "The active contents contained within this session",
305 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
307 properties[PROP_PENDING_CONTENTS] = g_param_spec_pointer("pending-contents",
308 "Pending contents",
309 "The pending contents contained within this session",
310 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
312 g_object_class_install_properties(obj_class, PROP_LAST, properties);
315 /******************************************************************************
316 * Public API
317 *****************************************************************************/
318 void
319 jingle_session_register(PurplePlugin *plugin) {
320 jingle_session_register_type(G_TYPE_MODULE(plugin));
323 JingleSession *
324 jingle_session_create(JabberStream *js, const gchar *sid,
325 const gchar *local_jid, const gchar *remote_jid,
326 gboolean is_initiator)
328 JingleSession *session = g_object_new(jingle_session_get_type(),
329 "js", js,
330 "sid", sid,
331 "local-jid", local_jid,
332 "remote-jid", remote_jid,
333 "is_initiator", is_initiator,
334 NULL);
336 /* insert it into the hash table */
337 if (!js->sessions) {
338 purple_debug_info("jingle",
339 "Creating hash table for sessions\n");
340 js->sessions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
342 purple_debug_info("jingle",
343 "inserting session with key: %s into table\n", sid);
344 g_hash_table_insert(js->sessions, g_strdup(sid), session);
346 return session;
349 JabberStream *
350 jingle_session_get_js(JingleSession *session)
352 JabberStream *js;
353 g_object_get(session, "js", &js, NULL);
354 return js;
357 gchar *
358 jingle_session_get_sid(JingleSession *session)
360 gchar *sid;
361 g_object_get(session, "sid", &sid, NULL);
362 return sid;
365 gchar *
366 jingle_session_get_local_jid(JingleSession *session)
368 gchar *local_jid;
369 g_object_get(session, "local-jid", &local_jid, NULL);
370 return local_jid;
373 gchar *
374 jingle_session_get_remote_jid(JingleSession *session)
376 gchar *remote_jid;
377 g_object_get(session, "remote-jid", &remote_jid, NULL);
378 return remote_jid;
381 gboolean
382 jingle_session_is_initiator(JingleSession *session)
384 gboolean is_initiator;
385 g_object_get(session, "is-initiator", &is_initiator, NULL);
386 return is_initiator;
389 gboolean
390 jingle_session_get_state(JingleSession *session)
392 gboolean state;
393 g_object_get(session, "state", &state, NULL);
394 return state;
397 GList *
398 jingle_session_get_contents(JingleSession *session)
400 GList *contents;
401 g_object_get(session, "contents", &contents, NULL);
402 return contents;
405 GList *
406 jingle_session_get_pending_contents(JingleSession *session)
408 GList *pending_contents;
409 g_object_get(session, "pending-contents", &pending_contents, NULL);
410 return pending_contents;
413 JingleSession *
414 jingle_session_find_by_sid(JabberStream *js, const gchar *sid)
416 JingleSession *session = NULL;
418 if (js->sessions)
419 session = g_hash_table_lookup(js->sessions, sid);
421 purple_debug_info("jingle", "find_by_id %s\n", sid);
422 purple_debug_info("jingle", "lookup: %p\n", session);
424 return session;
427 JingleSession *
428 jingle_session_find_by_jid(JabberStream *js, const gchar *jid)
430 return js->sessions != NULL ?
431 g_hash_table_find(js->sessions,
432 find_by_jid_ghr, (gpointer)jid) : NULL;
435 JabberIq *
436 jingle_session_create_ack(JingleSession *session, const PurpleXmlNode *jingle)
438 JabberIq *result = jabber_iq_new(
439 jingle_session_get_js(session),
440 JABBER_IQ_RESULT);
441 PurpleXmlNode *packet = purple_xmlnode_get_parent(jingle);
442 jabber_iq_set_id(result, purple_xmlnode_get_attrib(packet, "id"));
443 purple_xmlnode_set_attrib(result->node, "from", purple_xmlnode_get_attrib(packet, "to"));
444 purple_xmlnode_set_attrib(result->node, "to", purple_xmlnode_get_attrib(packet, "from"));
445 return result;
448 PurpleXmlNode *
449 jingle_session_to_xml(JingleSession *session, PurpleXmlNode *jingle, JingleActionType action)
451 if (action != JINGLE_SESSION_INFO && action != JINGLE_SESSION_TERMINATE) {
452 GList *iter;
453 if (action == JINGLE_CONTENT_ACCEPT
454 || action == JINGLE_CONTENT_ADD
455 || action == JINGLE_CONTENT_REMOVE)
456 iter = jingle_session_get_pending_contents(session);
457 else
458 iter = jingle_session_get_contents(session);
460 for (; iter; iter = g_list_next(iter)) {
461 jingle_content_to_xml(iter->data, jingle, action);
464 return jingle;
467 JabberIq *
468 jingle_session_to_packet(JingleSession *session, JingleActionType action)
470 JabberIq *iq = jingle_create_iq(session);
471 PurpleXmlNode *jingle = jingle_add_jingle_packet(session, iq, action);
472 jingle_session_to_xml(session, jingle, action);
473 return iq;
476 void jingle_session_handle_action(JingleSession *session, PurpleXmlNode *jingle, JingleActionType action)
478 GList *iter;
479 if (action == JINGLE_CONTENT_ADD || action == JINGLE_CONTENT_REMOVE)
480 iter = jingle_session_get_pending_contents(session);
481 else
482 iter = jingle_session_get_contents(session);
484 for (; iter; iter = g_list_next(iter)) {
485 jingle_content_handle_action(iter->data, jingle, action);
489 JingleContent *
490 jingle_session_find_content(JingleSession *session, const gchar *name, const gchar *creator)
492 JingleSessionPrivate *priv = NULL;
493 GList *iter;
495 g_return_val_if_fail(JINGLE_IS_SESSION(session), NULL);
497 if (name == NULL)
498 return NULL;
500 priv = jingle_session_get_instance_private(session);
502 iter = priv->contents;
503 for (; iter; iter = g_list_next(iter)) {
504 JingleContent *content = iter->data;
505 gchar *cname = jingle_content_get_name(content);
506 gboolean result = purple_strequal(name, cname);
507 g_free(cname);
509 if (creator != NULL) {
510 gchar *ccreator = jingle_content_get_creator(content);
511 result = (result && purple_strequal(creator, ccreator));
512 g_free(ccreator);
515 if (result == TRUE)
516 return content;
518 return NULL;
521 JingleContent *
522 jingle_session_find_pending_content(JingleSession *session, const gchar *name, const gchar *creator)
524 JingleSessionPrivate *priv = NULL;
525 GList *iter;
527 g_return_val_if_fail(JINGLE_IS_SESSION(session), NULL);
529 if (name == NULL)
530 return NULL;
532 priv = jingle_session_get_instance_private(session);
534 iter = priv->pending_contents;
535 for (; iter; iter = g_list_next(iter)) {
536 JingleContent *content = iter->data;
537 gchar *cname = jingle_content_get_name(content);
538 gboolean result = purple_strequal(name, cname);
539 g_free(cname);
541 if (creator != NULL) {
542 gchar *ccreator = jingle_content_get_creator(content);
543 result = (result && purple_strequal(creator, ccreator));
544 g_free(ccreator);
547 if (result == TRUE)
548 return content;
550 return NULL;
553 void
554 jingle_session_add_content(JingleSession *session, JingleContent* content)
556 JingleSessionPrivate *priv = NULL;
558 g_return_if_fail(JINGLE_IS_SESSION(session));
560 priv = jingle_session_get_instance_private(session);
562 priv->contents = g_list_append(priv->contents, content);
563 jingle_content_set_session(content, session);
565 g_object_notify_by_pspec(G_OBJECT(session), properties[PROP_CONTENTS]);
568 void
569 jingle_session_remove_content(JingleSession *session, const gchar *name, const gchar *creator)
571 JingleSessionPrivate *priv = NULL;
572 JingleContent *content = NULL;
574 g_return_if_fail(JINGLE_IS_SESSION(session));
576 priv = jingle_session_get_instance_private(session);
577 content = jingle_session_find_content(session, name, creator);
579 if (content) {
580 priv->contents = g_list_remove(priv->contents, content);
581 g_object_unref(content);
583 g_object_notify_by_pspec(G_OBJECT(session), properties[PROP_CONTENTS]);
587 void
588 jingle_session_add_pending_content(JingleSession *session, JingleContent* content)
590 JingleSessionPrivate *priv = NULL;
592 g_return_if_fail(JINGLE_IS_SESSION(session));
594 priv = jingle_session_get_instance_private(session);
596 priv->pending_contents = g_list_append(priv->pending_contents, content);
597 jingle_content_set_session(content, session);
599 g_object_notify_by_pspec(G_OBJECT(session), properties[PROP_PENDING_CONTENTS]);
602 void
603 jingle_session_remove_pending_content(JingleSession *session, const gchar *name, const gchar *creator)
605 JingleSessionPrivate *priv = NULL;
606 JingleContent *content = NULL;
608 g_return_if_fail(JINGLE_IS_SESSION(session));
610 priv = jingle_session_get_instance_private(session);
611 content = jingle_session_find_pending_content(session, name, creator);
613 if (content) {
614 priv->pending_contents = g_list_remove(priv->pending_contents, content);
615 g_object_unref(content);
617 g_object_notify_by_pspec(G_OBJECT(session), properties[PROP_PENDING_CONTENTS]);
621 void
622 jingle_session_accept_content(JingleSession *session, const gchar *name, const gchar *creator)
624 JingleContent *content = NULL;
626 g_return_if_fail(JINGLE_IS_SESSION(session));
628 content = jingle_session_find_pending_content(session, name, creator);
629 if (content) {
630 g_object_ref(content);
631 jingle_session_remove_pending_content(session, name, creator);
632 jingle_session_add_content(session, content);
636 void
637 jingle_session_accept_session(JingleSession *session)
639 JingleSessionPrivate *priv = NULL;
641 g_return_if_fail(JINGLE_IS_SESSION(session));
643 priv = jingle_session_get_instance_private(session);
645 priv->state = TRUE;
647 g_object_notify_by_pspec(G_OBJECT(session), properties[PROP_STATE]);
650 JabberIq *
651 jingle_session_terminate_packet(JingleSession *session, const gchar *reason)
653 JabberIq *iq = NULL;
655 g_return_val_if_fail(JINGLE_IS_SESSION(session), NULL);
657 iq = jingle_session_to_packet(session, JINGLE_SESSION_TERMINATE);
659 if (reason != NULL) {
660 PurpleXmlNode *reason_node;
661 PurpleXmlNode *jingle = purple_xmlnode_get_child(iq->node, "jingle");
662 reason_node = purple_xmlnode_new_child(jingle, "reason");
663 purple_xmlnode_new_child(reason_node, reason);
666 return iq;
669 JabberIq *
670 jingle_session_redirect_packet(JingleSession *session, const gchar *sid)
672 JabberIq *iq = NULL;
673 PurpleXmlNode *alt_session;
675 g_return_val_if_fail(JINGLE_IS_SESSION(session), NULL);
677 iq = jingle_session_terminate_packet(session, "alternative-session");
679 if (sid == NULL)
680 return iq;
682 alt_session = purple_xmlnode_get_child(iq->node,
683 "jingle/reason/alternative-session");
685 if (alt_session != NULL) {
686 PurpleXmlNode *sid_node = purple_xmlnode_new_child(alt_session, "sid");
687 purple_xmlnode_insert_data(sid_node, sid, -1);
690 return iq;