~liuxingcs/+junk/pidgin

« back to all changes in this revision

Viewing changes to libpurple/protocols/jabber/jingle/jingle.c

  • Committer: liuxing
  • Date: 2013-04-25 11:10:17 UTC
  • Revision ID: liuxingcs@yeah.net-20130425111017-fm4mtfsuvhq0dbqd
pidgin

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * @file jingle.c
 
3
 *
 
4
 * purple - Jabber Protocol Plugin
 
5
 *
 
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.
 
9
 *
 
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.
 
14
 *
 
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.
 
19
 *
 
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
 
23
 *
 
24
 */
 
25
 
 
26
#include "internal.h"
 
27
#include "network.h"
 
28
 
 
29
#include "content.h"
 
30
#include "debug.h"
 
31
#include "jingle.h"
 
32
#include "session.h"
 
33
#include "iceudp.h"
 
34
#include "rawudp.h"
 
35
#include "rtp.h"
 
36
 
 
37
#include <string.h>
 
38
#ifdef USE_VV
 
39
#include <gst/gst.h>
 
40
#endif
 
41
 
 
42
GType
 
43
jingle_get_type(const gchar *type)
 
44
{
 
45
        if (type == NULL)
 
46
                return G_TYPE_NONE;
 
47
 
 
48
        if (!strcmp(type, JINGLE_TRANSPORT_RAWUDP))
 
49
                return JINGLE_TYPE_RAWUDP;
 
50
        else if (!strcmp(type, JINGLE_TRANSPORT_ICEUDP))
 
51
                return JINGLE_TYPE_ICEUDP;
 
52
#if 0
 
53
        else if (!strcmp(type, JINGLE_TRANSPORT_SOCKS))
 
54
                return JINGLE_TYPE_SOCKS;
 
55
        else if (!strcmp(type, JINGLE_TRANSPORT_IBB))
 
56
                return JINGLE_TYPE_IBB;
 
57
#endif
 
58
#ifdef USE_VV
 
59
        else if (!strcmp(type, JINGLE_APP_RTP))
 
60
                return JINGLE_TYPE_RTP;
 
61
#endif
 
62
#if 0
 
63
        else if (!strcmp(type, JINGLE_APP_FT))
 
64
                return JINGLE_TYPE_FT;
 
65
        else if (!strcmp(type, JINGLE_APP_XML))
 
66
                return JINGLE_TYPE_XML;
 
67
#endif
 
68
        else
 
69
                return G_TYPE_NONE;
 
70
}
 
71
 
 
72
static void
 
73
jingle_handle_unknown_type(JingleSession *session, xmlnode *jingle)
 
74
{
 
75
        /* Send error */
 
76
}
 
77
 
 
78
static void
 
79
jingle_handle_content_accept(JingleSession *session, xmlnode *jingle)
 
80
{
 
81
        xmlnode *content = xmlnode_get_child(jingle, "content");
 
82
        jabber_iq_send(jingle_session_create_ack(session, jingle));
 
83
 
 
84
        for (; content; content = xmlnode_get_next_twin(content)) {
 
85
                const gchar *name = xmlnode_get_attrib(content, "name");
 
86
                const gchar *creator = xmlnode_get_attrib(content, "creator");
 
87
                jingle_session_accept_content(session, name, creator);
 
88
                /* signal here */
 
89
        }
 
90
}
 
91
 
 
92
static void
 
93
jingle_handle_content_add(JingleSession *session, xmlnode *jingle)
 
94
{
 
95
        xmlnode *content = xmlnode_get_child(jingle, "content");
 
96
        jabber_iq_send(jingle_session_create_ack(session, jingle));
 
97
 
 
98
        for (; content; content = xmlnode_get_next_twin(content)) {
 
99
                JingleContent *pending_content =
 
100
                                jingle_content_parse(content);
 
101
                if (pending_content == NULL) {
 
102
                        purple_debug_error("jingle",
 
103
                                        "Error parsing \"content-add\" content.\n");
 
104
                        jabber_iq_send(jingle_session_terminate_packet(session,
 
105
                                "unsupported-applications"));
 
106
                } else {
 
107
                        jingle_session_add_pending_content(session,
 
108
                                        pending_content);
 
109
                }
 
110
        }
 
111
 
 
112
        /* XXX: signal here */
 
113
}
 
