comparison libpurple/protocols/jabber/jingle/session.c @ 26014:bd598b606ca4

Restructure Jingle code to more easily support multiple application types. Actually negotiate a rawudp transport rather than pretending to use iceudp.
author Mike Ruprecht <maiku@soc.pidgin.im>
date Sun, 19 Oct 2008 04:37:23 +0000
parents
children db517c55c508
comparison
equal deleted inserted replaced
26013:5a774d0817d8 26014:bd598b606ca4
1 /**
2 * @file session.c
3 *
4 * purple
5 *
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.
10 *
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.
15 *
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
19 */
20
21 #include "config.h"
22 #include "content.h"
23 #include "debug.h"
24 #include "session.h"
25 #include "jingle.h"
26
27 #include <string.h>
28
29 struct _JingleSessionPrivate
30 {
31 gchar *sid;
32 JabberStream *js;
33 gchar *remote_jid;
34 gchar *local_jid;
35 gboolean is_initiator;
36 gboolean state;
37 GList *contents;
38 GList *pending_contents;
39 };
40
41 #define JINGLE_SESSION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), JINGLE_TYPE_SESSION, JingleSessionPrivate))
42
43 static void jingle_session_class_init (JingleSessionClass *klass);
44 static void jingle_session_init (JingleSession *session);
45 static void jingle_session_finalize (GObject *object);
46 static void jingle_session_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
47 static void jingle_session_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
48
49 static GObjectClass *parent_class = NULL;
50
51 enum {
52 PROP_0,
53 PROP_SID,
54 PROP_JS,
55 PROP_REMOTE_JID,
56 PROP_LOCAL_JID,
57 PROP_IS_INITIATOR,
58 PROP_STATE,
59 PROP_CONTENTS,
60 PROP_PENDING_CONTENTS,
61 };
62
63 GType
64 jingle_session_get_type()
65 {
66 static GType type = 0;
67
68 if (type == 0) {
69 static const GTypeInfo info = {
70 sizeof(JingleSessionClass),
71 NULL,
72 NULL,
73 (GClassInitFunc) jingle_session_class_init,
74 NULL,
75 NULL,
76 sizeof(JingleSession),
77 0,
78 (GInstanceInitFunc) jingle_session_init,
79 NULL
80 };
81 type = g_type_register_static(G_TYPE_OBJECT, "JingleSession", &info, 0);
82 }
83 return type;
84 }
85
86 static void
87 jingle_session_class_init (JingleSessionClass *klass)
88 {
89 GObjectClass *gobject_class = (GObjectClass*)klass;
90 parent_class = g_type_class_peek_parent(klass);
91
92 gobject_class->finalize = jingle_session_finalize;
93 gobject_class->set_property = jingle_session_set_property;
94 gobject_class->get_property = jingle_session_get_property;
95
96 g_object_class_install_property(gobject_class, PROP_SID,
97 g_param_spec_string("sid",
98 "Session ID",
99 "The unique session ID of the Jingle Session.",
100 NULL,
101 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
102
103 g_object_class_install_property(gobject_class, PROP_JS,
104 g_param_spec_pointer("js",
105 "JabberStream",
106 "The Jabber stream associated with this session.",
107 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
108
109 g_object_class_install_property(gobject_class, PROP_REMOTE_JID,
110 g_param_spec_string("remote-jid",
111 "Remote JID",
112 "The JID of the remote participant.",
113 NULL,
114 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
115
116 g_object_class_install_property(gobject_class, PROP_LOCAL_JID,
117 g_param_spec_string("local-jid",
118 "Local JID",
119 "The JID of the local participant.",
120 NULL,
121 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
122
123 g_object_class_install_property(gobject_class, PROP_IS_INITIATOR,
124 g_param_spec_boolean("is-initiator",
125 "Is Initiator",
126 "Whether or not the local JID is the initiator of the session.",
127 FALSE,
128 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
129
130 g_object_class_install_property(gobject_class, PROP_STATE,
131 g_param_spec_boolean("state",
132 "State",
133 "The state of the session (PENDING=FALSE, ACTIVE=TRUE).",
134 FALSE,
135 G_PARAM_READABLE));
136
137 g_object_class_install_property(gobject_class, PROP_CONTENTS,
138 g_param_spec_pointer("contents",
139 "Contents",
140 "The active contents contained within this session",
141 G_PARAM_READABLE));
142
143 g_object_class_install_property(gobject_class, PROP_PENDING_CONTENTS,
144 g_param_spec_pointer("pending-contents",
145 "Pending contents",
146 "The pending contents contained within this session",
147 G_PARAM_READABLE));
148
149 g_type_class_add_private(klass, sizeof(JingleSessionPrivate));
150 }
151
152 static void
153 jingle_session_init (JingleSession *session)
154 {
155 session->priv = JINGLE_SESSION_GET_PRIVATE(session);
156 memset(session->priv, 0, sizeof(session->priv));
157 }
158
159 static void
160 jingle_session_finalize (GObject *session)
161 {
162 JingleSessionPrivate *priv = JINGLE_SESSION_GET_PRIVATE(session);
163 purple_debug_info("jingle","jingle_session_finalize\n");
164
165 g_hash_table_remove(priv->js->sessions, priv->sid);
166
167 g_free(priv->sid);
168 g_free(priv->remote_jid);
169 g_free(priv->local_jid);
170
171 for (; priv->contents; priv->contents =
172 g_list_delete_link(priv->contents, priv->contents)) {
173 g_object_unref(priv->contents->data);
174 }
175 for (; priv->pending_contents; priv->pending_contents =
176 g_list_delete_link(priv->pending_contents, priv->pending_contents)) {
177 g_object_unref(priv->pending_contents->data);
178 }
179
180 parent_class->finalize(session);
181 }
182
183 static void
184 jingle_session_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
185 {
186 JingleSession *session;
187 g_return_if_fail(JINGLE_IS_SESSION(object));
188
189 session = JINGLE_SESSION(object);
190
191 switch (prop_id) {
192 case PROP_SID:
193 g_free(session->priv->sid);
194 session->priv->sid = g_value_dup_string(value);
195 break;
196 case PROP_JS:
197 session->priv->js = g_value_get_pointer(value);
198 break;
199 case PROP_REMOTE_JID:
200 g_free(session->priv->remote_jid);
201 session->priv->remote_jid = g_value_dup_string(value);
202 break;
203 case PROP_LOCAL_JID:
204 g_free(session->priv->local_jid);
205 session->priv->local_jid = g_value_dup_string(value);
206 break;
207 case PROP_IS_INITIATOR:
208 session->priv->is_initiator = g_value_get_boolean(value);
209 break;
210 case PROP_STATE:
211 session->priv->state = g_value_get_boolean(value);
212 break;
213 case PROP_CONTENTS:
214 session->priv->contents = g_value_get_pointer(value);
215 break;
216 case PROP_PENDING_CONTENTS:
217 session->priv->pending_contents = g_value_get_pointer(value);
218 break;
219 default:
220 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
221 break;
222 }
223 }
224
225 static void
226 jingle_session_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
227 {
228 JingleSession *session;
229 g_return_if_fail(JINGLE_IS_SESSION(object));
230
231 session = JINGLE_SESSION(object);
232
233 switch (prop_id) {
234 case PROP_SID:
235 g_value_set_string(value, session->priv->sid);
236 break;
237 case PROP_JS:
238 g_value_set_pointer(value, session->priv->js);
239 break;
240 case PROP_REMOTE_JID:
241 g_value_set_string(value, session->priv->remote_jid);
242 break;
243 case PROP_LOCAL_JID:
244 g_value_set_string(value, session->priv->local_jid);
245 break;
246 case PROP_IS_INITIATOR:
247 g_value_set_boolean(value, session->priv->is_initiator);
248 break;
249 case PROP_STATE:
250 g_value_set_boolean(value, session->priv->state);
251 break;
252 case PROP_CONTENTS:
253 g_value_set_pointer(value, session->priv->contents);
254 break;
255 case PROP_PENDING_CONTENTS:
256 g_value_set_pointer(value, session->priv->pending_contents);
257 break;
258 default:
259 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
260 break;
261 }
262 }
263
264
265 JingleSession *
266 jingle_session_create(JabberStream *js, const gchar *sid,
267 const gchar *local_jid, const gchar *remote_jid,
268 gboolean is_initiator)
269 {
270 JingleSession *session = g_object_new(jingle_session_get_type(),
271 "js", js,
272 "sid", sid,
273 "local-jid", local_jid,
274 "remote-jid", remote_jid,
275 "is_initiator", is_initiator,
276 NULL);
277
278 /* insert it into the hash table */
279 if (!js->sessions) {
280 purple_debug_info("jingle",
281 "Creating hash table for sessions\n");
282 js->sessions = g_hash_table_new(g_str_hash, g_str_equal);
283 }
284 purple_debug_info("jingle",
285 "inserting session with key: %s into table\n", sid);
286 g_hash_table_insert(js->sessions, g_strdup(sid), session);
287
288 return session;
289 }
290
291 JabberStream *
292 jingle_session_get_js(JingleSession *session)
293 {
294 JabberStream *js;
295 g_object_get(session, "js", &js, NULL);
296 return js;
297 }
298
299 gchar *
300 jingle_session_get_sid(JingleSession *session)
301 {
302 gchar *sid;
303 g_object_get(session, "sid", &sid, NULL);
304 return sid;
305 }
306
307 gchar *
308 jingle_session_get_local_jid(JingleSession *session)
309 {
310 gchar *local_jid;
311 g_object_get(session, "local-jid", &local_jid, NULL);
312 return local_jid;
313 }
314
315 gchar *
316 jingle_session_get_remote_jid(JingleSession *session)
317 {
318 gchar *remote_jid;
319 g_object_get(session, "remote-jid", &remote_jid, NULL);
320 return remote_jid;
321 }
322
323 gboolean
324 jingle_session_is_initiator(JingleSession *session)
325 {
326 gboolean is_initiator;
327 g_object_get(session, "is-initiator", &is_initiator, NULL);
328 return is_initiator;
329 }
330
331 gboolean
332 jingle_session_get_state(JingleSession *session)
333 {
334 gboolean state;
335 g_object_get(session, "state", &state, NULL);
336 return state;
337 }
338
339 GList *
340 jingle_session_get_contents(JingleSession *session)
341 {
342 GList *contents;
343 g_object_get(session, "contents", &contents, NULL);
344 return contents;
345 }
346
347 GList *
348 jingle_session_get_pending_contents(JingleSession *session)
349 {
350 GList *pending_contents;
351 g_object_get(session, "pending-contents", &pending_contents, NULL);
352 return pending_contents;
353 }
354
355 JingleSession *
356 jingle_session_find_by_sid(JabberStream *js, const gchar *sid)
357 {
358 purple_debug_info("jingle", "find_by_id %s\n", sid);
359 purple_debug_info("jingle", "lookup: %p\n", (js->sessions) ?
360 g_hash_table_lookup(js->sessions, sid) : NULL);
361 return (JingleSession *) (js->sessions) ?
362 g_hash_table_lookup(js->sessions, sid) : NULL;
363 }
364
365 JingleSession *
366 jingle_session_find_by_jid(JabberStream *js, const gchar *jid)
367 {
368 GList *values = (js->sessions) ?
369 g_hash_table_get_values(js->sessions) : NULL;
370 gboolean use_bare = strchr(jid, '/') == NULL;
371
372 for (; values; values = g_list_delete_link(values, values)) {
373 JingleSession *session = (JingleSession *)values->data;
374 gchar *remote_jid = jingle_session_get_remote_jid(session);
375 gchar *cmp_jid = use_bare ? jabber_get_bare_jid(remote_jid)
376 : g_strdup(remote_jid);
377 g_free(remote_jid);
378 if (!strcmp(jid, cmp_jid)) {
379 g_free(cmp_jid);
380 g_list_free(values);
381 return session;
382 }
383 g_free(cmp_jid);
384 }
385
386 return NULL;
387 }
388
389 static xmlnode *
390 jingle_add_jingle_packet(JingleSession *session,
391 JabberIq *iq, JingleActionType action)
392 {
393 xmlnode *jingle = iq ?
394 xmlnode_new_child(iq->node, "jingle") :
395 xmlnode_new("jingle");
396 gchar *local_jid = jingle_session_get_local_jid(session);
397 gchar *remote_jid = jingle_session_get_remote_jid(session);
398
399 xmlnode_set_namespace(jingle, JINGLE);
400 xmlnode_set_attrib(jingle, "action", jingle_get_action_name(action));
401
402 if (jingle_session_is_initiator(session)) {
403 xmlnode_set_attrib(jingle, "initiator",
404 jingle_session_get_local_jid(session));
405 xmlnode_set_attrib(jingle, "responder",
406 jingle_session_get_remote_jid(session));
407 } else {
408 xmlnode_set_attrib(jingle, "initiator",
409 jingle_session_get_remote_jid(session));
410 xmlnode_set_attrib(jingle, "responder",
411 jingle_session_get_local_jid(session));
412 }
413
414 g_free(local_jid);
415 g_free(remote_jid);
416
417 xmlnode_set_attrib(jingle, "sid", jingle_session_get_sid(session));
418
419 return jingle;
420 }
421
422 JabberIq *
423 jingle_session_create_ack(JingleSession *session, const xmlnode *jingle)
424 {
425 JabberIq *result = jabber_iq_new(
426 jingle_session_get_js(session),
427 JABBER_IQ_RESULT);
428 xmlnode *packet = xmlnode_get_parent(jingle);
429 jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id"));
430 xmlnode_set_attrib(result->node, "from", xmlnode_get_attrib(packet, "to"));
431 xmlnode_set_attrib(result->node, "to", xmlnode_get_attrib(packet, "from"));
432 return result;
433 }
434
435 static JabberIq *
436 jingle_create_iq(JingleSession *session)
437 {
438 JabberStream *js = jingle_session_get_js(session);
439 JabberIq *result = jabber_iq_new(js, JABBER_IQ_SET);
440 gchar *from = jingle_session_get_local_jid(session);
441 gchar *to = jingle_session_get_remote_jid(session);
442
443 xmlnode_set_attrib(result->node, "from", from);
444 xmlnode_set_attrib(result->node, "to", to);
445
446 g_free(from);
447 g_free(to);
448 return result;
449 }
450
451 xmlnode *
452 jingle_session_to_xml(JingleSession *session, xmlnode *jingle, JingleActionType action)
453 {
454 if (action != JINGLE_SESSION_INFO && action != JINGLE_SESSION_TERMINATE) {
455 GList *iter;
456 if (action == JINGLE_CONTENT_ACCEPT
457 || action == JINGLE_CONTENT_ADD
458 || action == JINGLE_CONTENT_REMOVE)
459 iter = jingle_session_get_pending_contents(session);
460 else
461 iter = jingle_session_get_contents(session);
462
463 for (; iter; iter = g_list_next(iter)) {
464 jingle_content_to_xml(iter->data, jingle, action);
465 }
466 }
467 return jingle;
468 }
469
470 JabberIq *
471 jingle_session_to_packet(JingleSession *session, JingleActionType action)
472 {
473 JabberIq *iq = jingle_create_iq(session);
474 xmlnode *jingle = jingle_add_jingle_packet(session, iq, action);
475 jingle_session_to_xml(session, jingle, action);
476 return iq;
477 }
478
479 void jingle_session_handle_action(JingleSession *session, xmlnode *jingle, JingleActionType action)
480 {
481 GList *iter;
482 if (action == JINGLE_CONTENT_ADD || action == JINGLE_CONTENT_REMOVE)
483 iter = jingle_session_get_pending_contents(session);
484 else
485 iter = jingle_session_get_contents(session);
486
487 for (; iter; iter = g_list_next(iter)) {
488 jingle_content_handle_action(iter->data, jingle, action);
489 }
490 }
491
492 JingleContent *
493 jingle_session_find_content(JingleSession *session, const gchar *name, const gchar *creator)
494 {
495 GList *iter = session->priv->contents;
496 for (; iter; iter = g_list_next(iter)) {
497 JingleContent *content = iter->data;
498 gchar *cname = jingle_content_get_name(content);
499 gchar *ccreator = jingle_content_get_creator(content);
500 gboolean result = (!strcmp(name, cname) && !strcmp(creator, ccreator));
501
502 g_free(cname);
503 g_free(ccreator);
504
505 if (result == TRUE)
506 return content;
507 }
508 return NULL;
509 }
510
511 JingleContent *
512 jingle_session_find_pending_content(JingleSession *session, const gchar *name, const gchar *creator)
513 {
514 GList *iter = session->priv->pending_contents;
515 for (; iter; iter = g_list_next(iter)) {
516 JingleContent *content = iter->data;
517 gchar *cname = jingle_content_get_name(content);
518 gchar *ccreator = jingle_content_get_creator(content);
519 gboolean result = (!strcmp(name, cname) && !strcmp(creator, ccreator));
520
521 g_free(cname);
522 g_free(ccreator);
523
524 if (result == TRUE)
525 return content;
526 }
527 return NULL;
528 }
529
530 void
531 jingle_session_add_content(JingleSession *session, JingleContent* content)
532 {
533 session->priv->contents =
534 g_list_append(session->priv->contents, content);
535 jingle_content_set_session(content, session);
536 }
537
538 void
539 jingle_session_remove_content(JingleSession *session, const gchar *name, const gchar *creator)
540 {
541 JingleContent *content =
542 jingle_session_find_content(session, name, creator);
543
544 if (content) {
545 session->priv->contents =
546 g_list_remove(session->priv->contents, content);
547 g_object_unref(content);
548 }
549 }
550
551 void
552 jingle_session_add_pending_content(JingleSession *session, JingleContent* content)
553 {
554 session->priv->pending_contents =
555 g_list_append(session->priv->pending_contents, content);
556 jingle_content_set_session(content, session);
557 }
558
559 void
560 jingle_session_remove_pending_content(JingleSession *session, const gchar *name, const gchar *creator)
561 {
562 JingleContent *content = jingle_session_find_pending_content(session, name, creator);
563
564 if (content) {
565 session->priv->pending_contents =
566 g_list_remove(session->priv->pending_contents, content);
567 g_object_unref(content);
568 }
569 }
570
571 void
572 jingle_session_accept_content(JingleSession *session, const gchar *name, const gchar *creator)
573 {
574 JingleContent *content = jingle_session_find_pending_content(session, name, creator);
575
576 if (content) {
577 g_object_ref(content);
578 jingle_session_remove_pending_content(session, name, creator);
579 jingle_session_add_content(session, content);
580 }
581 }
582
583 void
584 jingle_session_accept_session(JingleSession *session)
585 {
586 session->priv->state = TRUE;
587 }
588