comparison libpurple/protocols/jabber/jingle.c @ 25638:9f36ed35615e

Add the new jingle.c/h files.
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Sat, 22 Mar 2008 04:48:36 +0000
parents
children e1c8ec1259de
comparison
equal deleted inserted replaced
25637:92e71f6e10d4 25638:9f36ed35615e
1 /*
2 * purple - Jabber Protocol Plugin
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
17 *
18 */
19
20 #include "config.h"
21 #include "purple.h"
22 #include "jingle.h"
23 #include "xmlnode.h"
24
25 #include <stdlib.h>
26 #include <string.h>
27 #include <glib.h>
28
29 #ifdef USE_FARSIGHT
30
31 #include <farsight/farsight.h>
32 #include <farsight/farsight-transport.h>
33
34 /* keep a hash table of JingleSessions */
35 static GHashTable *sessions = NULL;
36
37 static gboolean
38 jabber_jingle_session_equal(gconstpointer a, gconstpointer b)
39 {
40 purple_debug_info("jingle",
41 "jabber_jingle_session_equal, comparing %s and %s\n",
42 ((JingleSession *)a)->id,
43 ((JingleSession *)b)->id);
44 return !strcmp(((JingleSession *) a)->id, ((JingleSession *) b)->id);
45 }
46
47 static JingleSession *
48 jabber_jingle_session_create_internal(JabberStream *js,
49 const char *id)
50 {
51 JingleSession *sess = g_new0(JingleSession, 1);
52 sess->js = js;
53
54 if (id) {
55 sess->id = g_strdup(id);
56 } else if (js) {
57 /* init the session ID... */
58 sess->id = jabber_get_next_id(js);
59 }
60
61 /* insert it into the hash table */
62 if (!sessions) {
63 purple_debug_info("jingle", "Creating hash table for sessions\n");
64 sessions = g_hash_table_new(g_str_hash, g_str_equal);
65 }
66 purple_debug_info("jingle", "inserting session with key: %s into table\n",
67 sess->id);
68 g_hash_table_insert(sessions, sess->id, sess);
69
70 sess->remote_candidates = NULL;
71 sess->remote_codecs = NULL;
72
73 return sess;
74 }
75
76 JabberStream *
77 jabber_jingle_session_get_js(const JingleSession *sess)
78 {
79 return sess->js;
80 }
81
82 JingleSession *
83 jabber_jingle_session_create(JabberStream *js)
84 {
85 JingleSession *sess = jabber_jingle_session_create_internal(js, NULL);
86 sess->is_initiator = TRUE;
87 return sess;
88 }
89
90 JingleSession *
91 jabber_jingle_session_create_by_id(JabberStream *js, const char *id)
92 {
93 JingleSession *sess = jabber_jingle_session_create_internal(js, id);
94 sess->is_initiator = FALSE;
95 return sess;
96 }
97
98 const char *
99 jabber_jingle_session_get_id(const JingleSession *sess)
100 {
101 return sess->id;
102 }
103
104 void
105 jabber_jingle_session_destroy(JingleSession *sess)
106 {
107 g_hash_table_remove(sessions, sess->id);
108 g_free(sess->id);
109 farsight_codec_list_destroy(sess->remote_codecs);
110 g_list_free(sess->remote_candidates);
111 g_free(sess);
112 }
113
114 JingleSession *
115 jabber_jingle_session_find_by_id(const char *id)
116 {
117 purple_debug_info("jingle", "find_by_id %s\n", id);
118 purple_debug_info("jingle", "hash table: %lx\n", sessions);
119 purple_debug_info("jingle", "hash table size %d\n",
120 g_hash_table_size(sessions));
121 purple_debug_info("jingle", "lookup: %lx\n", g_hash_table_lookup(sessions, id));
122 return (JingleSession *) g_hash_table_lookup(sessions, id);
123 }
124
125 GList *
126 jabber_jingle_get_codecs(const xmlnode *description)
127 {
128 GList *codecs = NULL;
129 xmlnode *codec_element = NULL;
130 const char *encoding_name,*id, *clock_rate;
131 FarsightCodec *codec;
132
133 for (codec_element = xmlnode_get_child(description, "payload-type") ;
134 codec_element ;
135 codec_element = xmlnode_get_next_twin(codec_element)) {
136 encoding_name = xmlnode_get_attrib(codec_element, "name");
137 id = xmlnode_get_attrib(codec_element, "id");
138 clock_rate = xmlnode_get_attrib(codec_element, "clockrate");
139
140 codec = g_new0(FarsightCodec, 1);
141 farsight_codec_init(codec, atoi(id), encoding_name,
142 FARSIGHT_MEDIA_TYPE_AUDIO,
143 clock_rate ? atoi(clock_rate) : 0);
144 codecs = g_list_append(codecs, codec);
145 }
146 return codecs;
147 }
148
149 GList *
150 jabber_jingle_get_candidates(const xmlnode *transport)
151 {
152 GList *candidates = NULL;
153 xmlnode *candidate = NULL;
154 FarsightTransportInfo *ti;
155
156 for (candidate = xmlnode_get_child(transport, "candidate") ;
157 candidate ;
158 candidate = xmlnode_get_next_twin(candidate)) {
159 const char *type = xmlnode_get_attrib(candidate, "type");
160 ti = g_new0(FarsightTransportInfo, 1);
161 ti->component = atoi(xmlnode_get_attrib(candidate, "component"));
162 ti->ip = xmlnode_get_attrib(candidate, "ip");
163 ti->port = atoi(xmlnode_get_attrib(candidate, "port"));
164 ti->proto = strcmp(xmlnode_get_attrib(candidate, "protocol"),
165 "udp") == 0 ?
166 FARSIGHT_NETWORK_PROTOCOL_UDP :
167 FARSIGHT_NETWORK_PROTOCOL_TCP;
168 /* it seems Farsight RTP doesn't handle this correctly right now */
169 ti->username = xmlnode_get_attrib(candidate, "ufrag");
170 ti->password = xmlnode_get_attrib(candidate, "pwd");
171 /* not quite sure about this */
172 ti->type = strcmp(type, "host") == 0 ?
173 FARSIGHT_CANDIDATE_TYPE_LOCAL :
174 strcmp(type, "prflx") == 0 ?
175 FARSIGHT_CANDIDATE_TYPE_DERIVED :
176 FARSIGHT_CANDIDATE_TYPE_RELAY;
177
178 candidates = g_list_append(candidates, ti);
179 }
180
181 return candidates;
182 }
183
184 FarsightStream *
185 jabber_jingle_session_get_stream(const JingleSession *sess)
186 {
187 return sess->stream;
188 }
189
190 void
191 jabber_jingle_session_set_stream(JingleSession *sess, FarsightStream *stream)
192 {
193 sess->stream = stream;
194 }
195
196 PurpleMedia *
197 jabber_jingle_session_get_media(const JingleSession *sess)
198 {
199 return sess->media;
200 }
201
202 void
203 jabber_jingle_session_set_media(JingleSession *sess, PurpleMedia *media)
204 {
205 sess->media = media;
206 }
207
208 const char *
209 jabber_jingle_session_get_remote_jid(const JingleSession *sess)
210 {
211 return sess->remote_jid;
212 }
213
214 void
215 jabber_jingle_session_set_remote_jid(JingleSession *sess,
216 const char *remote_jid)
217 {
218 sess->remote_jid = strdup(remote_jid);
219 }
220
221 const char *
222 jabber_jingle_session_get_initiator(const JingleSession *sess)
223 {
224 return sess->initiator;
225 }
226
227 void
228 jabber_jingle_session_set_initiator(JingleSession *sess,
229 const char *initiator)
230 {
231 sess->initiator = g_strdup(initiator);
232 }
233
234 gboolean
235 jabber_jingle_session_is_initiator(const JingleSession *sess)
236 {
237 return sess->is_initiator;
238 }
239
240 void
241 jabber_jingle_session_add_remote_candidate(JingleSession *sess,
242 const GList *candidate)
243 {
244 /* the length of the candidate list should be 1... */
245 GList *cand = candidate;
246 for (; cand ; cand = cand->next) {
247 purple_debug_info("jingle", "Adding remote candidate with id = %s\n",
248 ((FarsightTransportInfo *) cand->data)->candidate_id);
249 sess->remote_candidates = g_list_append(sess->remote_candidates,
250 cand->data);
251 }
252 }
253
254 static GList *
255 jabber_jingle_session_get_remote_candidate(const JingleSession *sess,
256 const gchar *id)
257 {
258 GList *candidates = NULL;
259 GList *find = sess->remote_candidates;
260 for (; find ; find = find->next) {
261 const FarsightTransportInfo *candidate =
262 (FarsightTransportInfo *) find->data;
263 if (!strcmp(candidate->candidate_id, id)) {
264 candidates = g_list_append(candidates, (void *) candidate);
265 }
266 }
267 return candidates;
268 }
269
270 static xmlnode *
271 jabber_jingle_session_create_jingle_element(const JingleSession *sess,
272 const char *action)
273 {
274 xmlnode *jingle = xmlnode_new("jingle");
275 xmlnode_set_namespace(jingle, "urn:xmpp:tmp:jingle");
276 xmlnode_set_attrib(jingle, "action", action);
277 xmlnode_set_attrib(jingle, "initiator",
278 jabber_jingle_session_get_initiator(sess));
279 xmlnode_set_attrib(jingle, "responder",
280 jabber_jingle_session_is_initiator(sess) ?
281 jabber_jingle_session_get_remote_jid(sess) :
282 jabber_jingle_session_get_initiator(sess));
283 xmlnode_set_attrib(jingle, "sid", sess->id);
284
285 return jingle;
286 }
287
288 xmlnode *
289 jabber_jingle_session_create_terminate(const JingleSession *sess,
290 const char *reasoncode,
291 const char *reasontext)
292 {
293 xmlnode *jingle =
294 jabber_jingle_session_create_jingle_element(sess, "session-terminate");
295 xmlnode_set_attrib(jingle, "resoncode", reasoncode);
296 if (reasontext) {
297 xmlnode_set_attrib(jingle, "reasontext", reasontext);
298 }
299 xmlnode_set_attrib(jingle, "sid", sess->id);
300
301 return jingle;
302 }
303
304 static xmlnode *
305 jabber_jingle_session_create_description(const JingleSession *sess)
306 {
307 GList *codecs =
308 farsight_stream_get_local_codecs(jabber_jingle_session_get_stream(sess));
309
310 xmlnode *description = xmlnode_new("description");
311 xmlnode_set_namespace(description, "urn:xmpp:tmp:jingle:apps:audio-rtp");
312
313 /* get codecs */
314 for (; codecs ; codecs = codecs->next) {
315 FarsightCodec *codec = (FarsightCodec*)codecs->data;
316 char id[8], clockrate[10];
317 xmlnode *payload = xmlnode_new_child(description, "payload-type");
318
319 g_snprintf(id, sizeof(id), "%d", codec->id);
320 g_snprintf(clockrate, sizeof(clockrate), "%d", codec->clock_rate);
321
322 xmlnode_set_attrib(payload, "name", codec->encoding_name);
323 xmlnode_set_attrib(payload, "id", id);
324 xmlnode_set_attrib(payload, "clockrate", clockrate);
325 }
326
327 return description;
328 }
329
330 /* split into two separate methods, one to generate session-accept
331 (includes codecs) and one to generate transport-info (includes transports
332 candidates) */
333 xmlnode *
334 jabber_jingle_session_create_session_accept(const JingleSession *sess)
335 {
336 xmlnode *jingle =
337 jabber_jingle_session_create_jingle_element(sess, "session-accept");
338 xmlnode *content = NULL;
339 xmlnode *description = NULL;
340
341
342 content = xmlnode_new_child(jingle, "content");
343 xmlnode_set_attrib(content, "creator", "initiator");
344 xmlnode_set_attrib(content, "name", "audio-content");
345 xmlnode_set_attrib(content, "profile", "RTP/AVP");
346
347 description = jabber_jingle_session_create_description(sess);
348 xmlnode_insert_child(jingle, description);
349
350 return jingle;
351 }
352
353 static guint
354 jabber_jingle_get_priority(guint type, guint network)
355 {
356 switch (type) {
357 case FARSIGHT_CANDIDATE_TYPE_LOCAL:
358 return network == 0 ? 4096 : network == 1 ? 2048 : 1024;
359 break;
360 case FARSIGHT_CANDIDATE_TYPE_DERIVED:
361 return 126;
362 break;
363 case FARSIGHT_CANDIDATE_TYPE_RELAY:
364 return 100;
365 break;
366 default:
367 return 0; /* unknown type, should not happen */
368 }
369 }
370
371 xmlnode *
372 jabber_jingle_session_create_transport_info(const JingleSession *sess,
373 gchar *candidate_id)
374 {
375 xmlnode *jingle =
376 jabber_jingle_session_create_jingle_element(sess, "transport-info");
377 xmlnode *content = NULL;
378 xmlnode *transport = NULL;
379 GList *candidates =
380 farsight_stream_get_native_candidate(
381 jabber_jingle_session_get_stream(sess), candidate_id);
382
383 content = xmlnode_new_child(jingle, "content");
384 xmlnode_set_attrib(content, "creator", "initiator");
385 xmlnode_set_attrib(content, "name", "audio-content");
386 xmlnode_set_attrib(content, "profile", "RTP/AVP");
387
388 transport = xmlnode_new_child(content, "transport");
389 xmlnode_set_namespace(transport, "urn:xmpp:tmp:jingle:transports:ice-tcp");
390
391 /* get transport candidate */
392 for (; candidates ; candidates = candidates->next) {
393 FarsightTransportInfo *transport_info =
394 (FarsightTransportInfo *) candidates->data;
395 char port[8];
396 char prio[8];
397 char component[8];
398 xmlnode *candidate = NULL;
399
400 if (!strcmp(transport_info->ip, "127.0.0.1")) {
401 continue;
402 }
403
404 candidate = xmlnode_new_child(transport, "candidate");
405
406 g_snprintf(port, sizeof(port), "%d", transport_info->port);
407 g_snprintf(prio, sizeof(prio), "%d",
408 jabber_jingle_get_priority(transport_info->type, 0));
409 g_snprintf(component, sizeof(component), "%d", transport_info->component);
410
411 xmlnode_set_attrib(candidate, "component", component);
412 xmlnode_set_attrib(candidate, "foundation", "1"); /* what about this? */
413 xmlnode_set_attrib(candidate, "generation", "0"); /* ? */
414 xmlnode_set_attrib(candidate, "ip", transport_info->ip);
415 xmlnode_set_attrib(candidate, "network", "0"); /* ? */
416 xmlnode_set_attrib(candidate, "port", port);
417 xmlnode_set_attrib(candidate, "priority", prio); /* Is this correct? */
418 xmlnode_set_attrib(candidate, "protocol",
419 transport_info->proto == FARSIGHT_NETWORK_PROTOCOL_UDP ?
420 "udp" : "tcp");
421 if (transport_info->username)
422 xmlnode_set_attrib(candidate, "ufrag", transport_info->username);
423 if (transport_info->password)
424 xmlnode_set_attrib(candidate, "pwd", transport_info->password);
425
426 xmlnode_set_attrib(candidate, "type",
427 transport_info->type == FARSIGHT_CANDIDATE_TYPE_LOCAL ?
428 "host" :
429 transport_info->type == FARSIGHT_CANDIDATE_TYPE_DERIVED ?
430 "prflx" :
431 transport_info->type == FARSIGHT_CANDIDATE_TYPE_RELAY ?
432 "relay" : NULL);
433
434 }
435 farsight_transport_list_destroy(candidates);
436
437 return jingle;
438 }
439
440 xmlnode *
441 jabber_jingle_session_create_content_replace(const JingleSession *sess,
442 gchar *native_candidate_id,
443 gchar *remote_candidate_id)
444 {
445 xmlnode *jingle =
446 jabber_jingle_session_create_jingle_element(sess, "content-replace");
447 xmlnode *content = NULL;
448 xmlnode *transport = NULL;
449 xmlnode *candidate = NULL;
450 xmlnode *description = NULL;
451 xmlnode *payload_type = NULL;
452 char local_port[8];
453 char remote_port[8];
454 char prio[8];
455 char component[8];
456
457 purple_debug_info("jingle", "creating content-modify for native candidate %s " \
458 ", remote candidate %s\n", native_candidate_id,
459 remote_candidate_id);
460
461 /* It seems the ID's we get from the Farsight callback is phony... */
462 /*
463 GList *native_candidates =
464 farsight_stream_get_native_candidate(jabber_jingle_session_get_stream(sess),
465 native_candidate_id);
466 purple_debug_info("jingle", "found %d native candidates with id %s\n",
467 g_list_length(native_candidates), native_candidate_id);
468 GList *remote_candidates =
469 jabber_jingle_session_get_remote_candidate(sess, remote_candidate_id);
470 */
471
472 /* we assume lists have length == 1, I think they must... */
473 /*
474 FarsightTransportInfo *native =
475 (FarsightTransportInfo *) native_candidates->data;
476 FarsightTransportInfo *remote =
477 (FarsightTransportInfo *) remote_candidates->data;
478 */
479
480 content = xmlnode_new_child(jingle, "content");
481 xmlnode_set_attrib(content, "creator", "initiator");
482 xmlnode_set_attrib(content, "name", "audio-content");
483 xmlnode_set_attrib(content, "profile", "RTP/AVP");
484
485 description = xmlnode_new_child(content, "description");
486 xmlnode_set_namespace(description, "urn:xmpp:tmp:jingle:apps:audio-rtp");
487
488 payload_type = xmlnode_new_child(description, "payload-type");
489 /* get top codec from codec_intersection to put here... */
490 /* later on this should probably handle changing codec */
491
492 /* Skip creating the actual "content" element for now, since we don't
493 get enough info in the Farsight callback */
494 #if 0
495 transport = xmlnode_new_child(content, "transport");
496 xmlnode_set_namespace(transport, "urn:xmpp:tmp:jingle:transports:ice-tcp");
497
498 candidate = xmlnode_new_child(transport, "candidate");
499
500 g_snprintf(local_port, sizeof(local_port), "%d", native->port);
501 g_snprintf(remote_port, sizeof(remote_port), "%d", remote->port);
502 g_snprintf(prio, sizeof(prio), "%d",
503 jabber_jingle_get_priority(native->type, 0));
504 g_snprintf(component, sizeof(component), "%d", native->component);
505
506 xmlnode_set_attrib(candidate, "component", component);
507 xmlnode_set_attrib(candidate, "foundation", "1"); /* what about this? */
508 xmlnode_set_attrib(candidate, "generation", "0"); /* ? */
509 xmlnode_set_attrib(candidate, "ip", native->ip);
510 xmlnode_set_attrib(candidate, "network", "1"); /* ? */
511 xmlnode_set_attrib(candidate, "port", local_port);
512 xmlnode_set_attrib(candidate, "priority", prio); /* Is this correct? */
513 xmlnode_set_attrib(candidate, "protocol",
514 native->proto == FARSIGHT_NETWORK_PROTOCOL_UDP ?
515 "udp" : "tcp");
516 if (native->username)
517 xmlnode_set_attrib(candidate, "ufrag", native->username);
518 if (native->password)
519 xmlnode_set_attrib(candidate, "pwd", native->password);
520
521 xmlnode_set_attrib(candidate, "type",
522 native->type == FARSIGHT_CANDIDATE_TYPE_LOCAL ?
523 "host" :
524 native->type == FARSIGHT_CANDIDATE_TYPE_DERIVED ?
525 "prflx" :
526 native->type == FARSIGHT_CANDIDATE_TYPE_RELAY ?
527 "relay" : NULL);
528 /* relay */
529 if (native->type == FARSIGHT_CANDIDATE_TYPE_RELAY) {
530 /* set rel-addr and rel-port? How? */
531 }
532
533 xmlnode_set_attrib(candidate, "rem-addr", remote->ip);
534 xmlnode_set_attrib(candidate, "rem-port", remote_port);
535
536 farsight_transport_list_destroy(native_candidates);
537 farsight_transport_list_destroy(remote_candidates);
538
539 #endif /* 0 */
540
541 purple_debug_info("jingle", "End create content modify\n");
542
543 return jingle;
544 }
545
546 xmlnode *
547 jabber_jingle_session_create_content_accept(const JingleSession *sess)
548 {
549 xmlnode *jingle =
550 jabber_jingle_session_create_jingle_element(sess, "content-accept");
551
552 xmlnode *content = xmlnode_new_child(jingle, "content");
553 xmlnode *description = jabber_jingle_session_create_description(sess);
554
555 xmlnode_set_attrib(content, "creator", "initiator");
556 xmlnode_set_attrib(content, "name", "audio-content");
557 xmlnode_set_attrib(content, "profile", "RTP/AVP");
558
559 xmlnode_insert_child(jingle, description);
560
561 return jingle;
562 }
563
564 void
565 jabber_jingle_session_set_remote_codecs(JingleSession *sess, GList *codecs)
566 {
567 if (sess->remote_codecs) {
568 farsight_codec_list_destroy(sess->remote_codecs);
569 }
570 sess->remote_codecs = codecs;
571 }
572
573 GList *
574 jabber_jingle_session_get_remote_codecs(const JingleSession *sess)
575 {
576 return sess->remote_codecs;
577 }
578
579 #endif /* USE_FARSIGHT */