114
 
 
115
static void
 
116
jingle_handle_content_modify(JingleSession *session, xmlnode *jingle)
 
117
{
 
118
        xmlnode *content = xmlnode_get_child(jingle, "content");
 
119
        jabber_iq_send(jingle_session_create_ack(session, jingle));
 
120
 
 
121
        for (; content; content = xmlnode_get_next_twin(content)) {
 
122
                const gchar *name = xmlnode_get_attrib(content, "name");
 
123
                const gchar *creator = xmlnode_get_attrib(content, "creator");
 
124
                JingleContent *local_content = jingle_session_find_content(session, name, creator);
 
125
 
 
126
                if (local_content != NULL) {
 
127
                        const gchar *senders = xmlnode_get_attrib(content, "senders");
 
128
                        gchar *local_senders = jingle_content_get_senders(local_content);
 
129
                        if (!purple_strequal(senders, local_senders))
 
130
                                jingle_content_modify(local_content, senders);
 
131
                        g_free(local_senders);
 
132
                } else {
 
133
                        purple_debug_error("jingle", "content_modify: unknown content\n");
 
134
                        jabber_iq_send(jingle_session_terminate_packet(session,
 
135
                                "unknown-applications"));
 
136
                }
 
137
        }
 
138
}
 
139
 
 
140
static void
 
141
jingle_handle_content_reject(JingleSession *session, xmlnode *jingle)
 
142
{
 
143
        xmlnode *content = xmlnode_get_child(jingle, "content");
 
144
        jabber_iq_send(jingle_session_create_ack(session, jingle));
 
145
 
 
146
        for (; content; content = xmlnode_get_next_twin(content)) {
 
147
                const gchar *name = xmlnode_get_attrib(content, "name");
 
148
                const gchar *creator = xmlnode_get_attrib(content, "creator");
 
149
                jingle_session_remove_pending_content(session, name, creator);
 
150
                /* signal here */
 
151
        }
 
152
}
 
153
 
 
154
static void
 
155
jingle_handle_content_remove(JingleSession *session, xmlnode *jingle)
 
156
{
 
157
        xmlnode *content = xmlnode_get_child(jingle, "content");
 
158
 
 
159
        jabber_iq_send(jingle_session_create_ack(session, jingle));
 
160
 
 
161
        for (; content; content = xmlnode_get_next_twin(content)) {
 
162
                const gchar *name = xmlnode_get_attrib(content, "name");
 
163
                const gchar *creator = xmlnode_get_attrib(content, "creator");
 
164
                jingle_session_remove_content(session, name, creator);
 
165
        }
 
166
}
 
167
 
 
168
static void
 
169
jingle_handle_description_info(JingleSession *session, xmlnode *jingle)
 
170
{
 
171
        xmlnode *content = xmlnode_get_child(jingle, "content");
 
172
 
 
173
        jabber_iq_send(jingle_session_create_ack(session, jingle));
 
174
 
 
175
        jingle_session_accept_session(session);
 
176
 
 
177
        for (; content; content = xmlnode_get_next_twin(content)) {
 
178
                const gchar *name = xmlnode_get_attrib(content, "name");
 
179
                const gchar *creator = xmlnode_get_attrib(content, "creator");
 
180
                JingleContent *parsed_content =
 
181
                                jingle_session_find_content(session, name, creator);
 
182
                if (parsed_content == NULL) {
 
183
                        purple_debug_error("jingle", "Error parsing content\n");
 
184
                        jabber_iq_send(jingle_session_terminate_packet(session,
 
185
                                "unsupported-applications"));
 
186
                } else {
 
187
                        jingle_content_handle_action(parsed_content, content,
 
188
                                        JINGLE_DESCRIPTION_INFO);
 
189
                }
 
190
        }
 
191
}
 
192
 
 
193
static void
 
194
jingle_handle_security_info(JingleSession *session, xmlnode *jingle)
 
195
{
 
196
        jabber_iq_send(jingle_session_create_ack(session, jingle));
 
197
}
 
198
 
 
199
static void
 
200
jingle_handle_session_accept(JingleSession *session, xmlnode *jingle)
 
201
{
 
202
        xmlnode *content = xmlnode_get_child(jingle, "content");
 
203
 
 
204
        jabber_iq_send(jingle_session_create_ack(session, jingle));
 
205
 
 
206
        jingle_session_accept_session(session);
 
207
 
 
208
        for (; content; content = xmlnode_get_next_twin(content)) {
 
209
                const gchar *name = xmlnode_get_attrib(content, "name");
 
210
                const gchar *creator = xmlnode_get_attrib(content, "creator");
 
211
                JingleContent *parsed_content =
 
212
                                jingle_session_find_content(session, name, creator);
 
213
                if (parsed_content == NULL) {
 
214
                        purple_debug_error("jingle", "Error parsing content\n");
 
215
                        jabber_iq_send(jingle_session_terminate_packet(session,
 
216
                                "unsupported-applications"));
 
217
                } else {
 
218
                        jingle_content_handle_action(parsed_content, content,
 
219
                                        JINGLE_SESSION_ACCEPT);
 
220
                }
 
221
        }
 
222
}
 
223
 
 
224
static void
 
225
jingle_handle_session_info(JingleSession *session, xmlnode *jingle)
 
226
{
 
227
        jabber_iq_send(jingle_session_create_ack(session, jingle));
 
228
        /* XXX: call signal */
 
229
}
 
230
 
 
231
static void
 
232
jingle_handle_session_initiate(JingleSession *session, xmlnode *jingle)
 
233
{
 
234
        xmlnode *content = xmlnode_get_child(jingle, "content");
 
235
 
 
236
        for (; content; content = xmlnode_get_next_twin(content)) {
 
237
                JingleContent *parsed_content = jingle_content_parse(content);
 
238
                if (parsed_content == NULL) {
 
239
                        purple_debug_error("jingle", "Error parsing content\n");
 
240
                        jabber_iq_send(jingle_session_terminate_packet(session,
 
241
                                "unsupported-applications"));
 
242
                } else {
 
243
                        jingle_session_add_content(session, parsed_content);
 
244
                        jingle_content_handle_action(parsed_content, content,
 
245
                                        JINGLE_SESSION_INITIATE);
 
246
                }
 
247
        }
 
248
 
 
249
        jabber_iq_send(jingle_session_create_ack(session, jingle));
 
250
}
 
251
 
 
252
static void
 
253
jingle_handle_session_terminate(JingleSession *session, xmlnode *jingle)
 
254
{
 
255
        jabber_iq_send(jingle_session_create_ack(session, jingle));
 
256
 
 
257
        jingle_session_handle_action(session, jingle,
 
258
                        JINGLE_SESSION_TERMINATE);
 
259
        /* display reason? */
 
260
        g_object_unref(session);
 
261
}
 
262
 
 
263
static void
 
264
jingle_handle_transport_accept(JingleSession *session, xmlnode *jingle)
 
265
{
 
266
        xmlnode *content = xmlnode_get_child(jingle, "content");
 
267
 
 
268
        jabber_iq_send(jingle_session_create_ack(session, jingle));
 
269
 
 
270
        for (; content; content = xmlnode_get_next_twin(content)) {
 
271
                const gchar *name = xmlnode_get_attrib(content, "name");
 
272
                const gchar *creator = xmlnode_get_attrib(content, "creator");
 
273
                JingleContent *content = jingle_session_find_content(session, name, creator);
 
274
                jingle_content_accept_transport(content);
 
275
        }
 
276
}
 
277
 
 
278
static void
 
279
jingle_handle_transport_info(JingleSession *session, xmlnode *jingle)
 
280
{
 
281
        xmlnode *content = xmlnode_get_child(jingle, "content");
 
282
 
 
283
        jabber_iq_send(jingle_session_create_ack(session, jingle));
 
284
 
 
285
        for (; content; content = xmlnode_get_next_twin(content)) {
 
286
                const gchar *name = xmlnode_get_attrib(content, "name");
 
287
                const gchar *creator = xmlnode_get_attrib(content, "creator");
 
288
                JingleContent *parsed_content =
 
289
                                jingle_session_find_content(session, name, creator);
 
290
                if (parsed_content == NULL) {
 
291
                        purple_debug_error("jingle", "Error parsing content\n");
 
292
                        jabber_iq_send(jingle_session_terminate_packet(session,
 
293
                                "unsupported-applications"));
 
294
                } else {
 
295
                        jingle_content_handle_action(parsed_content, content,
 
296
                                        JINGLE_TRANSPORT_INFO);
 
297
                }
 
298
        }
 
299
}
 
300
 
 
301
static void
 
302
jingle_handle_transport_reject(JingleSession *session, xmlnode *jingle)
 
303
{
 
304
        xmlnode *content = xmlnode_get_child(jingle, "content");
 
305
 
 
306
        jabber_iq_send(jingle_session_create_ack(session, jingle));
 
307
 
 
308
        for (; content; content = xmlnode_get_next_twin(content)) {
 
309
                const gchar *name = xmlnode_get_attrib(content, "name");
 
310
                const gchar *creator = xmlnode_get_attrib(content, "creator");
 
311
                JingleContent *content = jingle_session_find_content(session, name, creator);
 
312
                jingle_content_remove_pending_transport(content);
 
313
        }
 
314
}
 
315
 
 
316
static void
 
317
jingle_handle_transport_replace(JingleSession *session, xmlnode *jingle)
 
318
{
 
319
        xmlnode *content = xmlnode_get_child(jingle, "content");
 
320
 
 
321
        jabber_iq_send(jingle_session_create_ack(session, jingle));
 
322
 
 
323
        for (; content; content = xmlnode_get_next_twin(content)) {
 
324
                const gchar *name = xmlnode_get_attrib(content, "name");
 
325
                const gchar *creator = xmlnode_get_attrib(content, "creator");
 
326
                xmlnode *xmltransport = xmlnode_get_child(content, "transport");
 
327
                JingleTransport *transport = jingle_transport_parse(xmltransport);
 
328
                JingleContent *content = jingle_session_find_content(session, name, creator);
 
329
 
 
330
                jingle_content_set_pending_transport(content, transport);
 
331
        }
 
332
}
 
333
 
 
334
typedef struct {
 
335
        const char *name;
 
336
        void (*handler)(JingleSession*, xmlnode*);
 
337
} JingleAction;
 
338
 
 
339
static const JingleAction jingle_actions[] = {
 
340
        {"unknown-type",        jingle_handle_unknown_type},
 
341
        {"content-accept",      jingle_handle_content_accept},
 
342
        {"content-add",         jingle_handle_content_add},
 
343
        {"content-modify",      jingle_handle_content_modify},
 
344
        {"content-reject",      jingle_handle_content_reject},
 
345
        {"content-remove",      jingle_handle_content_remove},
 
346
        {"description-info",    jingle_handle_description_info},
 
347
        {"security-info",       jingle_handle_security_info},
 
348
        {"session-accept",      jingle_handle_session_accept},
 
349
        {"session-info",        jingle_handle_session_info},
 
350
        {"session-initiate",    jingle_handle_session_initiate},
 
351
        {"session-terminate",   jingle_handle_session_terminate},
 
352
        {"transport-accept",    jingle_handle_transport_accept},
 
353
        {"transport-info",      jingle_handle_transport_info},
 
354
        {"transport-reject",    jingle_handle_transport_reject},
 
355
        {"transport-replace",   jingle_handle_transport_replace},
 
356
};
 
357
 
 
358
const gchar *
 
359
jingle_get_action_name(JingleActionType action)
 
360
{
 
361
        return jingle_actions[action].name;
 
362
}
 
363
 
 
364
JingleActionType
 
365
jingle_get_action_type(const gchar *action)
 
366
{
 
367
        static const int num_actions =
 
368
                        sizeof(jingle_actions)/sizeof(JingleAction);
 
369
        /* Start at 1 to skip the unknown-action type */
 
370
        int i = 1;
 
371
        for (; i < num_actions; ++i) {
 
372
                if (!strcmp(action, jingle_actions[i].name))
 
373
                        return i;
 
374
        }
 
375
        return JINGLE_UNKNOWN_TYPE;
 
376
}
 
377
 
 
378
void
 
379
jingle_parse(JabberStream *js, const char *from, JabberIqType type,
 
380
             const char *id, xmlnode *jingle)
 
381
{
 
382
        const gchar *action;
 
383
        const gchar *sid;
 
384
        JingleActionType action_type;
 
385
        JingleSession *session;
 
386
 
 
387
        if (type != JABBER_IQ_SET) {
 
388
                /* TODO: send iq error here */
 
389
                return;
 
390
        }
 
391
 
 
392
        if (!(action = xmlnode_get_attrib(jingle, "action"))) {
 
393
                /* TODO: send iq error here */
 
394
                return;
 
395
        }
 
396
 
 
397
        action_type = jingle_get_action_type(action);
 
398
 
 
399
        purple_debug_info("jabber", "got Jingle package action = %s\n",
 
400
                          action);
 
401
 
 
402
        if (!(sid = xmlnode_get_attrib(jingle, "sid"))) {
 
403
                /* send iq error here */
 
404
                return;
 
405
        }
 
406
 
 
407
        if (!(session = jingle_session_find_by_sid(js, sid))
 
408
                        && strcmp(action, "session-initiate")) {
 
409
                purple_debug_error("jingle", "jabber_jingle_session_parse couldn't find session\n");
 
410
                /* send iq error here */
 
411
                return;
 
412
        }
 
413
 
 
414
        if (action_type == JINGLE_SESSION_INITIATE) {
 
415
                if (session) {
 
416
                        /* This should only happen if you start a session with yourself */
 
417
                        purple_debug_error("jingle", "Jingle session with "
 
418
                                        "id={%s} already exists\n", sid);
 
419
                        /* send iq error */
 
420
                        return;
 
421
                } else {
 
422
                        char *own_jid = g_strdup_printf("%s@%s/%s", js->user->node,
 
423
                                        js->user->domain, js->user->resource);
 
424
                        session = jingle_session_create(js, sid, own_jid, from, FALSE);
 
425
                        g_free(own_jid);
 
426
                }
 
427
        }
 
428
 
 
429
        jingle_actions[action_type].handler(session, jingle);
 
430
}
 
431
 
 
432
static void
 
433
jingle_terminate_sessions_gh(gpointer key, gpointer value, gpointer user_data)
 
434
{
 
435
        g_object_unref(value);
 
436
}
 
437
 
 
438
void
 
439
jingle_terminate_sessions(JabberStream *js)
 
440
{
 
441
        if (js->sessions)
 
442
                g_hash_table_foreach(js->sessions,
 
443
                                jingle_terminate_sessions_gh, NULL);
 
444
}
 
445
 
 
446
#ifdef USE_VV
 
447
static GValueArray *
 
448
jingle_create_relay_info(const gchar *ip, guint port, const gchar *username,
 
449
        const gchar *password, const gchar *relay_type, GValueArray *relay_info)
 
450
{
 
451
        GValue value;
 
452
        GstStructure *turn_setup = gst_structure_new("relay-info",
 
453
                "ip", G_TYPE_STRING, ip,
 
454
                "port", G_TYPE_UINT, port,
 
455
                "username", G_TYPE_STRING, username,
 
456
                "password", G_TYPE_STRING, password,
 
457
                "relay-type", G_TYPE_STRING, relay_type,
 
458
                NULL);
 
459
        purple_debug_info("jabber", "created gst_structure %" GST_PTR_FORMAT "\n",
 
460
                turn_setup);
 
461
        if (turn_setup) {
 
462
                memset(&value, 0, sizeof(GValue));
 
463
                g_value_init(&value, GST_TYPE_STRUCTURE);
 
464
                gst_value_set_structure(&value, turn_setup);
 
465
                relay_info = g_value_array_append(relay_info, &value);
 
466
                gst_structure_free(turn_setup);
 
467
        }
 
468
        return relay_info;
 
469
}
 
470
 
 
471
GParameter *
 
472
jingle_get_params(JabberStream *js, const gchar *relay_ip, guint relay_udp,
 
473
        guint relay_tcp, guint relay_ssltcp, const gchar *relay_username,
 
474
    const gchar *relay_password, guint *num)
 
475
{
 
476
        /* don't set a STUN server if one is set globally in prefs, in that case
 
477
         this will be handled in media.c */
 
478
        gboolean has_account_stun = js->stun_ip && !purple_network_get_stun_ip();
 
479
        guint num_params = has_account_stun ?
 
480
                (relay_ip ? 3 : 2) : (relay_ip ? 1 : 0);
 
481
        GParameter *params = NULL;
 
482
        int next_index = 0;
 
483
 
 
484
        if (num_params > 0) {
 
485
                params = g_new0(GParameter, num_params);
 
486
 
 
487
                if (has_account_stun) {
 
488
                        purple_debug_info("jabber",
 
489
                                "setting param stun-ip for stream using Google auto-config: %s\n",
 
490
                                js->stun_ip);
 
491
                        params[next_index].name = "stun-ip";
 
492
                        g_value_init(&params[next_index].value, G_TYPE_STRING);
 
493
                        g_value_set_string(&params[next_index].value, js->stun_ip);
 
494
                        purple_debug_info("jabber",
 
495
                                "setting param stun-port for stream using Google auto-config: %d\n",
 
496
                                js->stun_port);
 
497
                        next_index++;
 
498
                        params[next_index].name = "stun-port";
 
499
                        g_value_init(&params[next_index].value, G_TYPE_UINT);
 
500
                        g_value_set_uint(&params[next_index].value, js->stun_port);
 
501
                        next_index++;
 
502
                }
 
503
 
 
504
                if (relay_ip) {
 
505
                        GValueArray *relay_info = g_value_array_new(0);
 
506
 
 
507
                        if (relay_udp) {
 
508
                                relay_info =
 
509
                                        jingle_create_relay_info(relay_ip, relay_udp, relay_username,
 
510
                                                relay_password, "udp", relay_info);
 
511
                        }
 
512
                        if (relay_tcp) {
 
513
                                relay_info =
 
514
                                        jingle_create_relay_info(relay_ip, relay_tcp, relay_username,
 
515
                                                relay_password, "tcp", relay_info);
 
516
                        }
 
517
                        if (relay_ssltcp) {
 
518
                                relay_info =
 
519
                                        jingle_create_relay_info(relay_ip, relay_ssltcp, relay_username,
 
520
                                                relay_password, "tls", relay_info);
 
521
                        }
 
522
                        params[next_index].name = "relay-info";
 
523
                        g_value_init(&params[next_index].value, G_TYPE_VALUE_ARRAY);
 
524
                        g_value_set_boxed(&params[next_index].value, relay_info);
 
525
                        g_value_array_free(relay_info);
 
526
                }
 
527
        }
 
528
 
 
529
        *num = num_params;
 
530
        return params;
 
531
}
 
532
#endif
 
533