Statistics
| Branch: | Revision:

janus-gateway / janus.c @ f82a8605

History | View | Annotate | Download (159 KB)

1
/*! \file   janus.c
2
 * \author Lorenzo Miniero <lorenzo@meetecho.com>
3
 * \copyright GNU General Public License v3
4
 * \brief  Janus core
5
 * \details Implementation of the gateway core. This code takes care of
6
 * the gateway initialization (command line/configuration) and setup,
7
 * and makes use of the available transport plugins (by default HTTP,
8
 * WebSockets, RabbitMQ, if compiled) and Janus protocol (a JSON-based
9
 * protocol) to interact with the applications, whether they're web based
10
 * or not. The core also takes care of bridging peers and plugins
11
 * accordingly, in terms of both messaging and real-time media transfer
12
 * via WebRTC. 
13
 * 
14
 * \ingroup core
15
 * \ref core
16
 */
17
 
18
#include <dlfcn.h>
19
#include <dirent.h>
20
#include <net/if.h>
21
#include <netdb.h>
22
#include <signal.h>
23
#include <getopt.h>
24
#include <sys/resource.h>
25
#include <sys/stat.h>
26

    
27
#include "janus.h"
28
#include "cmdline.h"
29
#include "config.h"
30
#include "apierror.h"
31
#include "log.h"
32
#include "debug.h"
33
#include "rtcp.h"
34
#include "sdp.h"
35
#include "auth.h"
36
#include "utils.h"
37

    
38

    
39
#define JANUS_NAME                                "Janus WebRTC Gateway"
40
#define JANUS_AUTHOR                        "Meetecho s.r.l."
41
#define JANUS_VERSION                        10
42
#define JANUS_VERSION_STRING        "0.1.0"
43

    
44
#ifdef __MACH__
45
#define SHLIB_EXT "0.dylib"
46
#else
47
#define SHLIB_EXT ".so"
48
#endif
49

    
50

    
51
static janus_config *config = NULL;
52
static char *config_file = NULL;
53
static char *configs_folder = NULL;
54

    
55
static GHashTable *transports = NULL;
56
static GHashTable *transports_so = NULL;
57

    
58
static GHashTable *plugins = NULL;
59
static GHashTable *plugins_so = NULL;
60

    
61

    
62
/* Certificates */
63
static char *server_pem = NULL;
64
gchar *janus_get_server_pem(void) {
65
        return server_pem;
66
}
67
static char *server_key = NULL;
68
gchar *janus_get_server_key(void) {
69
        return server_key;
70
}
71

    
72

    
73
/* API secrets */
74
static char *api_secret = NULL, *admin_api_secret = NULL;
75

    
76

    
77
/* Admin/Monitor helpers */
78
json_t *janus_admin_stream_summary(janus_ice_stream *stream);
79
json_t *janus_admin_component_summary(janus_ice_component *component);
80

    
81

    
82
/* IP addresses */
83
static gchar local_ip[INET6_ADDRSTRLEN];
84
gchar *janus_get_local_ip(void) {
85
        return local_ip;
86
}
87
static gchar *public_ip = NULL;
88
gchar *janus_get_public_ip(void) {
89
        /* Fallback to the local IP, if we have no public one */
90
        return public_ip ? public_ip : local_ip;
91
}
92
void janus_set_public_ip(const char *ip) {
93
        /* once set do not override */
94
        if(ip == NULL || public_ip != NULL)
95
                return;
96
        public_ip = g_strdup(ip);
97
}
98
static volatile gint stop = 0;
99
gint janus_is_stopping(void) {
100
        return g_atomic_int_get(&stop);
101
}
102

    
103

    
104
/* Information */
105
json_t *janus_info(const char *transaction);
106
json_t *janus_info(const char *transaction) {
107
        /* Prepare a summary on the gateway */
108
        json_t *info = json_object();
109
        json_object_set_new(info, "janus", json_string("server_info"));
110
        if(transaction != NULL)
111
                json_object_set_new(info, "transaction", json_string(transaction));
112
        json_object_set_new(info, "name", json_string(JANUS_NAME));
113
        json_object_set_new(info, "version", json_integer(JANUS_VERSION));
114
        json_object_set_new(info, "version_string", json_string(JANUS_VERSION_STRING));
115
        json_object_set_new(info, "author", json_string(JANUS_AUTHOR));
116
        json_object_set_new(info, "log-to-stdout", json_string(janus_log_is_stdout_enabled() ? "true" : "false"));
117
        json_object_set_new(info, "log-to-file", json_string(janus_log_is_logfile_enabled() ? "true" : "false"));
118
        if(janus_log_is_logfile_enabled())
119
                json_object_set_new(info, "log-path", json_string(janus_log_get_logfile_path()));
120
#ifdef HAVE_SCTP
121
        json_object_set_new(info, "data_channels", json_string("true"));
122
#else
123
        json_object_set_new(info, "data_channels", json_string("false"));
124
#endif
125
        json_object_set_new(info, "local-ip", json_string(local_ip));
126
        if(public_ip != NULL)
127
                json_object_set_new(info, "public-ip", json_string(public_ip));
128
        json_object_set_new(info, "ipv6", json_string(janus_ice_is_ipv6_enabled() ? "true" : "false"));
129
        json_object_set_new(info, "ice-tcp", json_string(janus_ice_is_ice_tcp_enabled() ? "true" : "false"));
130
        if(janus_ice_get_stun_server() != NULL) {
131
                char server[255];
132
                g_snprintf(server, 255, "%s:%"SCNu16, janus_ice_get_stun_server(), janus_ice_get_stun_port());
133
                json_object_set_new(info, "stun-server", json_string(server));
134
        }
135
        if(janus_ice_get_turn_server() != NULL) {
136
                char server[255];
137
                g_snprintf(server, 255, "%s:%"SCNu16, janus_ice_get_turn_server(), janus_ice_get_turn_port());
138
                json_object_set_new(info, "turn-server", json_string(server));
139
        }
140
        json_object_set_new(info, "api_secret", json_string(api_secret != NULL ? "true" : "false"));
141
        json_object_set_new(info, "auth_token", json_string(janus_auth_is_enabled() ? "true" : "false"));
142
        /* Available transports */
143
        json_t *t_data = json_object();
144
        if(transports && g_hash_table_size(transports) > 0) {
145
                GHashTableIter iter;
146
                gpointer value;
147
                g_hash_table_iter_init(&iter, transports);
148
                while (g_hash_table_iter_next(&iter, NULL, &value)) {
149
                        janus_transport *t = value;
150
                        if(t == NULL) {
151
                                continue;
152
                        }
153
                        json_t *transport = json_object();
154
                        json_object_set_new(transport, "name", json_string(t->get_name()));
155
                        json_object_set_new(transport, "author", json_string(t->get_author()));
156
                        json_object_set_new(transport, "description", json_string(t->get_description()));
157
                        json_object_set_new(transport, "version_string", json_string(t->get_version_string()));
158
                        json_object_set_new(transport, "version", json_integer(t->get_version()));
159
                        json_object_set_new(t_data, t->get_package(), transport);
160
                }
161
        }
162
        json_object_set_new(info, "transports", t_data);
163
        /* Available plugins */
164
        json_t *p_data = json_object();
165
        if(plugins && g_hash_table_size(plugins) > 0) {
166
                GHashTableIter iter;
167
                gpointer value;
168
                g_hash_table_iter_init(&iter, plugins);
169
                while (g_hash_table_iter_next(&iter, NULL, &value)) {
170
                        janus_plugin *p = value;
171
                        if(p == NULL) {
172
                                continue;
173
                        }
174
                        json_t *plugin = json_object();
175
                        json_object_set_new(plugin, "name", json_string(p->get_name()));
176
                        json_object_set_new(plugin, "author", json_string(p->get_author()));
177
                        json_object_set_new(plugin, "description", json_string(p->get_description()));
178
                        json_object_set_new(plugin, "version_string", json_string(p->get_version_string()));
179
                        json_object_set_new(plugin, "version", json_integer(p->get_version()));
180
                        json_object_set_new(p_data, p->get_package(), plugin);
181
                }
182
        }
183
        json_object_set_new(info, "plugins", p_data);
184
        
185
        return info;
186
}
187

    
188

    
189
/* Logging */
190
int janus_log_level = 0;
191
gboolean janus_log_timestamps = FALSE;
192
gboolean janus_log_colors = FALSE;
193
int lock_debug = 0;
194

    
195

    
196
/*! \brief Signal handler (just used to intercept CTRL+C) */
197
void janus_handle_signal(int signum);
198
void janus_handle_signal(int signum)
199
{
200
        switch(g_atomic_int_get(&stop)) {
201
                case 0:
202
                        JANUS_PRINT("Stopping gateway, please wait...\n");
203
                        break;
204
                case 1:
205
                        JANUS_PRINT("In a hurry? I'm trying to free resources cleanly, here!\n");
206
                        break;
207
                default:
208
                        JANUS_PRINT("Ok, leaving immediately...\n");
209
                        break;
210
        }
211
        g_atomic_int_inc(&stop);
212
        if(g_atomic_int_get(&stop) > 2)
213
                exit(1);
214
}
215

    
216

    
217
/** @name Transport plugin callback interface
218
 * These are the callbacks implemented by the gateway core, as part of
219
 * the janus_transport_callbacks interface. Everything the transport
220
 * plugins send the gateway is handled here.
221
 */
222
///@{
223
void janus_transport_incoming_request(janus_transport *plugin, void *transport, void *request_id, gboolean admin, json_t *message, json_error_t *error);
224
void janus_transport_gone(janus_transport *plugin, void *transport);
225
gboolean janus_transport_is_api_secret_needed(janus_transport *plugin);
226
gboolean janus_transport_is_api_secret_valid(janus_transport *plugin, const char *apisecret);
227
gboolean janus_transport_is_auth_token_needed(janus_transport *plugin);
228
gboolean janus_transport_is_auth_token_valid(janus_transport *plugin, const char *token);
229

    
230
static janus_transport_callbacks janus_handler_transport =
231
        {
232
                .incoming_request = janus_transport_incoming_request,
233
                .transport_gone = janus_transport_gone,
234
                .is_api_secret_needed = janus_transport_is_api_secret_needed,
235
                .is_api_secret_valid = janus_transport_is_api_secret_valid,
236
                .is_auth_token_needed = janus_transport_is_auth_token_needed,
237
                .is_auth_token_valid = janus_transport_is_auth_token_valid,
238
        }; 
239
GThreadPool *tasks = NULL;
240
void janus_transport_task(gpointer data, gpointer user_data);
241
///@}
242

    
243

    
244
/** @name Plugin callback interface
245
 * These are the callbacks implemented by the gateway core, as part of
246
 * the janus_callbacks interface. Everything the plugins send the
247
 * gateway is handled here.
248
 */
249
///@{
250
int janus_plugin_push_event(janus_plugin_session *plugin_session, janus_plugin *plugin, const char *transaction, const char *message, const char *sdp_type, const char *sdp);
251
json_t *janus_plugin_handle_sdp(janus_plugin_session *plugin_session, janus_plugin *plugin, const char *sdp_type, const char *sdp);
252
void janus_plugin_relay_rtp(janus_plugin_session *plugin_session, int video, char *buf, int len);
253
void janus_plugin_relay_rtcp(janus_plugin_session *plugin_session, int video, char *buf, int len);
254
void janus_plugin_relay_data(janus_plugin_session *plugin_session, char *buf, int len);
255
void janus_plugin_close_pc(janus_plugin_session *plugin_session);
256
void janus_plugin_end_session(janus_plugin_session *plugin_session);
257
static janus_callbacks janus_handler_plugin =
258
        {
259
                .push_event = janus_plugin_push_event,
260
                .relay_rtp = janus_plugin_relay_rtp,
261
                .relay_rtcp = janus_plugin_relay_rtcp,
262
                .relay_data = janus_plugin_relay_data,
263
                .close_pc = janus_plugin_close_pc,
264
                .end_session = janus_plugin_end_session,
265
        }; 
266
///@}
267

    
268

    
269
/* Gateway Sessions */
270
static janus_mutex sessions_mutex;
271
static GHashTable *sessions = NULL, *old_sessions = NULL;
272
static GMainContext *sessions_watchdog_context = NULL;
273

    
274

    
275
#define SESSION_TIMEOUT                60                /* FIXME Should this be higher, e.g., 120 seconds? */
276

    
277
static gboolean janus_cleanup_session(gpointer user_data) {
278
        janus_session *session = (janus_session *) user_data;
279

    
280
        JANUS_LOG(LOG_DBG, "Cleaning up session %"SCNu64"...\n", session->session_id);
281
        janus_session_destroy(session->session_id);
282

    
283
        return G_SOURCE_REMOVE;
284
}
285

    
286
static gboolean janus_check_sessions(gpointer user_data) {
287
        GMainContext *watchdog_context = (GMainContext *) user_data;
288
        janus_mutex_lock(&sessions_mutex);
289
        if(sessions && g_hash_table_size(sessions) > 0) {
290
                GHashTableIter iter;
291
                gpointer value;
292
                g_hash_table_iter_init(&iter, sessions);
293
                while (g_hash_table_iter_next(&iter, NULL, &value)) {
294
                        janus_session *session = (janus_session *) value;
295
                        if (!session || session->destroy) {
296
                                continue;
297
                        }
298
                        gint64 now = janus_get_monotonic_time();
299
                        if (now - session->last_activity >= SESSION_TIMEOUT * G_USEC_PER_SEC && !session->timeout) {
300
                                JANUS_LOG(LOG_INFO, "Timeout expired for session %"SCNu64"...\n", session->session_id);
301

    
302
                                /* Notify the transport */
303
                                if(session->source) {
304
                                        json_t *event = json_object();
305
                                        json_object_set_new(event, "janus", json_string("timeout"));
306
                                        json_object_set_new(event, "session_id", json_integer(session->session_id));
307
                                        /* Send this to the transport client */
308
                                        session->source->transport->send_message(session->source->instance, NULL, FALSE, event);
309
                                        /* Notify the transport plugin about the session timeout */
310
                                        session->source->transport->session_over(session->source->instance, session->session_id, TRUE);
311
                                }
312
                                
313
                                /* Mark the session as over, we'll deal with it later */
314
                                session->timeout = 1;
315
                                /* FIXME Is this safe? apparently it causes hash table errors on the console */
316
                                g_hash_table_iter_remove(&iter);
317
                                g_hash_table_insert(old_sessions, GUINT_TO_POINTER(session->session_id), session);
318

    
319
                                /* Schedule the session for deletion */
320
                                GSource *timeout_source = g_timeout_source_new_seconds(3);
321
                                g_source_set_callback(timeout_source, janus_cleanup_session, session, NULL);
322
                                g_source_attach(timeout_source, watchdog_context);
323
                                g_source_unref(timeout_source);
324
                        }
325
                }
326
        }
327
        janus_mutex_unlock(&sessions_mutex);
328

    
329
        return G_SOURCE_CONTINUE;
330
}
331

    
332
static gpointer janus_sessions_watchdog(gpointer user_data) {
333
        GMainLoop *loop = (GMainLoop *) user_data;
334
        GMainContext *watchdog_context = g_main_loop_get_context(loop);
335
        GSource *timeout_source;
336

    
337
        timeout_source = g_timeout_source_new_seconds(2);
338
        g_source_set_callback(timeout_source, janus_check_sessions, watchdog_context, NULL);
339
        g_source_attach(timeout_source, watchdog_context);
340
        g_source_unref(timeout_source);
341

    
342
        JANUS_LOG(LOG_INFO, "Sessions watchdog started\n");
343

    
344
        g_main_loop_run(loop);
345

    
346
        return NULL;
347
}
348

    
349
janus_session *janus_session_create(guint64 session_id) {
350
        if(session_id == 0) {
351
                while(session_id == 0) {
352
                        session_id = g_random_int();
353
                        if(janus_session_find(session_id) != NULL) {
354
                                /* Session ID already taken, try another one */
355
                                session_id = 0;
356
                        }
357
                }
358
        }
359
        JANUS_LOG(LOG_INFO, "Creating new session: %"SCNu64"\n", session_id);
360
        janus_session *session = (janus_session *)g_malloc0(sizeof(janus_session));
361
        if(session == NULL) {
362
                JANUS_LOG(LOG_FATAL, "Memory error!\n");
363
                return NULL;
364
        }
365
        session->session_id = session_id;
366
        session->source = NULL;
367
        session->destroy = 0;
368
        session->timeout = 0;
369
        session->last_activity = janus_get_monotonic_time();
370
        janus_mutex_init(&session->mutex);
371
        janus_mutex_lock(&sessions_mutex);
372
        g_hash_table_insert(sessions, GUINT_TO_POINTER(session_id), session);
373
        janus_mutex_unlock(&sessions_mutex);
374
        return session;
375
}
376

    
377
janus_session *janus_session_find(guint64 session_id) {
378
        janus_mutex_lock(&sessions_mutex);
379
        janus_session *session = g_hash_table_lookup(sessions, GUINT_TO_POINTER(session_id));
380
        janus_mutex_unlock(&sessions_mutex);
381
        return session;
382
}
383

    
384
janus_session *janus_session_find_destroyed(guint64 session_id) {
385
        janus_mutex_lock(&sessions_mutex);
386
        janus_session *session = g_hash_table_lookup(old_sessions, GUINT_TO_POINTER(session_id));
387
        janus_mutex_unlock(&sessions_mutex);
388
        return session;
389
}
390

    
391
void janus_session_notify_event(guint64 session_id, json_t *event) {
392
        janus_mutex_lock(&sessions_mutex);
393
        janus_session *session = g_hash_table_lookup(sessions, GUINT_TO_POINTER(session_id));
394
        janus_mutex_unlock(&sessions_mutex);
395
        if(session != NULL && session->source != NULL && session->source->transport != NULL) {
396
                /* Send this to the transport client */
397
                JANUS_LOG(LOG_HUGE, "Sending event to %s (%p)\n", session->source->transport->get_package(), session->source->instance);
398
                session->source->transport->send_message(session->source->instance, NULL, FALSE, event);
399
        } else {
400
                /* No transport, free the event */
401
                json_decref(event);
402
        }
403
}
404

    
405

    
406
/* Destroys a session but does not remove it from the sessions hash table. */
407
gint janus_session_destroy(guint64 session_id) {
408
        janus_session *session = janus_session_find_destroyed(session_id);
409
        if(session == NULL) {
410
                JANUS_LOG(LOG_ERR, "Couldn't find session to destroy: %"SCNu64"\n", session_id);
411
                return -1;
412
        }
413
        JANUS_LOG(LOG_VERB, "Destroying session %"SCNu64"\n", session_id);
414
        session->destroy = 1;
415
        if (session->ice_handles != NULL && g_hash_table_size(session->ice_handles) > 0) {
416
                GHashTableIter iter;
417
                gpointer value;
418
                /* Remove all handles */
419
                g_hash_table_iter_init(&iter, session->ice_handles);
420
                while (g_hash_table_iter_next(&iter, NULL, &value)) {
421
                        janus_ice_handle *handle = value;
422
                        if(!handle || g_atomic_int_get(&stop)) {
423
                                continue;
424
                        }
425
                        janus_ice_handle_destroy(session, handle->handle_id);
426
                        g_hash_table_iter_remove(&iter);
427
                }
428
        }
429

    
430
        /* FIXME Actually destroy session */
431
        janus_session_free(session);
432

    
433
        return 0;
434
}
435

    
436
void janus_session_free(janus_session *session) {
437
        if(session == NULL)
438
                return;
439
        janus_mutex_lock(&session->mutex);
440
        if(session->ice_handles != NULL) {
441
                g_hash_table_destroy(session->ice_handles);
442
                session->ice_handles = NULL;
443
        }
444
        if(session->source != NULL) {
445
                janus_request_destroy(session->source);
446
                session->source = NULL;
447
        }
448
        janus_mutex_unlock(&session->mutex);
449
        g_free(session);
450
        session = NULL;
451
}
452

    
453

    
454
/* Requests management */
455
janus_request *janus_request_new(janus_transport *transport, void *instance, void *request_id, gboolean admin, json_t *message) {
456
        janus_request *request = (janus_request *)g_malloc0(sizeof(janus_request));
457
        request->transport = transport;
458
        request->instance = instance;
459
        request->request_id = request_id;
460
        request->admin = admin;
461
        request->message = message;
462
        return request;
463
}
464

    
465
void janus_request_destroy(janus_request *request) {
466
        if(request == NULL)
467
                return;
468
        request->transport = NULL;
469
        request->instance = NULL;
470
        request->request_id = NULL;
471
        if(request->message)
472
                json_decref(request->message);
473
        request->message = NULL;
474
        g_free(request);
475
}
476

    
477
int janus_process_incoming_request(janus_request *request) {
478
        int ret = -1;
479
        if(request == NULL) {
480
                JANUS_LOG(LOG_ERR, "Missing request or payload to process, giving up...\n");
481
                return ret;
482
        }
483
        json_t *root = request->message;
484
        /* Ok, let's start with the ids */
485
        guint64 session_id = 0, handle_id = 0;
486
        json_t *s = json_object_get(root, "session_id");
487
        if(s && json_is_integer(s))
488
                session_id = json_integer_value(s);
489
        json_t *h = json_object_get(root, "handle_id");
490
        if(h && json_is_integer(h))
491
                handle_id = json_integer_value(h);
492

    
493
        /* Get transaction and message request */
494
        json_t *transaction = json_object_get(root, "transaction");
495
        if(!transaction) {
496
                ret = janus_process_error(request, session_id, NULL, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "Missing mandatory element (transaction)");
497
                goto jsondone;
498
        }
499
        if(!json_is_string(transaction)) {
500
                ret = janus_process_error(request, session_id, NULL, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (transaction should be a string)");
501
                goto jsondone;
502
        }
503
        const gchar *transaction_text = json_string_value(transaction);
504
        json_t *message = json_object_get(root, "janus");
505
        if(!message) {
506
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "Missing mandatory element (janus)");
507
                goto jsondone;
508
        }
509
        if(!json_is_string(message)) {
510
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (janus should be a string)");
511
                goto jsondone;
512
        }
513
        const gchar *message_text = json_string_value(message);
514
        
515
        if(session_id == 0 && handle_id == 0) {
516
                /* Can only be a 'Create new session', a 'Get info' or a 'Ping/Pong' request */
517
                if(!strcasecmp(message_text, "info")) {
518
                        ret = janus_process_success(request, janus_info(transaction_text));
519
                        goto jsondone;
520
                }
521
                if(!strcasecmp(message_text, "ping")) {
522
                        /* Prepare JSON reply */
523
                        json_t *reply = json_object();
524
                        json_object_set_new(reply, "janus", json_string("pong"));
525
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
526
                        ret = janus_process_success(request, reply);
527
                        goto jsondone;
528
                }
529
                if(strcasecmp(message_text, "create")) {
530
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
531
                        goto jsondone;
532
                }
533
                /* Any secret/token to check? */
534
                gboolean secret_authorized = FALSE, token_authorized = FALSE;
535
                if(api_secret == NULL && !janus_auth_is_enabled()) {
536
                        /* Nothing to check */
537
                        secret_authorized = TRUE;
538
                        token_authorized = TRUE;
539
                } else {
540
                        if(api_secret != NULL) {
541
                                /* There's an API secret, check that the client provided it */
542
                                json_t *secret = json_object_get(root, "apisecret");
543
                                if(secret && json_is_string(secret) && janus_strcmp_const_time(json_string_value(secret), api_secret)) {
544
                                        secret_authorized = TRUE;
545
                                }
546
                        }
547
                        if(janus_auth_is_enabled()) {
548
                                /* The token based authentication mechanism is enabled, check that the client provided it */
549
                                json_t *token = json_object_get(root, "token");
550
                                if(token && json_is_string(token) && janus_auth_check_token(json_string_value(token))) {
551
                                        token_authorized = TRUE;
552
                                }
553
                        }
554
                        /* We consider a request authorized if either the proper API secret or a valid token has been provided */
555
                        if(!secret_authorized && !token_authorized) {
556
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED, NULL);
557
                                goto jsondone;
558
                        }
559
                }
560
                session_id = 0;
561
                json_t *id = json_object_get(root, "id");
562
                if(id != NULL) {
563
                        /* The application provided the session ID to use */
564
                        if(!json_is_integer(id) || json_integer_value(id) < 0) {
565
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (id should be a positive integer)");
566
                                goto jsondone;
567
                        }
568
                        session_id = json_integer_value(id);
569
                        if(session_id > 0 && janus_session_find(session_id) != NULL) {
570
                                /* Session ID already taken */
571
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_CONFLICT, "Session ID already in use");
572
                                goto jsondone;
573
                        }
574
                }
575
                /* Handle it */
576
                janus_session *session = janus_session_create(session_id);
577
                if(session == NULL) {
578
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Memory error");
579
                        goto jsondone;
580
                }
581
                session_id = session->session_id;
582
                /* Take note of the request source that originated this session (HTTP, WebSockets, RabbitMQ?) */
583
                session->source = janus_request_new(request->transport, request->instance, NULL, FALSE, NULL);
584
                /* Notify the source that a new session has been created */
585
                request->transport->session_created(request->instance, session->session_id);
586
                /* Prepare JSON reply */
587
                json_t *reply = json_object();
588
                json_object_set_new(reply, "janus", json_string("success"));
589
                json_object_set_new(reply, "transaction", json_string(transaction_text));
590
                json_t *data = json_object();
591
                json_object_set_new(data, "id", json_integer(session_id));
592
                json_object_set_new(reply, "data", data);
593
                /* Send the success reply */
594
                ret = janus_process_success(request, reply);
595
                goto jsondone;
596
        }
597
        if(session_id < 1) {
598
                JANUS_LOG(LOG_ERR, "Invalid session\n");
599
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, NULL);
600
                goto jsondone;
601
        }
602
        if(h && handle_id < 1) {
603
                JANUS_LOG(LOG_ERR, "Invalid handle\n");
604
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, NULL);
605
                goto jsondone;
606
        }
607

    
608
        /* Go on with the processing */
609
        gboolean secret_authorized = FALSE, token_authorized = FALSE;
610
        if(api_secret == NULL && !janus_auth_is_enabled()) {
611
                /* Nothing to check */
612
                secret_authorized = TRUE;
613
                token_authorized = TRUE;
614
        } else {
615
                if(api_secret != NULL) {
616
                        /* There's an API secret, check that the client provided it */
617
                        json_t *secret = json_object_get(root, "apisecret");
618
                        if(secret && json_is_string(secret) && janus_strcmp_const_time(json_string_value(secret), api_secret)) {
619
                                secret_authorized = TRUE;
620
                        }
621
                }
622
                if(janus_auth_is_enabled()) {
623
                        /* The token based authentication mechanism is enabled, check that the client provided it */
624
                        json_t *token = json_object_get(root, "token");
625
                        if(token && json_is_string(token) && janus_auth_check_token(json_string_value(token))) {
626
                                token_authorized = TRUE;
627
                        }
628
                }
629
                /* We consider a request authorized if either the proper API secret or a valid token has been provided */
630
                if(!secret_authorized && !token_authorized) {
631
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED, NULL);
632
                        goto jsondone;
633
                }
634
        }
635

    
636
        /* If we got here, make sure we have a session (and/or a handle) */
637
        janus_session *session = janus_session_find(session_id);
638
        if(!session) {
639
                JANUS_LOG(LOG_ERR, "Couldn't find any session %"SCNu64"...\n", session_id);
640
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, "No such session %"SCNu64"", session_id);
641
                goto jsondone;
642
        }
643
        /* Update the last activity timer */
644
        session->last_activity = janus_get_monotonic_time();
645
        janus_ice_handle *handle = NULL;
646
        if(handle_id > 0) {
647
                handle = janus_ice_handle_find(session, handle_id);
648
                if(!handle) {
649
                        JANUS_LOG(LOG_ERR, "Couldn't find any handle %"SCNu64" in session %"SCNu64"...\n", handle_id, session_id);
650
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_HANDLE_NOT_FOUND, "No such handle %"SCNu64" in session %"SCNu64"", handle_id, session_id);
651
                        goto jsondone;
652
                }
653
        }
654

    
655
        /* What is this? */
656
        if(!strcasecmp(message_text, "keepalive")) {
657
                /* Just a keep-alive message, reply with an ack */
658
                JANUS_LOG(LOG_VERB, "Got a keep-alive on session %"SCNu64"\n", session_id);
659
                json_t *reply = json_object();
660
                json_object_set_new(reply, "janus", json_string("ack"));
661
                json_object_set_new(reply, "session_id", json_integer(session_id));
662
                json_object_set_new(reply, "transaction", json_string(transaction_text));
663
                /* Send the success reply */
664
                ret = janus_process_success(request, reply);
665
        } else if(!strcasecmp(message_text, "attach")) {
666
                if(handle != NULL) {
667
                        /* Attach is a session-level command */
668
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
669
                        goto jsondone;
670
                }
671
                json_t *plugin = json_object_get(root, "plugin");
672
                if(!plugin) {
673
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "Missing mandatory element (plugin)");
674
                        goto jsondone;
675
                }
676
                if(!json_is_string(plugin)) {
677
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (plugin should be a string)");
678
                        goto jsondone;
679
                }
680
                const gchar *plugin_text = json_string_value(plugin);
681
                janus_plugin *plugin_t = janus_plugin_find(plugin_text);
682
                if(plugin_t == NULL) {
683
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_NOT_FOUND, "No such plugin '%s'", plugin_text);
684
                        goto jsondone;
685
                }
686
                /* If the auth token mechanism is enabled, we should check if this token can access this plugin */
687
                if(janus_auth_is_enabled()) {
688
                        json_t *token = json_object_get(root, "token");
689
                        if(token != NULL) {
690
                                const char *token_value = json_string_value(token);
691
                                if(token_value && !janus_auth_check_plugin(token_value, plugin_t)) {
692
                                        JANUS_LOG(LOG_ERR, "Token '%s' can't access plugin '%s'\n", token_value, plugin_text);
693
                                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED_PLUGIN, "Provided token can't access plugin '%s'", plugin_text);
694
                                        goto jsondone;
695
                                }
696
                        }
697
                }
698
                /* Create handle */
699
                handle = janus_ice_handle_create(session);
700
                if(handle == NULL) {
701
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Memory error");
702
                        goto jsondone;
703
                }
704
                handle_id = handle->handle_id;
705
                /* Attach to the plugin */
706
                int error = 0;
707
                if((error = janus_ice_handle_attach_plugin(session, handle_id, plugin_t)) != 0) {
708
                        /* TODO Make error struct to pass verbose information */
709
                        janus_ice_handle_destroy(session, handle_id);
710
                        janus_mutex_lock(&session->mutex);
711
                        g_hash_table_remove(session->ice_handles, GUINT_TO_POINTER(handle_id));
712
                        janus_mutex_unlock(&session->mutex);
713

    
714
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_ATTACH, "Couldn't attach to plugin: error '%d'", error);
715
                        goto jsondone;
716
                }
717
                /* Prepare JSON reply */
718
                json_t *reply = json_object();
719
                json_object_set_new(reply, "janus", json_string("success"));
720
                json_object_set_new(reply, "session_id", json_integer(session_id));
721
                json_object_set_new(reply, "transaction", json_string(transaction_text));
722
                json_t *data = json_object();
723
                json_object_set_new(data, "id", json_integer(handle_id));
724
                json_object_set_new(reply, "data", data);
725
                /* Send the success reply */
726
                ret = janus_process_success(request, reply);
727
        } else if(!strcasecmp(message_text, "destroy")) {
728
                if(handle != NULL) {
729
                        /* Query is a session-level command */
730
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
731
                        goto jsondone;
732
                }
733
                /* Schedule the session for deletion */
734
                session->destroy = 1;
735
                janus_mutex_lock(&sessions_mutex);
736
                g_hash_table_remove(sessions, GUINT_TO_POINTER(session->session_id));
737
                g_hash_table_insert(old_sessions, GUINT_TO_POINTER(session->session_id), session);
738
                GSource *timeout_source = g_timeout_source_new_seconds(3);
739
                g_source_set_callback(timeout_source, janus_cleanup_session, session, NULL);
740
                g_source_attach(timeout_source, sessions_watchdog_context);
741
                g_source_unref(timeout_source);
742
                janus_mutex_unlock(&sessions_mutex);
743
                /* Notify the source that the session has been destroyed */
744
                if(session->source && session->source->transport)
745
                        session->source->transport->session_over(session->source->instance, session->session_id, FALSE);
746

    
747
                /* Prepare JSON reply */
748
                json_t *reply = json_object();
749
                json_object_set_new(reply, "janus", json_string("success"));
750
                json_object_set_new(reply, "session_id", json_integer(session_id));
751
                json_object_set_new(reply, "transaction", json_string(transaction_text));
752
                /* Send the success reply */
753
                ret = janus_process_success(request, reply);
754
        } else if(!strcasecmp(message_text, "detach")) {
755
                if(handle == NULL) {
756
                        /* Query is an handle-level command */
757
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
758
                        goto jsondone;
759
                }
760
                if(handle->app == NULL || handle->app_handle == NULL) {
761
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_DETACH, "No plugin to detach from");
762
                        goto jsondone;
763
                }
764
                int error = janus_ice_handle_destroy(session, handle_id);
765
                janus_mutex_lock(&session->mutex);
766
                g_hash_table_remove(session->ice_handles, GUINT_TO_POINTER(handle_id));
767
                janus_mutex_unlock(&session->mutex);
768

    
769
                if(error != 0) {
770
                        /* TODO Make error struct to pass verbose information */
771
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_DETACH, "Couldn't detach from plugin: error '%d'", error);
772
                        /* TODO Delete handle instance */
773
                        goto jsondone;
774
                }
775
                /* Prepare JSON reply */
776
                json_t *reply = json_object();
777
                json_object_set_new(reply, "janus", json_string("success"));
778
                json_object_set_new(reply, "session_id", json_integer(session_id));
779
                json_object_set_new(reply, "transaction", json_string(transaction_text));
780
                /* Send the success reply */
781
                ret = janus_process_success(request, reply);
782
        } else if(!strcasecmp(message_text, "message")) {
783
                if(handle == NULL) {
784
                        /* Query is an handle-level command */
785
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
786
                        goto jsondone;
787
                }
788
                if(handle->app == NULL || handle->app_handle == NULL) {
789
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "No plugin to handle this message");
790
                        goto jsondone;
791
                }
792
                janus_plugin *plugin_t = (janus_plugin *)handle->app;
793
                JANUS_LOG(LOG_VERB, "[%"SCNu64"] There's a message for %s\n", handle->handle_id, plugin_t->get_name());
794
                json_t *body = json_object_get(root, "body");
795
                if(body == NULL) {
796
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "Missing mandatory element (body)");
797
                        goto jsondone;
798
                }
799
                if(!json_is_object(body)) {
800
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_JSON_OBJECT, "Invalid body object");
801
                        goto jsondone;
802
                }
803
                /* Is there an SDP attached? */
804
                json_t *jsep = json_object_get(root, "jsep");
805
                char *jsep_type = NULL;
806
                char *jsep_sdp = NULL, *jsep_sdp_stripped = NULL;
807
                if(jsep != NULL) {
808
                        if(!json_is_object(jsep)) {
809
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_JSON_OBJECT, "Invalid jsep object");
810
                                goto jsondone;
811
                        }
812
                        json_t *type = json_object_get(jsep, "type");
813
                        if(!type) {
814
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "JSEP error: missing mandatory element (type)");
815
                                goto jsondone;
816
                        }
817
                        if(!json_is_string(type)) {
818
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "JSEP error: invalid element type (type should be a string)");
819
                                goto jsondone;
820
                        }
821
                        jsep_type = g_strdup(json_string_value(type));
822
                        type = NULL;
823
                        /* Are we still cleaning up from a previous media session? */
824
                        if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING)) {
825
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Still cleaning up from a previous media session, let's wait a bit...\n", handle->handle_id);
826
                                gint64 waited = 0;
827
                                while(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING)) {
828
                                        g_usleep(100000);
829
                                        waited += 100000;
830
                                        if(waited >= 3*G_USEC_PER_SEC) {
831
                                                JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Waited 3 seconds, that's enough!\n", handle->handle_id);
832
                                                break;
833
                                        }
834
                                }
835
                        }
836
                        /* Check the JSEP type */
837
                        janus_mutex_lock(&handle->mutex);
838
                        int offer = 0;
839
                        if(!strcasecmp(jsep_type, "offer")) {
840
                                offer = 1;
841
                                janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
842
                                janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER);
843
                                janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER);
844
                        } else if(!strcasecmp(jsep_type, "answer")) {
845
                                janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER);
846
                                offer = 0;
847
                        } else {
848
                                /* TODO Handle other message types as well */
849
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_JSEP_UNKNOWN_TYPE, "JSEP error: unknown message type '%s'", jsep_type);
850
                                g_free(jsep_type);
851
                                janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
852
                                janus_mutex_unlock(&handle->mutex);
853
                                goto jsondone;
854
                        }
855
                        json_t *sdp = json_object_get(jsep, "sdp");
856
                        if(!sdp) {
857
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "JSEP error: missing mandatory element (sdp)");
858
                                g_free(jsep_type);
859
                                janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
860
                                janus_mutex_unlock(&handle->mutex);
861
                                goto jsondone;
862
                        }
863
                        if(!json_is_string(sdp)) {
864
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "JSEP error: invalid element type (sdp should be a string)");
865
                                g_free(jsep_type);
866
                                janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
867
                                janus_mutex_unlock(&handle->mutex);
868
                                goto jsondone;
869
                        }
870
                        jsep_sdp = (char *)json_string_value(sdp);
871
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Remote SDP:\n%s", handle->handle_id, jsep_sdp);
872
                        /* Is this valid SDP? */
873
                        int audio = 0, video = 0, data = 0, bundle = 0, rtcpmux = 0, trickle = 0;
874
                        janus_sdp *parsed_sdp = janus_sdp_preparse(jsep_sdp, &audio, &video, &data, &bundle, &rtcpmux, &trickle);
875
                        if(parsed_sdp == NULL) {
876
                                /* Invalid SDP */
877
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_JSEP_INVALID_SDP, "JSEP error: invalid SDP");
878
                                g_free(jsep_type);
879
                                janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
880
                                janus_mutex_unlock(&handle->mutex);
881
                                goto jsondone;
882
                        }
883
                        /* FIXME We're only handling single audio/video lines for now... */
884
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio %s been negotiated, Video %s been negotiated, SCTP/DataChannels %s been negotiated\n",
885
                                            handle->handle_id,
886
                                            audio ? "has" : "has NOT",
887
                                            video ? "has" : "has NOT",
888
                                            data ? "have" : "have NOT");
889
                        if(audio > 1) {
890
                                JANUS_LOG(LOG_WARN, "[%"SCNu64"] More than one audio line? only going to negotiate one...\n", handle->handle_id);
891
                        }
892
                        if(video > 1) {
893
                                JANUS_LOG(LOG_WARN, "[%"SCNu64"] More than one video line? only going to negotiate one...\n", handle->handle_id);
894
                        }
895
                        if(data > 1) {
896
                                JANUS_LOG(LOG_WARN, "[%"SCNu64"] More than one data line? only going to negotiate one...\n", handle->handle_id);
897
                        }
898
#ifndef HAVE_SCTP
899
                        if(data) {
900
                                JANUS_LOG(LOG_WARN, "[%"SCNu64"]   -- DataChannels have been negotiated, but support for them has not been compiled...\n", handle->handle_id);
901
                        }
902
#endif
903
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] The browser: %s BUNDLE, %s rtcp-mux, %s doing Trickle ICE\n", handle->handle_id,
904
                                            bundle  ? "supports" : "does NOT support",
905
                                            rtcpmux ? "supports" : "does NOT support",
906
                                            trickle ? "is"       : "is NOT");
907
                        /* Check if it's a new session, or an update... */
908
                        if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_READY)
909
                                        || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)) {
910
                                /* New session */
911
                                if(offer) {
912
                                        /* Setup ICE locally (we received an offer) */
913
                                        if(janus_ice_setup_local(handle, offer, audio, video, data, bundle, rtcpmux, trickle) < 0) {
914
                                                JANUS_LOG(LOG_ERR, "Error setting ICE locally\n");
915
                                                g_free(jsep_type);
916
                                                janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
917
                                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Error setting ICE locally");
918
                                                janus_mutex_unlock(&handle->mutex);
919
                                                goto jsondone;
920
                                        }
921
                                } else {
922
                                        /* Make sure we're waiting for an ANSWER in the first place */
923
                                        if(!handle->agent) {
924
                                                JANUS_LOG(LOG_ERR, "Unexpected ANSWER (did we offer?)\n");
925
                                                g_free(jsep_type);
926
                                                janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
927
                                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNEXPECTED_ANSWER, "Unexpected ANSWER (did we offer?)");
928
                                                janus_mutex_unlock(&handle->mutex);
929
                                                goto jsondone;
930
                                        }
931
                                }
932
                                janus_sdp_parse(handle, parsed_sdp);
933
                                janus_sdp_free(parsed_sdp);
934
                                if(!offer) {
935
                                        /* Set remote candidates now (we received an answer) */
936
                                        if(bundle) {
937
                                                janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE);
938
                                        } else {
939
                                                janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE);
940
                                        }
941
                                        if(rtcpmux) {
942
                                                janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX);
943
                                        } else {
944
                                                janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX);
945
                                        }
946
                                        if(trickle) {
947
                                                janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE);
948
                                        } else {
949
                                                janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE);
950
                                        }
951
                                        if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
952
                                                JANUS_LOG(LOG_HUGE, "[%"SCNu64"]   -- bundle is supported by the browser, getting rid of one of the RTP/RTCP components, if any...\n", handle->handle_id);
953
                                                if(audio) {
954
                                                        /* Get rid of video and data, if present */
955
                                                        if(handle->streams && handle->video_stream) {
956
                                                                handle->audio_stream->video_ssrc = handle->video_stream->video_ssrc;
957
                                                                handle->audio_stream->video_ssrc_peer = handle->video_stream->video_ssrc_peer;
958
                                                                handle->audio_stream->video_ssrc_peer_rtx = handle->video_stream->video_ssrc_peer_rtx;
959
                                                                nice_agent_attach_recv(handle->agent, handle->video_stream->stream_id, 1, g_main_loop_get_context (handle->iceloop), NULL, NULL);
960
                                                                if(!janus_ice_is_rtcpmux_forced())
961
                                                                        nice_agent_attach_recv(handle->agent, handle->video_stream->stream_id, 2, g_main_loop_get_context (handle->iceloop), NULL, NULL);
962
                                                                nice_agent_remove_stream(handle->agent, handle->video_stream->stream_id);
963
                                                                janus_ice_stream_free(handle->streams, handle->video_stream);
964
                                                        }
965
                                                        handle->video_stream = NULL;
966
                                                        handle->video_id = 0;
967
                                                        if(handle->streams && handle->data_stream) {
968
                                                                nice_agent_attach_recv(handle->agent, handle->data_stream->stream_id, 1, g_main_loop_get_context (handle->iceloop), NULL, NULL);
969
                                                                nice_agent_remove_stream(handle->agent, handle->data_stream->stream_id);
970
                                                                janus_ice_stream_free(handle->streams, handle->data_stream);
971
                                                        }
972
                                                        handle->data_stream = NULL;
973
                                                        handle->data_id = 0;
974
                                                } else if(video) {
975
                                                        /* Get rid of data, if present */
976
                                                        if(handle->streams && handle->data_stream) {
977
                                                                nice_agent_attach_recv(handle->agent, handle->data_stream->stream_id, 1, g_main_loop_get_context (handle->iceloop), NULL, NULL);
978
                                                                nice_agent_remove_stream(handle->agent, handle->data_stream->stream_id);
979
                                                                janus_ice_stream_free(handle->streams, handle->data_stream);
980
                                                        }
981
                                                        handle->data_stream = NULL;
982
                                                        handle->data_id = 0;
983
                                                }
984
                                        }
985
                                        if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX) && !janus_ice_is_rtcpmux_forced()) {
986
                                                JANUS_LOG(LOG_HUGE, "[%"SCNu64"]   -- rtcp-mux is supported by the browser, getting rid of RTCP components, if any...\n", handle->handle_id);
987
                                                if(handle->audio_stream && handle->audio_stream->components != NULL) {
988
                                                        nice_agent_attach_recv(handle->agent, handle->audio_id, 2, g_main_loop_get_context (handle->iceloop), NULL, NULL);
989
                                                        /* Free the component */
990
                                                        janus_ice_component_free(handle->audio_stream->components, handle->audio_stream->rtcp_component);
991
                                                        handle->audio_stream->rtcp_component = NULL;
992
                                                        /* Create a dummy candidate and enforce it as the one to use for this now unneeded component */
993
                                                        NiceCandidate *c = nice_candidate_new(NICE_CANDIDATE_TYPE_HOST);
994
                                                        c->component_id = 2;
995
                                                        c->stream_id = handle->audio_stream->stream_id;
996
#ifndef HAVE_LIBNICE_TCP
997
                                                        c->transport = NICE_CANDIDATE_TRANSPORT_UDP;
998
#endif
999
                                                        strncpy(c->foundation, "1", NICE_CANDIDATE_MAX_FOUNDATION);
1000
                                                        c->priority = 1;
1001
                                                        nice_address_set_from_string(&c->addr, "127.0.0.1");
1002
                                                        nice_address_set_port(&c->addr, janus_ice_get_rtcpmux_blackhole_port());
1003
                                                        c->username = g_strdup(handle->audio_stream->ruser);
1004
                                                        c->password = g_strdup(handle->audio_stream->rpass);
1005
                                                        if(!nice_agent_set_selected_remote_candidate(handle->agent, handle->audio_stream->stream_id, 2, c)) {
1006
                                                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error forcing dummy candidate on RTCP component of stream %d\n", handle->handle_id, handle->audio_stream->stream_id);
1007
                                                                nice_candidate_free(c);
1008
                                                        }
1009
                                                }
1010
                                                if(handle->video_stream && handle->video_stream->components != NULL) {
1011
                                                        nice_agent_attach_recv(handle->agent, handle->video_id, 2, g_main_loop_get_context (handle->iceloop), NULL, NULL);
1012
                                                        /* Free the component */
1013
                                                        janus_ice_component_free(handle->video_stream->components, handle->video_stream->rtcp_component);
1014
                                                        handle->video_stream->rtcp_component = NULL;
1015
                                                        /* Create a dummy candidate and enforce it as the one to use for this now unneeded component */
1016
                                                        NiceCandidate *c = nice_candidate_new(NICE_CANDIDATE_TYPE_HOST);
1017
                                                        c->component_id = 2;
1018
                                                        c->stream_id = handle->video_stream->stream_id;
1019
#ifndef HAVE_LIBNICE_TCP
1020
                                                        c->transport = NICE_CANDIDATE_TRANSPORT_UDP;
1021
#endif
1022
                                                        strncpy(c->foundation, "1", NICE_CANDIDATE_MAX_FOUNDATION);
1023
                                                        c->priority = 1;
1024
                                                        nice_address_set_from_string(&c->addr, "127.0.0.1");
1025
                                                        nice_address_set_port(&c->addr, janus_ice_get_rtcpmux_blackhole_port());
1026
                                                        c->username = g_strdup(handle->video_stream->ruser);
1027
                                                        c->password = g_strdup(handle->video_stream->rpass);
1028
                                                        if(!nice_agent_set_selected_remote_candidate(handle->agent, handle->video_stream->stream_id, 2, c)) {
1029
                                                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error forcing dummy candidate on RTCP component of stream %d\n", handle->handle_id, handle->video_stream->stream_id);
1030
                                                                nice_candidate_free(c);
1031
                                                        }
1032
                                                }
1033
                                        }
1034
                                        /* FIXME Any disabled m-line? */
1035
                                        if(strstr(jsep_sdp, "m=audio 0")) {
1036
                                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio disabled via SDP\n", handle->handle_id);
1037
                                                if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)
1038
                                                                || (!video && !data)) {
1039
                                                        JANUS_LOG(LOG_HUGE, "  -- Marking audio stream as disabled\n");
1040
                                                        janus_ice_stream *stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->audio_id));
1041
                                                        if(stream)
1042
                                                                stream->disabled = TRUE;
1043
                                                }
1044
                                        }
1045
                                        if(strstr(jsep_sdp, "m=video 0")) {
1046
                                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Video disabled via SDP\n", handle->handle_id);
1047
                                                if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)
1048
                                                                || (!audio && !data)) {
1049
                                                        JANUS_LOG(LOG_HUGE, "  -- Marking video stream as disabled\n");
1050
                                                        janus_ice_stream *stream = NULL;
1051
                                                        if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
1052
                                                                stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->video_id));
1053
                                                        } else {
1054
                                                                gint id = handle->audio_id > 0 ? handle->audio_id : handle->video_id;
1055
                                                                stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(id));
1056
                                                        }
1057
                                                        if(stream)
1058
                                                                stream->disabled = TRUE;
1059
                                                }
1060
                                        }
1061
                                        if(strstr(jsep_sdp, "m=application 0 DTLS/SCTP")) {
1062
                                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Data Channel disabled via SDP\n", handle->handle_id);
1063
                                                if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)
1064
                                                                || (!audio && !video)) {
1065
                                                        JANUS_LOG(LOG_HUGE, "  -- Marking data channel stream as disabled\n");
1066
                                                        janus_ice_stream *stream = NULL;
1067
                                                        if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
1068
                                                                stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->data_id));
1069
                                                        } else {
1070
                                                                gint id = handle->audio_id > 0 ? handle->audio_id : (handle->video_id > 0 ? handle->video_id : handle->data_id);
1071
                                                                stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(id));
1072
                                                        }
1073
                                                        if(stream)
1074
                                                                stream->disabled = TRUE;
1075
                                                }
1076
                                        }
1077
                                        /* We got our answer */
1078
                                        janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1079
                                        /* Any pending trickles? */
1080
                                        if(handle->pending_trickles) {
1081
                                                JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Processing %d pending trickle candidates\n", handle->handle_id, g_list_length(handle->pending_trickles));
1082
                                                GList *temp = NULL;
1083
                                                while(handle->pending_trickles) {
1084
                                                        temp = g_list_first(handle->pending_trickles);
1085
                                                        handle->pending_trickles = g_list_remove_link(handle->pending_trickles, temp);
1086
                                                        janus_ice_trickle *trickle = (janus_ice_trickle *)temp->data;
1087
                                                        g_list_free(temp);
1088
                                                        if(trickle == NULL)
1089
                                                                continue;
1090
                                                        if((janus_get_monotonic_time() - trickle->received) > 15*G_USEC_PER_SEC) {
1091
                                                                /* FIXME Candidate is too old, discard it */
1092
                                                                janus_ice_trickle_destroy(trickle);
1093
                                                                /* FIXME We should report that */
1094
                                                                continue;
1095
                                                        }
1096
                                                        json_t *candidate = trickle->candidate;
1097
                                                        if(candidate == NULL) {
1098
                                                                janus_ice_trickle_destroy(trickle);
1099
                                                                continue;
1100
                                                        }
1101
                                                        if(json_is_object(candidate)) {
1102
                                                                /* We got a single candidate */
1103
                                                                int error = 0;
1104
                                                                const char *error_string = NULL;
1105
                                                                if((error = janus_ice_trickle_parse(handle, candidate, &error_string)) != 0) {
1106
                                                                        /* FIXME We should report the error parsing the trickle candidate */
1107
                                                                }
1108
                                                        } else if(json_is_array(candidate)) {
1109
                                                                /* We got multiple candidates in an array */
1110
                                                                JANUS_LOG(LOG_VERB, "Got multiple candidates (%zu)\n", json_array_size(candidate));
1111
                                                                if(json_array_size(candidate) > 0) {
1112
                                                                        /* Handle remote candidates */
1113
                                                                        size_t i = 0;
1114
                                                                        for(i=0; i<json_array_size(candidate); i++) {
1115
                                                                                json_t *c = json_array_get(candidate, i);
1116
                                                                                /* FIXME We don't care if any trickle fails to parse */
1117
                                                                                janus_ice_trickle_parse(handle, c, NULL);
1118
                                                                        }
1119
                                                                }
1120
                                                        }
1121
                                                        /* Done, free candidate */
1122
                                                        janus_ice_trickle_destroy(trickle);
1123
                                                }
1124
                                        }
1125
                                        /* This was an answer, check if it's time to start ICE */
1126
                                        if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE) &&
1127
                                                        !janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALL_TRICKLES)) {
1128
                                                JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- ICE Trickling is supported by the browser, waiting for remote candidates...\n", handle->handle_id);
1129
                                                janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_START);
1130
                                        } else {
1131
                                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Done! Sending connectivity checks...\n", handle->handle_id);
1132
                                                if(handle->audio_id > 0) {
1133
                                                        janus_ice_setup_remote_candidates(handle, handle->audio_id, 1);
1134
                                                        if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX))        /* http://tools.ietf.org/html/rfc5761#section-5.1.3 */
1135
                                                                janus_ice_setup_remote_candidates(handle, handle->audio_id, 2);
1136
                                                }
1137
                                                if(handle->video_id > 0) {
1138
                                                        janus_ice_setup_remote_candidates(handle, handle->video_id, 1);
1139
                                                        if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX))        /* http://tools.ietf.org/html/rfc5761#section-5.1.3 */
1140
                                                                janus_ice_setup_remote_candidates(handle, handle->video_id, 2);
1141
                                                }
1142
                                                if(handle->data_id > 0) {
1143
                                                        janus_ice_setup_remote_candidates(handle, handle->data_id, 1);
1144
                                                }
1145
                                        }
1146
                                }
1147
                        } else {
1148
                                /* TODO Actually handle session updates: for now we ignore them, and just relay them to plugins */
1149
                                JANUS_LOG(LOG_WARN, "[%"SCNu64"] Ignoring negotiation update, we don't support them yet...\n", handle->handle_id);
1150
                        }
1151
                        handle->remote_sdp = g_strdup(jsep_sdp);
1152
                        janus_mutex_unlock(&handle->mutex);
1153
                        /* Anonymize SDP */
1154
                        jsep_sdp_stripped = janus_sdp_anonymize(jsep_sdp);
1155
                        if(jsep_sdp_stripped == NULL) {
1156
                                /* Invalid SDP */
1157
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_JSEP_INVALID_SDP, "JSEP error: invalid SDP");
1158
                                g_free(jsep_type);
1159
                                janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1160
                                goto jsondone;
1161
                        }
1162
                        sdp = NULL;
1163
                        janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1164
                }
1165

    
1166
                /* Make sure the app handle is still valid */
1167
                if(handle->app == NULL || handle->app_handle == NULL || !janus_plugin_session_is_alive(handle->app_handle)) {
1168
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "No plugin to handle this message");
1169
                        if(jsep_type)
1170
                                g_free(jsep_type);
1171
                        if(jsep_sdp_stripped)
1172
                                g_free(jsep_sdp_stripped);
1173
                        janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1174
                        goto jsondone;
1175
                }
1176

    
1177
                /* Send the message to the plugin (which must eventually free transaction_text, body_text, jsep_type and sdp) */
1178
                char *body_text = json_dumps(body, JSON_INDENT(3) | JSON_PRESERVE_ORDER);
1179
                janus_plugin_result *result = plugin_t->handle_message(handle->app_handle, g_strdup((char *)transaction_text), body_text, jsep_type, jsep_sdp_stripped);
1180
                if(result == NULL) {
1181
                        /* Something went horribly wrong! */
1182
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "Plugin didn't give a result");
1183
                        goto jsondone;
1184
                }
1185
                if(result->type == JANUS_PLUGIN_OK) {
1186
                        /* The plugin gave a result already (synchronous request/response) */
1187
                        if(result->content == NULL) {
1188
                                /* Missing content... */
1189
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "Plugin didn't provide any content for this synchronous response");
1190
                                janus_plugin_result_destroy(result);
1191
                                goto jsondone;
1192
                        }
1193
                        json_error_t error;
1194
                        json_t *event = json_loads(result->content, 0, &error);
1195
                        if(!event) {
1196
                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot send response from plugin (JSON error: on line %d: %s)\n", handle->handle_id, error.line, error.text);
1197
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "Plugin returned an invalid JSON response");
1198
                                janus_plugin_result_destroy(result);
1199
                                goto jsondone;
1200
                        }
1201
                        if(!json_is_object(event)) {
1202
                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot send response from plugin (JSON error: not an object)\n", handle->handle_id);
1203
                                json_decref(event);
1204
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "Plugin returned an invalid JSON response");
1205
                                janus_plugin_result_destroy(result);
1206
                                goto jsondone;
1207
                        }
1208
                        /* Prepare JSON response */
1209
                        json_t *reply = json_object();
1210
                        json_object_set_new(reply, "janus", json_string("success"));
1211
                        json_object_set_new(reply, "session_id", json_integer(session->session_id));
1212
                        json_object_set_new(reply, "sender", json_integer(handle->handle_id));
1213
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1214
                        json_t *plugin_data = json_object();
1215
                        json_object_set_new(plugin_data, "plugin", json_string(plugin_t->get_package()));
1216
                        json_object_set_new(plugin_data, "data", event);
1217
                        json_object_set_new(reply, "plugindata", plugin_data);
1218
                        /* Send the success reply */
1219
                        ret = janus_process_success(request, reply);
1220
                } else if(result->type == JANUS_PLUGIN_OK_WAIT) {
1221
                        /* The plugin received the request but didn't process it yet, send an ack (asynchronous notifications may follow) */
1222
                        json_t *reply = json_object();
1223
                        json_object_set_new(reply, "janus", json_string("ack"));
1224
                        json_object_set_new(reply, "session_id", json_integer(session_id));
1225
                        if(result->content)
1226
                                json_object_set_new(reply, "hint", json_string(result->content));
1227
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1228
                        /* Send the success reply */
1229
                        ret = janus_process_success(request, reply);
1230
                } else {
1231
                        /* Something went horribly wrong! */
1232
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "%s", result->content ? g_strdup(result->content) : "Plugin returned a severe (unknown) error");
1233
                        janus_plugin_result_destroy(result);
1234
                        goto jsondone;
1235
                }                        
1236
                janus_plugin_result_destroy(result);
1237
        } else if(!strcasecmp(message_text, "trickle")) {
1238
                if(handle == NULL) {
1239
                        /* Trickle is an handle-level command */
1240
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
1241
                        goto jsondone;
1242
                }
1243
                if(handle->app == NULL || handle->app_handle == NULL || !janus_plugin_session_is_alive(handle->app_handle)) {
1244
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "No plugin to handle this trickle candidate");
1245
                        goto jsondone;
1246
                }
1247
                json_t *candidate = json_object_get(root, "candidate");
1248
                json_t *candidates = json_object_get(root, "candidates");
1249
                if(candidate == NULL && candidates == NULL) {
1250
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "Missing mandatory element (candidate|candidates)");
1251
                        goto jsondone;
1252
                }
1253
                if(candidate != NULL && candidates != NULL) {
1254
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_JSON, "Can't have both candidate and candidates");
1255
                        goto jsondone;
1256
                }
1257
                janus_mutex_lock(&handle->mutex);
1258
                if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE)) {
1259
                        /* It looks like this peer supports Trickle, after all */
1260
                        JANUS_LOG(LOG_VERB, "Handle %"SCNu64" supports trickle even if it didn't negotiate it...\n", handle->handle_id);
1261
                        janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE);
1262
                }
1263
                /* Is there any stream ready? this trickle may get here before the SDP it relates to */
1264
                if(handle->audio_stream == NULL && handle->video_stream == NULL && handle->data_stream == NULL) {
1265
                        JANUS_LOG(LOG_WARN, "[%"SCNu64"] No stream, queueing this trickle as it got here before the SDP...\n", handle->handle_id);
1266
                        /* Enqueue this trickle candidate(s), we'll process this later */
1267
                        janus_ice_trickle *early_trickle = janus_ice_trickle_new(handle, transaction_text, candidate ? candidate : candidates);
1268
                        handle->pending_trickles = g_list_append(handle->pending_trickles, early_trickle);
1269
                        /* Send the ack right away, an event will tell the application if the candidate(s) failed */
1270
                        goto trickledone;
1271
                }
1272
                /* Is the ICE stack ready already? */
1273
                if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER) ||
1274
                                !janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER) ||
1275
                                !janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER)) {
1276
                        const char *cause = NULL;
1277
                        if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER))
1278
                                cause = "processing the offer";
1279
                        else if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER))
1280
                                cause = "waiting for the answer";
1281
                        else if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER))
1282
                                cause = "waiting for the offer";
1283
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Still %s, queueing this trickle to wait until we're done there...\n",
1284
                                handle->handle_id, cause);
1285
                        /* Enqueue this trickle candidate(s), we'll process this later */
1286
                        janus_ice_trickle *early_trickle = janus_ice_trickle_new(handle, transaction_text, candidate ? candidate : candidates);
1287
                        handle->pending_trickles = g_list_append(handle->pending_trickles, early_trickle);
1288
                        /* Send the ack right away, an event will tell the application if the candidate(s) failed */
1289
                        goto trickledone;
1290
                }
1291
                if(candidate != NULL) {
1292
                        /* We got a single candidate */
1293
                        int error = 0;
1294
                        const char *error_string = NULL;
1295
                        if((error = janus_ice_trickle_parse(handle, candidate, &error_string)) != 0) {
1296
                                ret = janus_process_error(request, session_id, transaction_text, error, "%s", error_string);
1297
                                janus_mutex_unlock(&handle->mutex);
1298
                                goto jsondone;
1299
                        }
1300
                } else {
1301
                        /* We got multiple candidates in an array */
1302
                        if(!json_is_array(candidates)) {
1303
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "candidates is not an array");
1304
                                janus_mutex_unlock(&handle->mutex);
1305
                                goto jsondone;
1306
                        }
1307
                        JANUS_LOG(LOG_VERB, "Got multiple candidates (%zu)\n", json_array_size(candidates));
1308
                        if(json_array_size(candidates) > 0) {
1309
                                /* Handle remote candidates */
1310
                                size_t i = 0;
1311
                                for(i=0; i<json_array_size(candidates); i++) {
1312
                                        json_t *c = json_array_get(candidates, i);
1313
                                        /* FIXME We don't care if any trickle fails to parse */
1314
                                        janus_ice_trickle_parse(handle, c, NULL);
1315
                                }
1316
                        }
1317
                }
1318

    
1319
trickledone:
1320
                janus_mutex_unlock(&handle->mutex);
1321
                /* We reply right away, not to block the web server... */
1322
                json_t *reply = json_object();
1323
                json_object_set_new(reply, "janus", json_string("ack"));
1324
                json_object_set_new(reply, "session_id", json_integer(session_id));
1325
                json_object_set_new(reply, "transaction", json_string(transaction_text));
1326
                /* Send the success reply */
1327
                ret = janus_process_success(request, reply);
1328
        } else {
1329
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN_REQUEST, "Unknown request '%s'", message_text);
1330
        }
1331

    
1332
jsondone:
1333
        /* Done processing */
1334
        return ret;
1335
}
1336

    
1337
/* Admin/monitor WebServer requests handler */
1338
int janus_process_incoming_admin_request(janus_request *request) {
1339
        int ret = -1;
1340
        if(request == NULL) {
1341
                JANUS_LOG(LOG_ERR, "Missing request or payload to process, giving up...\n");
1342
                return ret;
1343
        }
1344
        json_t *root = request->message;
1345
        /* Ok, let's start with the ids */
1346
        guint64 session_id = 0, handle_id = 0;
1347
        json_t *s = json_object_get(root, "session_id");
1348
        if(s && json_is_integer(s))
1349
                session_id = json_integer_value(s);
1350
        json_t *h = json_object_get(root, "handle_id");
1351
        if(h && json_is_integer(h))
1352
                handle_id = json_integer_value(h);
1353

    
1354
        /* Get transaction and message request */
1355
        json_t *transaction = json_object_get(root, "transaction");
1356
        if(!transaction) {
1357
                ret = janus_process_error(request, session_id, NULL, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "Missing mandatory element (transaction)");
1358
                goto jsondone;
1359
        }
1360
        if(!json_is_string(transaction)) {
1361
                ret = janus_process_error(request, session_id, NULL, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (transaction should be a string)");
1362
                goto jsondone;
1363
        }
1364
        const gchar *transaction_text = json_string_value(transaction);
1365
        json_t *message = json_object_get(root, "janus");
1366
        if(!message) {
1367
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "Missing mandatory element (janus)");
1368
                goto jsondone;
1369
        }
1370
        if(!json_is_string(message)) {
1371
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (janus should be a string)");
1372
                goto jsondone;
1373
        }
1374
        const gchar *message_text = json_string_value(message);
1375
        
1376
        if(session_id == 0 && handle_id == 0) {
1377
                /* Can only be a 'Get all sessions' or some general setting manipulation request */
1378
                if(!strcasecmp(message_text, "info")) {
1379
                        /* The generic info request */
1380
                        ret = janus_process_success(request, janus_info(transaction_text));
1381
                        goto jsondone;
1382
                }
1383
                if(admin_api_secret != NULL) {
1384
                        /* There's an admin/monitor secret, check that the client provided it */
1385
                        json_t *secret = json_object_get(root, "admin_secret");
1386
                        if(!secret || !json_is_string(secret) || !janus_strcmp_const_time(json_string_value(secret), admin_api_secret)) {
1387
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED, NULL);
1388
                                goto jsondone;
1389
                        }
1390
                }
1391
                if(!strcasecmp(message_text, "get_status")) {
1392
                        /* Return some info on the settings (mostly debug-related, at the moment) */
1393
                        json_t *reply = json_object();
1394
                        json_object_set_new(reply, "janus", json_string("success"));
1395
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1396
                        json_t *status = json_object();
1397
                        json_object_set_new(status, "token_auth", json_integer(janus_auth_is_enabled()));
1398
                        json_object_set_new(status, "log_level", json_integer(janus_log_level));
1399
                        json_object_set_new(status, "log_timestamps", json_integer(janus_log_timestamps));
1400
                        json_object_set_new(status, "log_colors", json_integer(janus_log_colors));
1401
                        json_object_set_new(status, "locking_debug", json_integer(lock_debug));
1402
                        json_object_set_new(status, "libnice_debug", json_integer(janus_ice_is_ice_debugging_enabled()));
1403
                        json_object_set_new(status, "max_nack_queue", json_integer(janus_get_max_nack_queue()));
1404
                        json_object_set_new(reply, "status", status);
1405
                        /* Send the success reply */
1406
                        ret = janus_process_success(request, reply);
1407
                        goto jsondone;
1408
                } else if(!strcasecmp(message_text, "set_log_level")) {
1409
                        /* Change the debug logging level */
1410
                        json_t *level = json_object_get(root, "level");
1411
                        if(!level) {
1412
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "Missing mandatory element (level)");
1413
                                goto jsondone;
1414
                        }
1415
                        if(!json_is_integer(level) || json_integer_value(level) < 0) {
1416
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (level should be a positive integer)");
1417
                                goto jsondone;
1418
                        }
1419
                        int level_num = json_integer_value(level);
1420
                        if(level_num < LOG_NONE || level_num > LOG_MAX) {
1421
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (level should be between %d and %d)", LOG_NONE, LOG_MAX);
1422
                                goto jsondone;
1423
                        }
1424
                        janus_log_level = level_num;
1425
                        /* Prepare JSON reply */
1426
                        json_t *reply = json_object();
1427
                        json_object_set_new(reply, "janus", json_string("success"));
1428
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1429
                        json_object_set_new(reply, "level", json_integer(janus_log_level));
1430
                        /* Send the success reply */
1431
                        ret = janus_process_success(request, reply);
1432
                        goto jsondone;
1433
                } else if(!strcasecmp(message_text, "set_locking_debug")) {
1434
                        /* Enable/disable the locking debug (would show a message on the console for every lock attempt) */
1435
                        json_t *debug = json_object_get(root, "debug");
1436
                        if(!debug) {
1437
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "Missing mandatory element (debug)");
1438
                                goto jsondone;
1439
                        }
1440
                        if(!json_is_integer(debug) || json_integer_value(debug) < 0) {
1441
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (debug should be a positive integer)");
1442
                                goto jsondone;
1443
                        }
1444
                        int debug_num = json_integer_value(debug);
1445
                        if(debug_num < 0 || debug_num > 1) {
1446
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (debug should be either 0 or 1)");
1447
                                goto jsondone;
1448
                        }
1449
                        lock_debug = debug_num;
1450
                        /* Prepare JSON reply */
1451
                        json_t *reply = json_object();
1452
                        json_object_set_new(reply, "janus", json_string("success"));
1453
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1454
                        json_object_set_new(reply, "locking_debug", json_integer(lock_debug));
1455
                        /* Send the success reply */
1456
                        ret = janus_process_success(request, reply);
1457
                        goto jsondone;
1458
                } else if(!strcasecmp(message_text, "set_log_timestamps")) {
1459
                        /* Enable/disable the log timestamps */
1460
                        json_t *timestamps = json_object_get(root, "timestamps");
1461
                        if(!timestamps) {
1462
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "Missing mandatory element (timestamps)");
1463
                                goto jsondone;
1464
                        }
1465
                        if(!json_is_integer(timestamps) || json_integer_value(timestamps) < 0) {
1466
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (timestamps should be a positive integer)");
1467
                                goto jsondone;
1468
                        }
1469
                        int timestamps_num = json_integer_value(timestamps);
1470
                        if(timestamps_num < 0 || timestamps_num > 1) {
1471
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (timestamps should be either 0 or 1)");
1472
                                goto jsondone;
1473
                        }
1474
                        janus_log_timestamps = timestamps_num;
1475
                        /* Prepare JSON reply */
1476
                        json_t *reply = json_object();
1477
                        json_object_set_new(reply, "janus", json_string("success"));
1478
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1479
                        json_object_set_new(reply, "log_timestamps", json_integer(janus_log_timestamps));
1480
                        /* Send the success reply */
1481
                        ret = janus_process_success(request, reply);
1482
                        goto jsondone;
1483
                } else if(!strcasecmp(message_text, "set_log_colors")) {
1484
                        /* Enable/disable the log colors */
1485
                        json_t *colors = json_object_get(root, "colors");
1486
                        if(!colors) {
1487
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "Missing mandatory element (colors)");
1488
                                goto jsondone;
1489
                        }
1490
                        if(!json_is_integer(colors) || json_integer_value(colors) < 0) {
1491
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (colors should be a positive integer)");
1492
                                goto jsondone;
1493
                        }
1494
                        int colors_num = json_integer_value(colors);
1495
                        if(colors_num < 0 || colors_num > 1) {
1496
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (colors should be either 0 or 1)");
1497
                                goto jsondone;
1498
                        }
1499
                        janus_log_colors = colors_num;
1500
                        /* Prepare JSON reply */
1501
                        json_t *reply = json_object();
1502
                        json_object_set_new(reply, "janus", json_string("success"));
1503
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1504
                        json_object_set_new(reply, "log_colors", json_integer(janus_log_colors));
1505
                        /* Send the success reply */
1506
                        ret = janus_process_success(request, reply);
1507
                        goto jsondone;
1508
                } else if(!strcasecmp(message_text, "set_libnice_debug")) {
1509
                        /* Enable/disable the libnice debugging (http://nice.freedesktop.org/libnice/libnice-Debug-messages.html) */
1510
                        json_t *debug = json_object_get(root, "debug");
1511
                        if(!debug) {
1512
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "Missing mandatory element (debug)");
1513
                                goto jsondone;
1514
                        }
1515
                        if(!json_is_integer(debug) || json_integer_value(debug) < 0) {
1516
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (debug should be a positive integer)");
1517
                                goto jsondone;
1518
                        }
1519
                        int debug_num = json_integer_value(debug);
1520
                        if(debug_num < 0 || debug_num > 1) {
1521
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (debug should be either 0 or 1)");
1522
                                goto jsondone;
1523
                        }
1524
                        if(debug_num) {
1525
                                janus_ice_debugging_enable();
1526
                        } else {
1527
                                janus_ice_debugging_disable();
1528
                        }
1529
                        /* Prepare JSON reply */
1530
                        json_t *reply = json_object();
1531
                        json_object_set_new(reply, "janus", json_string("success"));
1532
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1533
                        json_object_set_new(reply, "libnice_debug", json_integer(janus_ice_is_ice_debugging_enabled()));
1534
                        /* Send the success reply */
1535
                        ret = janus_process_success(request, reply);
1536
                        goto jsondone;
1537
                } else if(!strcasecmp(message_text, "set_max_nack_queue")) {
1538
                        /* Change the current value for the max NACK queue */
1539
                        json_t *mnq = json_object_get(root, "max_nack_queue");
1540
                        if(!mnq) {
1541
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "Missing mandatory element (max_nack_queue)");
1542
                                goto jsondone;
1543
                        }
1544
                        if(!json_is_integer(mnq) || json_integer_value(mnq) < 0) {
1545
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (max_nack_queue should be a positive integer)");
1546
                                goto jsondone;
1547
                        }
1548
                        int mnq_num = json_integer_value(mnq);
1549
                        if(mnq_num < 0) {
1550
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (max_nack_queue should be a positive integer)");
1551
                                goto jsondone;
1552
                        }
1553
                        janus_set_max_nack_queue(mnq_num);
1554
                        /* Prepare JSON reply */
1555
                        json_t *reply = json_object();
1556
                        json_object_set_new(reply, "janus", json_string("success"));
1557
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1558
                        json_object_set_new(reply, "max_nack_queue", json_integer(janus_get_max_nack_queue()));
1559
                        /* Send the success reply */
1560
                        ret = janus_process_success(request, reply);
1561
                        goto jsondone;
1562
                } else if(!strcasecmp(message_text, "list_sessions")) {
1563
                        /* List sessions */
1564
                        session_id = 0;
1565
                        json_t *list = json_array();
1566
                        if(sessions != NULL && g_hash_table_size(sessions) > 0) {
1567
                                janus_mutex_lock(&sessions_mutex);
1568
                                GHashTableIter iter;
1569
                                gpointer value;
1570
                                g_hash_table_iter_init(&iter, sessions);
1571
                                while (g_hash_table_iter_next(&iter, NULL, &value)) {
1572
                                        janus_session *session = value;
1573
                                        if(session == NULL) {
1574
                                                continue;
1575
                                        }
1576
                                        json_array_append_new(list, json_integer(session->session_id));
1577
                                }
1578
                                janus_mutex_unlock(&sessions_mutex);
1579
                        }
1580
                        /* Prepare JSON reply */
1581
                        json_t *reply = json_object();
1582
                        json_object_set_new(reply, "janus", json_string("success"));
1583
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1584
                        json_object_set_new(reply, "sessions", list);
1585
                        /* Send the success reply */
1586
                        ret = janus_process_success(request, reply);
1587
                        goto jsondone;
1588
                } else if(!strcasecmp(message_text, "add_token")) {
1589
                        /* Add a token valid for authentication */
1590
                        if(!janus_auth_is_enabled()) {
1591
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Token based authentication disabled");
1592
                                goto jsondone;
1593
                        }
1594
                        json_t *token = json_object_get(root, "token");
1595
                        if(!token) {
1596
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "Missing mandatory element (token)");
1597
                                goto jsondone;
1598
                        }
1599
                        if(!json_is_string(token)) {
1600
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (token should be a string)");
1601
                                goto jsondone;
1602
                        }
1603
                        const char *token_value = json_string_value(token);
1604
                        /* Any plugin this token should be limited to? */
1605
                        json_t *allowed = json_object_get(root, "plugins");
1606
                        if(allowed && !json_is_array(allowed)) {
1607
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (plugins should be an array)");
1608
                                goto jsondone;
1609
                        }
1610
                        /* First of all, add the new token */
1611
                        if(!janus_auth_add_token(token_value)) {
1612
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Error adding token");
1613
                                goto jsondone;
1614
                        }
1615
                        /* Then take care of the plugins access limitations, if any */
1616
                        if(allowed && json_array_size(allowed) > 0) {
1617
                                /* Specify which plugins this token has access to */
1618
                                size_t i = 0;
1619
                                for(i=0; i<json_array_size(allowed); i++) {
1620
                                        json_t *p = json_array_get(allowed, i);
1621
                                        if(!p || !json_is_string(p)) {
1622
                                                /* FIXME Should we fail here? */
1623
                                                JANUS_LOG(LOG_WARN, "Invalid plugin passed to the new token request, skipping...\n");
1624
                                                continue;
1625
                                        }
1626
                                        const gchar *plugin_text = json_string_value(p);
1627
                                        janus_plugin *plugin_t = janus_plugin_find(plugin_text);
1628
                                        if(plugin_t == NULL) {
1629
                                                /* FIXME Should we fail here? */
1630
                                                JANUS_LOG(LOG_WARN, "No such plugin '%s' passed to the new token request, skipping...\n", plugin_text);
1631
                                                continue;
1632
                                        }
1633
                                        if(!janus_auth_allow_plugin(token_value, plugin_t)) {
1634
                                                JANUS_LOG(LOG_WARN, "Error allowing access to '%s' to the new token, bad things may happen...\n", plugin_text);
1635
                                        }
1636
                                }
1637
                        } else {
1638
                                /* No plugin limitation specified, allow all plugins */
1639
                                if(plugins && g_hash_table_size(plugins) > 0) {
1640
                                        GHashTableIter iter;
1641
                                        gpointer value;
1642
                                        g_hash_table_iter_init(&iter, plugins);
1643
                                        while (g_hash_table_iter_next(&iter, NULL, &value)) {
1644
                                                janus_plugin *plugin_t = value;
1645
                                                if(plugin_t == NULL)
1646
                                                        continue;
1647
                                                if(!janus_auth_allow_plugin(token_value, plugin_t)) {
1648
                                                        JANUS_LOG(LOG_WARN, "Error allowing access to '%s' to the new token, bad things may happen...\n", plugin_t->get_package());
1649
                                                }
1650
                                        }
1651
                                }
1652
                        }
1653
                        /* Get the list of plugins this new token can access */
1654
                        json_t *plugins_list = json_array();
1655
                        GList *plugins = janus_auth_list_plugins(token_value);
1656
                        if(plugins != NULL) {
1657
                                GList *tmp = plugins;
1658
                                while(tmp) {
1659
                                        janus_plugin *p = (janus_plugin *)tmp->data;
1660
                                        if(p != NULL)
1661
                                                json_array_append_new(plugins_list, json_string(p->get_package()));
1662
                                        tmp = tmp->next;
1663
                                }
1664
                                g_list_free(plugins);
1665
                                plugins = NULL;
1666
                        }
1667
                        /* Prepare JSON reply */
1668
                        json_t *reply = json_object();
1669
                        json_object_set_new(reply, "janus", json_string("success"));
1670
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1671
                        json_t *data = json_object();
1672
                        json_object_set_new(data, "plugins", plugins_list);
1673
                        json_object_set_new(reply, "data", data);
1674
                        /* Send the success reply */
1675
                        ret = janus_process_success(request, reply);
1676
                        goto jsondone;
1677
                } else if(!strcasecmp(message_text, "list_tokens")) {
1678
                        /* List all the valid tokens */
1679
                        if(!janus_auth_is_enabled()) {
1680
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Token based authentication disabled");
1681
                                goto jsondone;
1682
                        }
1683
                        json_t *tokens_list = json_array();
1684
                        GList *list = janus_auth_list_tokens();
1685
                        if(list != NULL) {
1686
                                GList *tmp = list;
1687
                                while(tmp) {
1688
                                        char *token = (char *)tmp->data;
1689
                                        if(token != NULL) {
1690
                                                GList *plugins = janus_auth_list_plugins(token);
1691
                                                if(plugins != NULL) {
1692
                                                        json_t *t = json_object();
1693
                                                        json_object_set_new(t, "token", json_string(token));
1694
                                                        json_t *plugins_list = json_array();
1695
                                                        GList *tmp2 = plugins;
1696
                                                        while(tmp2) {
1697
                                                                janus_plugin *p = (janus_plugin *)tmp2->data;
1698
                                                                if(p != NULL)
1699
                                                                        json_array_append_new(plugins_list, json_string(p->get_package()));
1700
                                                                tmp2 = tmp2->next;
1701
                                                        }
1702
                                                        g_list_free(plugins);
1703
                                                        plugins = NULL;
1704
                                                        json_object_set_new(t, "allowed_plugins", plugins_list);
1705
                                                        json_array_append_new(tokens_list, t);
1706
                                                }
1707
                                                tmp->data = NULL;
1708
                                                g_free(token);
1709
                                        }
1710
                                        tmp = tmp->next;
1711
                                }
1712
                                g_list_free(list);
1713
                        }
1714
                        /* Prepare JSON reply */
1715
                        json_t *reply = json_object();
1716
                        json_object_set_new(reply, "janus", json_string("success"));
1717
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1718
                        json_t *data = json_object();
1719
                        json_object_set_new(data, "tokens", tokens_list);
1720
                        json_object_set_new(reply, "data", data);
1721
                        /* Send the success reply */
1722
                        ret = janus_process_success(request, reply);
1723
                        goto jsondone;
1724
                } else if(!strcasecmp(message_text, "allow_token")) {
1725
                        /* Allow a valid token valid to access a plugin */
1726
                        if(!janus_auth_is_enabled()) {
1727
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Token based authentication disabled");
1728
                                goto jsondone;
1729
                        }
1730
                        json_t *token = json_object_get(root, "token");
1731
                        if(!token) {
1732
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "Missing mandatory element (token)");
1733
                                goto jsondone;
1734
                        }
1735
                        if(!json_is_string(token)) {
1736
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (token should be a string)");
1737
                                goto jsondone;
1738
                        }
1739
                        const char *token_value = json_string_value(token);
1740
                        /* Check if the token is valid, first */
1741
                        if(!janus_auth_check_token(token_value)) {
1742
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_TOKEN_NOT_FOUND, "Token %s not found", token_value);
1743
                                goto jsondone;
1744
                        }
1745
                        /* Any plugin this token should be limited to? */
1746
                        json_t *allowed = json_object_get(root, "plugins");
1747
                        if(!allowed) {
1748
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "Missing mandatory element (plugins)");
1749
                                goto jsondone;
1750
                        }
1751
                        if(!json_is_array(allowed) || json_array_size(allowed) == 0) {
1752
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (plugins should be an array)");
1753
                                goto jsondone;
1754
                        }
1755
                        /* Check the list first */
1756
                        size_t i = 0;
1757
                        gboolean ok = TRUE;
1758
                        for(i=0; i<json_array_size(allowed); i++) {
1759
                                json_t *p = json_array_get(allowed, i);
1760
                                if(!p || !json_is_string(p)) {
1761
                                        /* FIXME Should we fail here? */
1762
                                        JANUS_LOG(LOG_ERR, "Invalid plugin passed to the new token request...\n");
1763
                                        ok = FALSE;
1764
                                        break;
1765
                                }
1766
                                const gchar *plugin_text = json_string_value(p);
1767
                                janus_plugin *plugin_t = janus_plugin_find(plugin_text);
1768
                                if(plugin_t == NULL) {
1769
                                        /* FIXME Should we fail here? */
1770
                                        JANUS_LOG(LOG_ERR, "No such plugin '%s' passed to the new token request...\n", plugin_text);
1771
                                        ok = FALSE;
1772
                                        break;
1773
                                }
1774
                        }
1775
                        if(!ok) {
1776
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (some of the provided plugins are invalid)");
1777
                                goto jsondone;
1778
                        }
1779
                        /* Take care of the plugins access limitations */
1780
                        i = 0;
1781
                        for(i=0; i<json_array_size(allowed); i++) {
1782
                                json_t *p = json_array_get(allowed, i);
1783
                                const gchar *plugin_text = json_string_value(p);
1784
                                janus_plugin *plugin_t = janus_plugin_find(plugin_text);
1785
                                if(!janus_auth_allow_plugin(token_value, plugin_t)) {
1786
                                        /* FIXME Should we notify individual failures? */
1787
                                        JANUS_LOG(LOG_WARN, "Error allowing access to '%s' to the new token, bad things may happen...\n", plugin_text);
1788
                                }
1789
                        }
1790
                        /* Get the list of plugins this new token can now access */
1791
                        json_t *plugins_list = json_array();
1792
                        GList *plugins = janus_auth_list_plugins(token_value);
1793
                        if(plugins != NULL) {
1794
                                GList *tmp = plugins;
1795
                                while(tmp) {
1796
                                        janus_plugin *p = (janus_plugin *)tmp->data;
1797
                                        if(p != NULL)
1798
                                                json_array_append_new(plugins_list, json_string(p->get_package()));
1799
                                        tmp = tmp->next;
1800
                                }
1801
                                g_list_free(plugins);
1802
                                plugins = NULL;
1803
                        }
1804
                        /* Prepare JSON reply */
1805
                        json_t *reply = json_object();
1806
                        json_object_set_new(reply, "janus", json_string("success"));
1807
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1808
                        json_t *data = json_object();
1809
                        json_object_set_new(data, "plugins", plugins_list);
1810
                        json_object_set_new(reply, "data", data);
1811
                        /* Send the success reply */
1812
                        ret = janus_process_success(request, reply);
1813
                        goto jsondone;
1814
                } else if(!strcasecmp(message_text, "disallow_token")) {
1815
                        /* Disallow a valid token valid from accessing a plugin */
1816
                        if(!janus_auth_is_enabled()) {
1817
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Token based authentication disabled");
1818
                                goto jsondone;
1819
                        }
1820
                        json_t *token = json_object_get(root, "token");
1821
                        if(!token) {
1822
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "Missing mandatory element (token)");
1823
                                goto jsondone;
1824
                        }
1825
                        if(!json_is_string(token)) {
1826
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (token should be a string)");
1827
                                goto jsondone;
1828
                        }
1829
                        const char *token_value = json_string_value(token);
1830
                        /* Check if the token is valid, first */
1831
                        if(!janus_auth_check_token(token_value)) {
1832
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_TOKEN_NOT_FOUND, "Token %s not found", token_value);
1833
                                goto jsondone;
1834
                        }
1835
                        /* Any plugin this token should be prevented access to? */
1836
                        json_t *allowed = json_object_get(root, "plugins");
1837
                        if(!allowed) {
1838
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "Missing mandatory element (plugins)");
1839
                                goto jsondone;
1840
                        }
1841
                        if(!json_is_array(allowed) || json_array_size(allowed) == 0) {
1842
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (plugins should be an array)");
1843
                                goto jsondone;
1844
                        }
1845
                        /* Check the list first */
1846
                        size_t i = 0;
1847
                        gboolean ok = TRUE;
1848
                        for(i=0; i<json_array_size(allowed); i++) {
1849
                                json_t *p = json_array_get(allowed, i);
1850
                                if(!p || !json_is_string(p)) {
1851
                                        /* FIXME Should we fail here? */
1852
                                        JANUS_LOG(LOG_ERR, "Invalid plugin passed to the new token request...\n");
1853
                                        ok = FALSE;
1854
                                        break;
1855
                                }
1856
                                const gchar *plugin_text = json_string_value(p);
1857
                                janus_plugin *plugin_t = janus_plugin_find(plugin_text);
1858
                                if(plugin_t == NULL) {
1859
                                        /* FIXME Should we fail here? */
1860
                                        JANUS_LOG(LOG_ERR, "No such plugin '%s' passed to the new token request...\n", plugin_text);
1861
                                        ok = FALSE;
1862
                                        break;
1863
                                }
1864
                        }
1865
                        if(!ok) {
1866
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (some of the provided plugins are invalid)");
1867
                                goto jsondone;
1868
                        }
1869
                        /* Take care of the plugins access limitations */
1870
                        i = 0;
1871
                        for(i=0; i<json_array_size(allowed); i++) {
1872
                                json_t *p = json_array_get(allowed, i);
1873
                                const gchar *plugin_text = json_string_value(p);
1874
                                janus_plugin *plugin_t = janus_plugin_find(plugin_text);
1875
                                if(!janus_auth_disallow_plugin(token_value, plugin_t)) {
1876
                                        /* FIXME Should we notify individual failures? */
1877
                                        JANUS_LOG(LOG_WARN, "Error allowing access to '%s' to the new token, bad things may happen...\n", plugin_text);
1878
                                }
1879
                        }
1880
                        /* Get the list of plugins this new token can now access */
1881
                        json_t *plugins_list = json_array();
1882
                        GList *plugins = janus_auth_list_plugins(token_value);
1883
                        if(plugins != NULL) {
1884
                                GList *tmp = plugins;
1885
                                while(tmp) {
1886
                                        janus_plugin *p = (janus_plugin *)tmp->data;
1887
                                        if(p != NULL)
1888
                                                json_array_append_new(plugins_list, json_string(p->get_package()));
1889
                                        tmp = tmp->next;
1890
                                }
1891
                                g_list_free(plugins);
1892
                                plugins = NULL;
1893
                        }
1894
                        /* Prepare JSON reply */
1895
                        json_t *reply = json_object();
1896
                        json_object_set_new(reply, "janus", json_string("success"));
1897
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1898
                        json_t *data = json_object();
1899
                        json_object_set_new(data, "plugins", plugins_list);
1900
                        json_object_set_new(reply, "data", data);
1901
                        /* Send the success reply */
1902
                        ret = janus_process_success(request, reply);
1903
                        goto jsondone;
1904
                } else if(!strcasecmp(message_text, "remove_token")) {
1905
                        /* Invalidate a token for authentication purposes */
1906
                        if(!janus_auth_is_enabled()) {
1907
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Token based authentication disabled");
1908
                                goto jsondone;
1909
                        }
1910
                        json_t *token = json_object_get(root, "token");
1911
                        if(!token) {
1912
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "Missing mandatory element (token)");
1913
                                goto jsondone;
1914
                        }
1915
                        if(!json_is_string(token)) {
1916
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (token should be a string)");
1917
                                goto jsondone;
1918
                        }
1919
                        const char *token_value = json_string_value(token);
1920
                        if(!janus_auth_remove_token(token_value)) {
1921
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Error removing token");
1922
                                goto jsondone;
1923
                        }
1924
                        /* Prepare JSON reply */
1925
                        json_t *reply = json_object();
1926
                        json_object_set_new(reply, "janus", json_string("success"));
1927
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1928
                        /* Send the success reply */
1929
                        ret = janus_process_success(request, reply);
1930
                        goto jsondone;
1931
                } else {
1932
                        /* No message we know of */
1933
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
1934
                        goto jsondone;
1935
                }
1936
        }
1937
        if(session_id < 1) {
1938
                JANUS_LOG(LOG_ERR, "Invalid session\n");
1939
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, NULL);
1940
                goto jsondone;
1941
        }
1942
        if(h && handle_id < 1) {
1943
                JANUS_LOG(LOG_ERR, "Invalid handle\n");
1944
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, NULL);
1945
                goto jsondone;
1946
        }
1947

    
1948
        /* Go on with the processing */
1949
        if(admin_api_secret != NULL) {
1950
                /* There's an API secret, check that the client provided it */
1951
                json_t *secret = json_object_get(root, "admin_secret");
1952
                if(!secret || !json_is_string(secret) || !janus_strcmp_const_time(json_string_value(secret), admin_api_secret)) {
1953
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED, NULL);
1954
                        goto jsondone;
1955
                }
1956
        }
1957

    
1958
        /* If we got here, make sure we have a session (and/or a handle) */
1959
        janus_session *session = janus_session_find(session_id);
1960
        if(!session) {
1961
                JANUS_LOG(LOG_ERR, "Couldn't find any session %"SCNu64"...\n", session_id);
1962
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, "No such session %"SCNu64"", session_id);
1963
                goto jsondone;
1964
        }
1965
        janus_ice_handle *handle = NULL;
1966
        if(handle_id > 0) {
1967
                handle = janus_ice_handle_find(session, handle_id);
1968
                if(!handle) {
1969
                        JANUS_LOG(LOG_ERR, "Couldn't find any handle %"SCNu64" in session %"SCNu64"...\n", handle_id, session_id);
1970
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_HANDLE_NOT_FOUND, "No such handle %"SCNu64" in session %"SCNu64"", handle_id, session_id);
1971
                        goto jsondone;
1972
                }
1973
        }
1974

    
1975
        /* What is this? */
1976
        if(handle == NULL) {
1977
                /* Session-related */
1978
                if(strcasecmp(message_text, "list_handles")) {
1979
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
1980
                        goto jsondone;
1981
                }
1982
                /* List handles */
1983
                json_t *list = json_array();
1984
                if(session->ice_handles != NULL && g_hash_table_size(session->ice_handles) > 0) {
1985
                        GHashTableIter iter;
1986
                        gpointer value;
1987
                        janus_mutex_lock(&session->mutex);
1988
                        g_hash_table_iter_init(&iter, session->ice_handles);
1989
                        while (g_hash_table_iter_next(&iter, NULL, &value)) {
1990
                                janus_ice_handle *handle = value;
1991
                                if(handle == NULL) {
1992
                                        continue;
1993
                                }
1994
                                json_array_append_new(list, json_integer(handle->handle_id));
1995
                        }
1996
                        janus_mutex_unlock(&session->mutex);
1997
                }
1998
                /* Prepare JSON reply */
1999
                json_t *reply = json_object();
2000
                json_object_set_new(reply, "janus", json_string("success"));
2001
                json_object_set_new(reply, "transaction", json_string(transaction_text));
2002
                json_object_set_new(reply, "session_id", json_integer(session_id));
2003
                json_object_set_new(reply, "handles", list);
2004
                /* Send the success reply */
2005
                ret = janus_process_success(request, reply);
2006
                goto jsondone;
2007
        } else {
2008
                /* Handle-related */
2009
                if(strcasecmp(message_text, "handle_info")) {
2010
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
2011
                        goto jsondone;
2012
                }
2013
                /* Prepare info */
2014
                janus_mutex_lock(&handle->mutex);
2015
                json_t *info = json_object();
2016
                json_object_set_new(info, "session_id", json_integer(session_id));
2017
                json_object_set_new(info, "session_last_activity", json_integer(session->last_activity));
2018
                if(session->source && session->source->transport)
2019
                        json_object_set_new(info, "session_transport", json_string(session->source->transport->get_package()));
2020
                json_object_set_new(info, "handle_id", json_integer(handle_id));
2021
                json_object_set_new(info, "created", json_integer(handle->created));
2022
                json_object_set_new(info, "current_time", json_integer(janus_get_monotonic_time()));
2023
                if(handle->app && handle->app_handle && janus_plugin_session_is_alive(handle->app_handle)) {
2024
                        janus_plugin *plugin = (janus_plugin *)handle->app;
2025
                        json_object_set_new(info, "plugin", json_string(plugin->get_package()));
2026
                        if(plugin->query_session) {
2027
                                /* FIXME This check will NOT work with legacy plugins that were compiled BEFORE the method was specified in plugin.h */
2028
                                char *query = plugin->query_session(handle->app_handle);
2029
                                if(query != NULL) {
2030
                                        /* Make sure this is JSON */
2031
                                        json_error_t error;
2032
                                        json_t *query_info = json_loads(query, 0, &error);
2033
                                        if(!query_info || !json_is_object(query_info)) {
2034
                                                JANUS_LOG(LOG_WARN, "Ignoring invalid query response from the plugin\n");
2035
                                        } else {
2036
                                                json_object_set_new(info, "plugin_specific", query_info);
2037
                                        }
2038
                                        g_free(query);
2039
                                        query = NULL;
2040
                                }
2041
                        }
2042
                }
2043
                json_t *flags = json_object();
2044
                json_object_set_new(flags, "got-offer", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER)));
2045
                json_object_set_new(flags, "got-answer", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER)));
2046
                json_object_set_new(flags, "processing-offer", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER)));
2047
                json_object_set_new(flags, "starting", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_START)));
2048
                json_object_set_new(flags, "ready", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_READY)));
2049
                json_object_set_new(flags, "stopped", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)));
2050
                json_object_set_new(flags, "alert", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)));
2051
                json_object_set_new(flags, "bundle", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)));
2052
                json_object_set_new(flags, "rtcp-mux", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX)));
2053
                json_object_set_new(flags, "trickle", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE)));
2054
                json_object_set_new(flags, "all-trickles", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALL_TRICKLES)));
2055
                json_object_set_new(flags, "trickle-synced", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE_SYNCED)));
2056
                json_object_set_new(flags, "data-channels", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS)));
2057
                json_object_set_new(flags, "has-audio", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO)));
2058
                json_object_set_new(flags, "has-video", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO)));
2059
                json_object_set_new(flags, "plan-b", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PLAN_B)));
2060
                json_object_set_new(flags, "cleaning", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING)));
2061
                json_object_set_new(info, "flags", flags);
2062
                if(handle->agent) {
2063
                        json_object_set_new(info, "agent-created", json_integer(handle->agent_created));
2064
                        json_object_set_new(info, "ice-mode", json_string(janus_ice_is_ice_lite_enabled() ? "lite" : "full"));
2065
                        json_object_set_new(info, "ice-role", json_string(handle->controlling ? "controlling" : "controlled"));
2066
                }
2067
                json_t *sdps = json_object();
2068
                if(handle->local_sdp)
2069
                        json_object_set_new(sdps, "local", json_string(handle->local_sdp));
2070
                if(handle->remote_sdp)
2071
                        json_object_set_new(sdps, "remote", json_string(handle->remote_sdp));
2072
                json_object_set_new(info, "sdps", sdps);
2073
                if(handle->pending_trickles)
2074
                        json_object_set_new(info, "pending-trickles", json_integer(g_list_length(handle->pending_trickles)));
2075
                json_t *streams = json_array();
2076
                if(handle->audio_stream) {
2077
                        json_t *s = janus_admin_stream_summary(handle->audio_stream);
2078
                        if(s)
2079
                                json_array_append_new(streams, s);
2080
                }
2081
                if(handle->video_stream) {
2082
                        json_t *s = janus_admin_stream_summary(handle->video_stream);
2083
                        if(s)
2084
                                json_array_append_new(streams, s);
2085
                }
2086
                if(handle->data_stream) {
2087
                        json_t *s = janus_admin_stream_summary(handle->data_stream);
2088
                        if(s)
2089
                                json_array_append_new(streams, s);
2090
                }
2091
                json_object_set_new(info, "streams", streams);
2092
                janus_mutex_unlock(&handle->mutex);
2093
                /* Prepare JSON reply */
2094
                json_t *reply = json_object();
2095
                json_object_set_new(reply, "janus", json_string("success"));
2096
                json_object_set_new(reply, "transaction", json_string(transaction_text));
2097
                json_object_set_new(reply, "session_id", json_integer(session_id));
2098
                json_object_set_new(reply, "handle_id", json_integer(handle_id));
2099
                json_object_set_new(reply, "info", info);
2100
                /* Send the success reply */
2101
                ret = janus_process_success(request, reply);
2102
                goto jsondone;
2103
        }
2104

    
2105
jsondone:
2106
        /* Done processing */
2107
        return ret;
2108
}
2109

    
2110
int janus_process_success(janus_request *request, json_t *payload)
2111
{
2112
        if(!request || !payload)
2113
                return -1;
2114
        /* Pass to the right transport plugin */
2115
        JANUS_LOG(LOG_HUGE, "Sending %s API response to %s (%p)\n", request->admin ? "admin" : "Janus", request->transport->get_package(), request->instance);
2116
        return request->transport->send_message(request->instance, request->request_id, request->admin, payload);
2117
}
2118

    
2119
int janus_process_error(janus_request *request, uint64_t session_id, const char *transaction, gint error, const char *format, ...)
2120
{
2121
        if(!request)
2122
                return -1;
2123
        gchar *error_string = NULL;
2124
        gchar error_buf[512];
2125
        if(format == NULL) {
2126
                /* No error string provided, use the default one */
2127
                error_string = (gchar *)janus_get_api_error(error);
2128
        } else {
2129
                /* This callback has variable arguments (error string) */
2130
                va_list ap;
2131
                va_start(ap, format);
2132
                g_vsnprintf(error_buf, sizeof(error_buf), format, ap);
2133
                va_end(ap);
2134
                error_string = error_buf;
2135
        }
2136
        /* Done preparing error */
2137
        JANUS_LOG(LOG_VERB, "[%s] Returning %s API error %d (%s)\n", transaction, request->admin ? "admin" : "Janus", error, error_string);
2138
        /* Prepare JSON error */
2139
        json_t *reply = json_object();
2140
        json_object_set_new(reply, "janus", json_string("error"));
2141
        if(session_id > 0)
2142
                json_object_set_new(reply, "session_id", json_integer(session_id));
2143
        if(transaction != NULL)
2144
                json_object_set_new(reply, "transaction", json_string(transaction));
2145
        json_t *error_data = json_object();
2146
        json_object_set_new(error_data, "code", json_integer(error));
2147
        json_object_set_new(error_data, "reason", json_string(error_string));
2148
        json_object_set_new(reply, "error", error_data);
2149
        /* Pass to the right transport plugin */
2150
        return request->transport->send_message(request->instance, request->request_id, request->admin, reply);
2151
}
2152

    
2153

    
2154
/* Admin/monitor helpers */
2155
json_t *janus_admin_stream_summary(janus_ice_stream *stream) {
2156
        if(stream == NULL)
2157
                return NULL;
2158
        json_t *s = json_object();
2159
        json_object_set_new(s, "id", json_integer(stream->stream_id));
2160
        json_object_set_new(s, "ready", json_integer(stream->cdone));
2161
        json_object_set_new(s, "disabled", json_string(stream->disabled ? "true" : "false"));
2162
        json_t *ss = json_object();
2163
        if(stream->audio_ssrc)
2164
                json_object_set_new(ss, "audio", json_integer(stream->audio_ssrc));
2165
        if(stream->video_ssrc)
2166
                json_object_set_new(ss, "video", json_integer(stream->video_ssrc));
2167
        if(stream->audio_ssrc_peer)
2168
                json_object_set_new(ss, "audio-peer", json_integer(stream->audio_ssrc_peer));
2169
        if(stream->video_ssrc_peer)
2170
                json_object_set_new(ss, "video-peer", json_integer(stream->video_ssrc_peer));
2171
        if(stream->video_ssrc_peer_rtx)
2172
                json_object_set_new(ss, "video-peer-rtx", json_integer(stream->video_ssrc_peer_rtx));
2173
        json_object_set_new(s, "ssrc", ss);
2174
        json_t *components = json_array();
2175
        if(stream->rtp_component) {
2176
                json_t *c = janus_admin_component_summary(stream->rtp_component);
2177
                if(c)
2178
                        json_array_append_new(components, c);
2179
        }
2180
        if(stream->rtcp_component) {
2181
                json_t *c = janus_admin_component_summary(stream->rtcp_component);
2182
                if(c)
2183
                        json_array_append_new(components, c);
2184
        }
2185
        json_object_set_new(s, "components", components);
2186
        return s;
2187
}
2188

    
2189
json_t *janus_admin_component_summary(janus_ice_component *component) {
2190
        if(component == NULL)
2191
                return NULL;
2192
        json_t *c = json_object();
2193
        json_object_set_new(c, "id", json_integer(component->component_id));
2194
        json_object_set_new(c, "state", json_string(janus_get_ice_state_name(component->state)));
2195
        if(component->component_connected > 0)
2196
                json_object_set_new(c, "connected", json_integer(component->component_connected));
2197
        if(component->local_candidates) {
2198
                json_t *cs = json_array();
2199
                GSList *candidates = component->local_candidates, *i = NULL;
2200
                for (i = candidates; i; i = i->next) {
2201
                        gchar *lc = (gchar *) i->data;
2202
                        if(lc)
2203
                                json_array_append_new(cs, json_string(lc));
2204
                }
2205
                json_object_set_new(c, "local-candidates", cs);
2206
        }
2207
        if(component->remote_candidates) {
2208
                json_t *cs = json_array();
2209
                GSList *candidates = component->remote_candidates, *i = NULL;
2210
                for (i = candidates; i; i = i->next) {
2211
                        gchar *rc = (gchar *) i->data;
2212
                        if(rc)
2213
                                json_array_append_new(cs, json_string(rc));
2214
                }
2215
                json_object_set_new(c, "remote-candidates", cs);
2216
        }
2217
        if(component->selected_pair) {
2218
                json_object_set_new(c, "selected-pair", json_string(component->selected_pair));
2219
        }
2220
        json_t *d = json_object();
2221
        json_t *in_stats = json_object();
2222
        json_t *out_stats = json_object();
2223
        if(component->dtls) {
2224
                janus_dtls_srtp *dtls = component->dtls;
2225
                json_object_set_new(d, "fingerprint", json_string(janus_dtls_get_local_fingerprint()));
2226
                json_object_set_new(d, "remote-fingerprint", json_string(component->stream->remote_fingerprint));
2227
                json_object_set_new(d, "remote-fingerprint-hash", json_string(component->stream->remote_hashing));
2228
                json_object_set_new(d, "dtls-role", json_string(janus_get_dtls_srtp_role(component->stream->dtls_role)));
2229
                json_object_set_new(d, "dtls-state", json_string(janus_get_dtls_srtp_state(dtls->dtls_state)));
2230
                json_object_set_new(d, "valid", json_integer(dtls->srtp_valid));
2231
                json_object_set_new(d, "ready", json_integer(dtls->ready));
2232
                if(dtls->dtls_connected > 0)
2233
                        json_object_set_new(d, "connected", json_integer(dtls->dtls_connected));
2234
                json_object_set_new(in_stats, "audio_bytes", json_integer(component->in_stats.audio_bytes));
2235
                json_object_set_new(in_stats, "video_bytes", json_integer(component->in_stats.video_bytes));
2236
                json_object_set_new(in_stats, "data_bytes", json_integer(component->in_stats.data_bytes));
2237
                json_object_set_new(in_stats, "audio_nacks", json_integer(component->in_stats.audio_nacks));
2238
                json_object_set_new(in_stats, "video_nacks", json_integer(component->in_stats.video_nacks));
2239
                json_object_set_new(out_stats, "audio_bytes", json_integer(component->out_stats.audio_bytes));
2240
                json_object_set_new(out_stats, "video_bytes", json_integer(component->out_stats.video_bytes));
2241
                json_object_set_new(out_stats, "data_bytes", json_integer(component->out_stats.data_bytes));
2242
                json_object_set_new(out_stats, "audio_nacks", json_integer(component->out_stats.audio_nacks));
2243
                json_object_set_new(out_stats, "video_nacks", json_integer(component->out_stats.video_nacks));
2244
                /* Compute the last second stuff too */
2245
                gint64 now = janus_get_monotonic_time();
2246
                guint64 bytes = 0;
2247
                if(component->in_stats.audio_bytes_lastsec) {
2248
                        GList *lastsec = component->in_stats.audio_bytes_lastsec;
2249
                        while(lastsec) {
2250
                                janus_ice_stats_item *s = (janus_ice_stats_item *)lastsec->data;
2251
                                if(s && now-s->when < G_USEC_PER_SEC)
2252
                                        bytes += s->bytes;
2253
                                lastsec = lastsec->next;
2254
                        }
2255
                }
2256
                json_object_set_new(in_stats, "audio_bytes_lastsec", json_integer(bytes));
2257
                bytes = 0;
2258
                if(component->in_stats.video_bytes_lastsec) {
2259
                        GList *lastsec = component->in_stats.video_bytes_lastsec;
2260
                        while(lastsec) {
2261
                                janus_ice_stats_item *s = (janus_ice_stats_item *)lastsec->data;
2262
                                if(s && now-s->when < G_USEC_PER_SEC)
2263
                                        bytes += s->bytes;
2264
                                lastsec = lastsec->next;
2265
                        }
2266
                }
2267
                json_object_set_new(in_stats, "video_bytes_lastsec", json_integer(bytes));
2268
#ifdef HAVE_SCTP
2269
                if(dtls->sctp)        /* FIXME */
2270
                        json_object_set_new(d, "sctp-association", json_integer(1));
2271
#endif
2272
        }
2273
        json_object_set_new(c, "dtls", d);
2274
        json_object_set_new(c, "in_stats", in_stats);
2275
        json_object_set_new(c, "out_stats", out_stats);
2276
        return c;
2277
}
2278

    
2279

    
2280
/* Transports */
2281
void janus_transport_close(gpointer key, gpointer value, gpointer user_data) {
2282
        janus_transport *transport = (janus_transport *)value;
2283
        if(!transport)
2284
                return;
2285
        transport->destroy();
2286
}
2287

    
2288
void janus_transportso_close(gpointer key, gpointer value, gpointer user_data) {
2289
        void *transport = (janus_transport *)value;
2290
        if(!transport)
2291
                return;
2292
        //~ dlclose(transport);
2293
}
2294

    
2295
/* Transport callback interface */
2296
void janus_transport_incoming_request(janus_transport *plugin, void *transport, void *request_id, gboolean admin, json_t *message, json_error_t *error) {
2297
        JANUS_LOG(LOG_VERB, "Got %s API request from %s (%p)\n", admin ? "an admin" : "a Janus", plugin->get_package(), transport);
2298
        /* Create a janus_request instance to handle the request */
2299
        janus_request *request = janus_request_new(plugin, transport, request_id, admin, message);
2300
        GError *tperror = NULL;
2301
        g_thread_pool_push(tasks, request, &tperror);
2302
        if(tperror != NULL) {
2303
                /* Something went wrong... */
2304
                JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to push task in thread pool...\n", tperror->code, tperror->message ? tperror->message : "??");
2305
                json_t *transaction = json_object_get(message, "transaction");
2306
                const char *transaction_text = json_is_string(transaction) ? json_string_value(transaction) : NULL;
2307
                janus_process_error(request, 0, transaction_text, JANUS_ERROR_UNKNOWN, "Thread pool error");
2308
                janus_request_destroy(request);
2309
        }
2310
}
2311

    
2312
void janus_transport_gone(janus_transport *plugin, void *transport) {
2313
        /* Get rid of sessions this transport was handling */
2314
        JANUS_LOG(LOG_VERB, "A %s transport instance has gone away (%p)\n", plugin->get_package(), transport);
2315
        janus_mutex_lock(&sessions_mutex);
2316
        if(sessions && g_hash_table_size(sessions) > 0) {
2317
                GHashTableIter iter;
2318
                gpointer value;
2319
                g_hash_table_iter_init(&iter, sessions);
2320
                while(g_hash_table_iter_next(&iter, NULL, &value)) {
2321
                        janus_session *session = (janus_session *) value;
2322
                        if(!session || session->destroy || session->timeout || session->last_activity == 0)
2323
                                continue;
2324
                        if(session->source && session->source->instance == transport) {
2325
                                JANUS_LOG(LOG_VERB, "  -- Marking Session %"SCNu64" as over\n", session->session_id);
2326
                                session->last_activity = 0;        /* This will trigger a timeout */
2327
                        }
2328
                }
2329
        }
2330
        janus_mutex_unlock(&sessions_mutex);
2331
}
2332

    
2333
gboolean janus_transport_is_api_secret_needed(janus_transport *plugin) {
2334
        return api_secret != NULL;
2335
}
2336

    
2337
gboolean janus_transport_is_api_secret_valid(janus_transport *plugin, const char *apisecret) {
2338
        if(api_secret == NULL)
2339
                return TRUE;
2340
        return apisecret && janus_strcmp_const_time(apisecret, api_secret);
2341
}
2342

    
2343
gboolean janus_transport_is_auth_token_needed(janus_transport *plugin) {
2344
        return janus_auth_is_enabled();
2345
}
2346

    
2347
gboolean janus_transport_is_auth_token_valid(janus_transport *plugin, const char *token) {
2348
        if(!janus_auth_is_enabled())
2349
                return TRUE;
2350
        return token && janus_auth_check_token(token);
2351
}
2352

    
2353
void janus_transport_task(gpointer data, gpointer user_data) {
2354
        JANUS_LOG(LOG_VERB, "Transport task pool, serving request\n");
2355
        janus_request *request = (janus_request *)data;
2356
        if(request == NULL) {
2357
                JANUS_LOG(LOG_ERR, "Missing request\n");
2358
                return;
2359
        }
2360
        if(!request->admin)
2361
                janus_process_incoming_request(request);
2362
        else
2363
                janus_process_incoming_admin_request(request);
2364
        /* Done */
2365
        janus_request_destroy(request);
2366
}
2367

    
2368

    
2369
/* Plugins */
2370
void janus_plugin_close(gpointer key, gpointer value, gpointer user_data) {
2371
        janus_plugin *plugin = (janus_plugin *)value;
2372
        if(!plugin)
2373
                return;
2374
        plugin->destroy();
2375
}
2376

    
2377
void janus_pluginso_close(gpointer key, gpointer value, gpointer user_data) {
2378
        void *plugin = (janus_plugin *)value;
2379
        if(!plugin)
2380
                return;
2381
        //~ dlclose(plugin);
2382
}
2383

    
2384
janus_plugin *janus_plugin_find(const gchar *package) {
2385
        if(package != NULL && plugins != NULL)        /* FIXME Do we need to fix the key pointer? */
2386
                return g_hash_table_lookup(plugins, package);
2387
        return NULL;
2388
}
2389

    
2390

    
2391
/* Plugin callback interface */
2392
int janus_plugin_push_event(janus_plugin_session *plugin_session, janus_plugin *plugin, const char *transaction, const char *message, const char *sdp_type, const char *sdp) {
2393
        if(!plugin || !message)
2394
                return -1;
2395
        if(!plugin_session || plugin_session < (janus_plugin_session *)0x1000 ||
2396
                        !janus_plugin_session_is_alive(plugin_session) || plugin_session->stopped)
2397
                return -2;
2398
        janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
2399
        if(!ice_handle || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP))
2400
                return JANUS_ERROR_SESSION_NOT_FOUND;
2401
        janus_session *session = ice_handle->session;
2402
        if(!session || session->destroy)
2403
                return JANUS_ERROR_SESSION_NOT_FOUND;
2404
        /* Make sure this is JSON */
2405
        json_error_t error;
2406
        json_t *plugin_event = json_loads(message, 0, &error);
2407
        if(!plugin_event) {
2408
                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (JSON error: on line %d: %s)\n", ice_handle->handle_id, error.line, error.text);
2409
                return JANUS_ERROR_INVALID_JSON;
2410
        }
2411
        if(!json_is_object(plugin_event)) {
2412
                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (JSON error: not an object)\n", ice_handle->handle_id);
2413
                return JANUS_ERROR_INVALID_JSON_OBJECT;
2414
        }
2415
        /* Attach JSEP if possible? */
2416
        json_t *jsep = NULL;
2417
        if(sdp_type != NULL && sdp != NULL) {
2418
                jsep = janus_plugin_handle_sdp(plugin_session, plugin, sdp_type, sdp);
2419
                if(jsep == NULL) {
2420
                        if(ice_handle == NULL || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2421
                                        || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)) {
2422
                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (handle not available anymore or negotiation stopped)\n", ice_handle->handle_id);
2423
                                return JANUS_ERROR_HANDLE_NOT_FOUND;
2424
                        } else {
2425
                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (JSON error: problem with the SDP)\n", ice_handle->handle_id);
2426
                                return JANUS_ERROR_JSEP_INVALID_SDP;
2427
                        }
2428
                }
2429
        }
2430
        /* Prepare JSON event */
2431
        json_t *event = json_object();
2432
        json_object_set_new(event, "janus", json_string("event"));
2433
        json_object_set_new(event, "session_id", json_integer(session->session_id));
2434
        json_object_set_new(event, "sender", json_integer(ice_handle->handle_id));
2435
        if(transaction != NULL)
2436
                json_object_set_new(event, "transaction", json_string(transaction));
2437
        json_t *plugin_data = json_object();
2438
        json_object_set_new(plugin_data, "plugin", json_string(plugin->get_package()));
2439
        json_object_set_new(plugin_data, "data", plugin_event);
2440
        json_object_set_new(event, "plugindata", plugin_data);
2441
        if(jsep != NULL)
2442
                json_object_set_new(event, "jsep", jsep);
2443
        /* Send the event */
2444
        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Sending event to transport...\n", ice_handle->handle_id);
2445
        janus_session_notify_event(session->session_id, event);
2446
        
2447
        return JANUS_OK;
2448
}
2449

    
2450
json_t *janus_plugin_handle_sdp(janus_plugin_session *plugin_session, janus_plugin *plugin, const char *sdp_type, const char *sdp) {
2451
        if(!plugin_session || plugin_session < (janus_plugin_session *)0x1000 ||
2452
                        !janus_plugin_session_is_alive(plugin_session) || plugin_session->stopped ||
2453
                        plugin == NULL || sdp_type == NULL || sdp == NULL) {
2454
                JANUS_LOG(LOG_ERR, "Invalid arguments\n");
2455
                return NULL;
2456
        }
2457
        janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
2458
        //~ if(ice_handle == NULL || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_READY)) {
2459
        if(ice_handle == NULL) {
2460
                JANUS_LOG(LOG_ERR, "Invalid ICE handle\n");
2461
                return NULL;
2462
        }
2463
        int offer = 0;
2464
        if(!strcasecmp(sdp_type, "offer")) {
2465
                /* This is an offer from a plugin */
2466
                offer = 1;
2467
                janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER);
2468
                janus_flags_clear(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER);
2469
        } else if(!strcasecmp(sdp_type, "answer")) {
2470
                /* This is an answer from a plugin */
2471
                janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER);
2472
        } else {
2473
                /* TODO Handle other messages */
2474
                JANUS_LOG(LOG_ERR, "Unknown type '%s'\n", sdp_type);
2475
                return NULL;
2476
        }
2477
        /* Is this valid SDP? */
2478
        int audio = 0, video = 0, data = 0, bundle = 0, rtcpmux = 0, trickle = 0;
2479
        janus_sdp *parsed_sdp = janus_sdp_preparse(sdp, &audio, &video, &data, &bundle, &rtcpmux, &trickle);
2480
        if(parsed_sdp == NULL) {
2481
                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Couldn't parse SDP...\n", ice_handle->handle_id);
2482
                return NULL;
2483
        }
2484
        janus_sdp_free(parsed_sdp);
2485
        gboolean updating = FALSE;
2486
        if(offer) {
2487
                /* We still don't have a local ICE setup */
2488
                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio %s been negotiated\n", ice_handle->handle_id, audio ? "has" : "has NOT");
2489
                if(audio > 1) {
2490
                        JANUS_LOG(LOG_ERR, "[%"SCNu64"] More than one audio line? only going to negotiate one...\n", ice_handle->handle_id);
2491
                }
2492
                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Video %s been negotiated\n", ice_handle->handle_id, video ? "has" : "has NOT");
2493
                if(video > 1) {
2494
                        JANUS_LOG(LOG_ERR, "[%"SCNu64"] More than one video line? only going to negotiate one...\n", ice_handle->handle_id);
2495
                }
2496
                JANUS_LOG(LOG_VERB, "[%"SCNu64"] SCTP/DataChannels %s been negotiated\n", ice_handle->handle_id, data ? "have" : "have NOT");
2497
                if(data > 1) {
2498
                        JANUS_LOG(LOG_ERR, "[%"SCNu64"] More than one data line? only going to negotiate one...\n", ice_handle->handle_id);
2499
                }
2500
#ifndef HAVE_SCTP
2501
                if(data) {
2502
                        JANUS_LOG(LOG_WARN, "[%"SCNu64"]   -- DataChannels have been negotiated, but support for them has not been compiled...\n", ice_handle->handle_id);
2503
                }
2504
#endif
2505
                /* Are we still cleaning up from a previous media session? */
2506
                if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING)) {
2507
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Still cleaning up from a previous media session, let's wait a bit...\n", ice_handle->handle_id);
2508
                        gint64 waited = 0;
2509
                        while(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING)) {
2510
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Still cleaning up from a previous media session, let's wait a bit...\n", ice_handle->handle_id);
2511
                                g_usleep(100000);
2512
                                waited += 100000;
2513
                                if(waited >= 3*G_USEC_PER_SEC) {
2514
                                        JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Waited 3 seconds, that's enough!\n", ice_handle->handle_id);
2515
                                        break;
2516
                                }
2517
                        }
2518
                }
2519
                if(ice_handle->agent == NULL) {
2520
                        /* Process SDP in order to setup ICE locally (this is going to result in an answer from the browser) */
2521
                        if(janus_ice_setup_local(ice_handle, 0, audio, video, data, bundle, rtcpmux, trickle) < 0) {
2522
                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error setting ICE locally\n", ice_handle->handle_id);
2523
                                return NULL;
2524
                        }
2525
                } else {
2526
                        updating = TRUE;
2527
                        JANUS_LOG(LOG_INFO, "[%"SCNu64"] Updating existing session\n", ice_handle->handle_id);
2528
                }
2529
        }
2530
        if(!updating) {
2531
                /* Wait for candidates-done callback */
2532
                while(ice_handle->cdone < ice_handle->streams_num) {
2533
                        if(ice_handle == NULL || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2534
                                        || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)) {
2535
                                JANUS_LOG(LOG_WARN, "[%"SCNu64"] Handle detached or PC closed, giving up...!\n", ice_handle ? ice_handle->handle_id : 0);
2536
                                return NULL;
2537
                        }
2538
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Waiting for candidates-done callback...\n", ice_handle->handle_id);
2539
                        g_usleep(100000);
2540
                        if(ice_handle->cdone < 0) {
2541
                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error gathering candidates!\n", ice_handle->handle_id);
2542
                                return NULL;
2543
                        }
2544
                }
2545
        }
2546
        /* Anonymize SDP */
2547
        char *sdp_stripped = janus_sdp_anonymize(sdp);
2548
        if(sdp_stripped == NULL) {
2549
                /* Invalid SDP */
2550
                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Invalid SDP\n", ice_handle->handle_id);
2551
                return NULL;
2552
        }
2553
        /* Add our details */
2554
        char *sdp_merged = janus_sdp_merge(ice_handle, sdp_stripped);
2555
        if(sdp_merged == NULL) {
2556
                /* Couldn't merge SDP */
2557
                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error merging SDP\n", ice_handle->handle_id);
2558
                g_free(sdp_stripped);
2559
                return NULL;
2560
        }
2561
        /* FIXME Any disabled m-line? */
2562
        if(strstr(sdp_merged, "m=audio 0")) {
2563
                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio disabled via SDP\n", ice_handle->handle_id);
2564
                if(!janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)
2565
                                || (!video && !data)) {
2566
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Marking audio stream as disabled\n", ice_handle->handle_id);
2567
                        janus_ice_stream *stream = g_hash_table_lookup(ice_handle->streams, GUINT_TO_POINTER(ice_handle->audio_id));
2568
                        if(stream)
2569
                                stream->disabled = TRUE;
2570
                }
2571
        }
2572
        if(strstr(sdp_merged, "m=video 0")) {
2573
                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Video disabled via SDP\n", ice_handle->handle_id);
2574
                if(!janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)
2575
                                || (!audio && !data)) {
2576
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Marking video stream as disabled\n", ice_handle->handle_id);
2577
                        janus_ice_stream *stream = NULL;
2578
                        if(!janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
2579
                                stream = g_hash_table_lookup(ice_handle->streams, GUINT_TO_POINTER(ice_handle->video_id));
2580
                        } else {
2581
                                gint id = ice_handle->audio_id > 0 ? ice_handle->audio_id : ice_handle->video_id;
2582
                                stream = g_hash_table_lookup(ice_handle->streams, GUINT_TO_POINTER(id));
2583
                        }
2584
                        if(stream)
2585
                                stream->disabled = TRUE;
2586
                }
2587
        }
2588
        if(strstr(sdp_merged, "m=application 0 DTLS/SCTP")) {
2589
                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Data Channel disabled via SDP\n", ice_handle->handle_id);
2590
                if(!janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)
2591
                                || (!audio && !video)) {
2592
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Marking data channel stream as disabled\n", ice_handle->handle_id);
2593
                        janus_ice_stream *stream = NULL;
2594
                        if(!janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
2595
                                stream = g_hash_table_lookup(ice_handle->streams, GUINT_TO_POINTER(ice_handle->data_id));
2596
                        } else {
2597
                                gint id = ice_handle->audio_id > 0 ? ice_handle->audio_id : (ice_handle->video_id > 0 ? ice_handle->video_id : ice_handle->data_id);
2598
                                stream = g_hash_table_lookup(ice_handle->streams, GUINT_TO_POINTER(id));
2599
                        }
2600
                        if(stream)
2601
                                stream->disabled = TRUE;
2602
                }
2603
        }
2604

    
2605
        if(!updating) {
2606
                if(offer) {
2607
                        /* We set the flag to wait for an answer before handling trickle candidates */
2608
                        janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
2609
                } else {
2610
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Done! Ready to setup remote candidates and send connectivity checks...\n", ice_handle->handle_id);
2611
                        if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE) && audio && video) {
2612
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- bundle is supported by the browser, getting rid of one of the RTP/RTCP components, if any...\n", ice_handle->handle_id);
2613
                                if(audio) {
2614
                                        /* Get rid of video and data, if present */
2615
                                        if(ice_handle->streams && ice_handle->video_stream) {
2616
                                                ice_handle->audio_stream->video_ssrc = ice_handle->video_stream->video_ssrc;
2617
                                                ice_handle->audio_stream->video_ssrc_peer = ice_handle->video_stream->video_ssrc_peer;
2618
                                                ice_handle->audio_stream->video_ssrc_peer_rtx = ice_handle->video_stream->video_ssrc_peer_rtx;
2619
                                                nice_agent_attach_recv(ice_handle->agent, ice_handle->video_stream->stream_id, 1, g_main_loop_get_context (ice_handle->iceloop), NULL, NULL);
2620
                                                if(!janus_ice_is_rtcpmux_forced())
2621
                                                        nice_agent_attach_recv(ice_handle->agent, ice_handle->video_stream->stream_id, 2, g_main_loop_get_context (ice_handle->iceloop), NULL, NULL);
2622
                                                nice_agent_remove_stream(ice_handle->agent, ice_handle->video_stream->stream_id);
2623
                                                janus_ice_stream_free(ice_handle->streams, ice_handle->video_stream);
2624
                                        }
2625
                                        ice_handle->video_stream = NULL;
2626
                                        ice_handle->video_id = 0;
2627
                                        if(ice_handle->streams && ice_handle->data_stream) {
2628
                                                nice_agent_attach_recv(ice_handle->agent, ice_handle->data_stream->stream_id, 1, g_main_loop_get_context (ice_handle->iceloop), NULL, NULL);
2629
                                                nice_agent_remove_stream(ice_handle->agent, ice_handle->data_stream->stream_id);
2630
                                                janus_ice_stream_free(ice_handle->streams, ice_handle->data_stream);
2631
                                        }
2632
                                        ice_handle->data_stream = NULL;
2633
                                        ice_handle->data_id = 0;
2634
                                } else if(video) {
2635
                                        /* Get rid of data, if present */
2636
                                        if(ice_handle->streams && ice_handle->data_stream) {
2637
                                                nice_agent_attach_recv(ice_handle->agent, ice_handle->data_stream->stream_id, 1, g_main_loop_get_context (ice_handle->iceloop), NULL, NULL);
2638
                                                nice_agent_remove_stream(ice_handle->agent, ice_handle->data_stream->stream_id);
2639
                                                janus_ice_stream_free(ice_handle->streams, ice_handle->data_stream);
2640
                                        }
2641
                                        ice_handle->data_stream = NULL;
2642
                                        ice_handle->data_id = 0;
2643
                                }
2644
                        }
2645
                        if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX) && !janus_ice_is_rtcpmux_forced()) {
2646
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- rtcp-mux is supported by the browser, getting rid of RTCP components, if any...\n", ice_handle->handle_id);
2647
                                if(ice_handle->audio_stream && ice_handle->audio_stream->rtcp_component && ice_handle->audio_stream->components != NULL) {
2648
                                        nice_agent_attach_recv(ice_handle->agent, ice_handle->audio_id, 2, g_main_loop_get_context (ice_handle->iceloop), NULL, NULL);
2649
                                        /* Free the component */
2650
                                        janus_ice_component_free(ice_handle->audio_stream->components, ice_handle->audio_stream->rtcp_component);
2651
                                        ice_handle->audio_stream->rtcp_component = NULL;
2652
                                        /* Create a dummy candidate and enforce it as the one to use for this now unneeded component */
2653
                                        NiceCandidate *c = nice_candidate_new(NICE_CANDIDATE_TYPE_HOST);
2654
                                        c->component_id = 2;
2655
                                        c->stream_id = ice_handle->audio_stream->stream_id;
2656
#ifndef HAVE_LIBNICE_TCP
2657
                                        c->transport = NICE_CANDIDATE_TRANSPORT_UDP;
2658
#endif
2659
                                        strncpy(c->foundation, "1", NICE_CANDIDATE_MAX_FOUNDATION);
2660
                                        c->priority = 1;
2661
                                        nice_address_set_from_string(&c->addr, "127.0.0.1");
2662
                                        nice_address_set_port(&c->addr, janus_ice_get_rtcpmux_blackhole_port());
2663
                                        c->username = g_strdup(ice_handle->audio_stream->ruser);
2664
                                        c->password = g_strdup(ice_handle->audio_stream->rpass);
2665
                                        if(!nice_agent_set_selected_remote_candidate(ice_handle->agent, ice_handle->audio_stream->stream_id, 2, c)) {
2666
                                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error forcing dummy candidate on RTCP component of stream %d\n", ice_handle->handle_id, ice_handle->audio_stream->stream_id);
2667
                                                nice_candidate_free(c);
2668
                                        }
2669
                                }
2670
                                if(ice_handle->video_stream && ice_handle->video_stream->rtcp_component && ice_handle->video_stream->components != NULL) {
2671
                                        nice_agent_attach_recv(ice_handle->agent, ice_handle->video_id, 2, g_main_loop_get_context (ice_handle->iceloop), NULL, NULL);
2672
                                        /* Free the component */
2673
                                        janus_ice_component_free(ice_handle->video_stream->components, ice_handle->video_stream->rtcp_component);
2674
                                        ice_handle->video_stream->rtcp_component = NULL;
2675
                                        /* Create a dummy candidate and enforce it as the one to use for this now unneeded component */
2676
                                        NiceCandidate *c = nice_candidate_new(NICE_CANDIDATE_TYPE_HOST);
2677
                                        c->component_id = 2;
2678
                                        c->stream_id = ice_handle->video_stream->stream_id;
2679
#ifndef HAVE_LIBNICE_TCP
2680
                                        c->transport = NICE_CANDIDATE_TRANSPORT_UDP;
2681
#endif
2682
                                        strncpy(c->foundation, "1", NICE_CANDIDATE_MAX_FOUNDATION);
2683
                                        c->priority = 1;
2684
                                        nice_address_set_from_string(&c->addr, "127.0.0.1");
2685
                                        nice_address_set_port(&c->addr, janus_ice_get_rtcpmux_blackhole_port());
2686
                                        c->username = g_strdup(ice_handle->video_stream->ruser);
2687
                                        c->password = g_strdup(ice_handle->video_stream->rpass);
2688
                                        if(!nice_agent_set_selected_remote_candidate(ice_handle->agent, ice_handle->video_stream->stream_id, 2, c)) {
2689
                                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error forcing dummy candidate on RTCP component of stream %d\n", ice_handle->handle_id, ice_handle->video_stream->stream_id);
2690
                                                nice_candidate_free(c);
2691
                                        }
2692
                                }
2693
                        }
2694
                        janus_mutex_lock(&ice_handle->mutex);
2695
                        /* We got our answer */
2696
                        janus_flags_clear(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
2697
                        /* Any pending trickles? */
2698
                        if(ice_handle->pending_trickles) {
2699
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Processing %d pending trickle candidates\n", ice_handle->handle_id, g_list_length(ice_handle->pending_trickles));
2700
                                GList *temp = NULL;
2701
                                while(ice_handle->pending_trickles) {
2702
                                        temp = g_list_first(ice_handle->pending_trickles);
2703
                                        ice_handle->pending_trickles = g_list_remove_link(ice_handle->pending_trickles, temp);
2704
                                        janus_ice_trickle *trickle = (janus_ice_trickle *)temp->data;
2705
                                        g_list_free(temp);
2706
                                        if(trickle == NULL)
2707
                                                continue;
2708
                                        if((janus_get_monotonic_time() - trickle->received) > 15*G_USEC_PER_SEC) {
2709
                                                /* FIXME Candidate is too old, discard it */
2710
                                                janus_ice_trickle_destroy(trickle);
2711
                                                /* FIXME We should report that */
2712
                                                continue;
2713
                                        }
2714
                                        json_t *candidate = trickle->candidate;
2715
                                        if(candidate == NULL) {
2716
                                                janus_ice_trickle_destroy(trickle);
2717
                                                continue;
2718
                                        }
2719
                                        if(json_is_object(candidate)) {
2720
                                                /* We got a single candidate */
2721
                                                int error = 0;
2722
                                                const char *error_string = NULL;
2723
                                                if((error = janus_ice_trickle_parse(ice_handle, candidate, &error_string)) != 0) {
2724
                                                        /* FIXME We should report the error parsing the trickle candidate */
2725
                                                }
2726
                                        } else if(json_is_array(candidate)) {
2727
                                                /* We got multiple candidates in an array */
2728
                                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Got multiple candidates (%zu)\n", ice_handle->handle_id, json_array_size(candidate));
2729
                                                if(json_array_size(candidate) > 0) {
2730
                                                        /* Handle remote candidates */
2731
                                                        size_t i = 0;
2732
                                                        for(i=0; i<json_array_size(candidate); i++) {
2733
                                                                json_t *c = json_array_get(candidate, i);
2734
                                                                /* FIXME We don't care if any trickle fails to parse */
2735
                                                                janus_ice_trickle_parse(ice_handle, c, NULL);
2736
                                                        }
2737
                                                }
2738
                                        }
2739
                                        /* Done, free candidate */
2740
                                        janus_ice_trickle_destroy(trickle);
2741
                                }
2742
                        }
2743
                        /* This was an answer, check if it's time to start ICE */
2744
                        if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE) &&
2745
                                        !janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALL_TRICKLES)) {
2746
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- ICE Trickling is supported by the browser, waiting for remote candidates...\n", ice_handle->handle_id);
2747
                                janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_START);
2748
                        } else {
2749
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Done! Sending connectivity checks...\n", ice_handle->handle_id);
2750
                                if(ice_handle->audio_id > 0) {
2751
                                        janus_ice_setup_remote_candidates(ice_handle, ice_handle->audio_id, 1);
2752
                                        if(!janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX))        /* http://tools.ietf.org/html/rfc5761#section-5.1.3 */
2753
                                                janus_ice_setup_remote_candidates(ice_handle, ice_handle->audio_id, 2);
2754
                                }
2755
                                if(ice_handle->video_id > 0) {
2756
                                        janus_ice_setup_remote_candidates(ice_handle, ice_handle->video_id, 1);
2757
                                        if(!janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX))        /* http://tools.ietf.org/html/rfc5761#section-5.1.3 */
2758
                                                janus_ice_setup_remote_candidates(ice_handle, ice_handle->video_id, 2);
2759
                                }
2760
                                if(ice_handle->data_id > 0) {
2761
                                        janus_ice_setup_remote_candidates(ice_handle, ice_handle->data_id, 1);
2762
                                }
2763
                        }
2764
                        janus_mutex_unlock(&ice_handle->mutex);
2765
                }
2766
        }
2767
        
2768
        /* Prepare JSON event */
2769
        json_t *jsep = json_object();
2770
        json_object_set_new(jsep, "type", json_string(sdp_type));
2771
        json_object_set_new(jsep, "sdp", json_string(sdp_merged));
2772
        g_free(sdp_stripped);
2773
        //~ g_free(sdp_merged);
2774
        ice_handle->local_sdp = sdp_merged;
2775
        return jsep;
2776
}
2777

    
2778
void janus_plugin_relay_rtp(janus_plugin_session *plugin_session, int video, char *buf, int len) {
2779
        if((plugin_session < (janus_plugin_session *)0x1000) || plugin_session->stopped || buf == NULL || len < 1)
2780
                return;
2781
        janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
2782
        if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2783
                        || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
2784
                return;
2785
        janus_ice_relay_rtp(handle, video, buf, len);
2786
}
2787

    
2788
void janus_plugin_relay_rtcp(janus_plugin_session *plugin_session, int video, char *buf, int len) {
2789
        if((plugin_session < (janus_plugin_session *)0x1000) || plugin_session->stopped || buf == NULL || len < 1)
2790
                return;
2791
        janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
2792
        if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2793
                        || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
2794
                return;
2795
        janus_ice_relay_rtcp(handle, video, buf, len);
2796
}
2797

    
2798
void janus_plugin_relay_data(janus_plugin_session *plugin_session, char *buf, int len) {
2799
        if((plugin_session < (janus_plugin_session *)0x1000) || plugin_session->stopped || buf == NULL || len < 1)
2800
                return;
2801
        janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
2802
        if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2803
                        || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
2804
                return;
2805
#ifdef HAVE_SCTP
2806
        janus_ice_relay_data(handle, buf, len);
2807
#else
2808
        JANUS_LOG(LOG_WARN, "Asked to relay data, but Data Channels support has not been compiled...\n");
2809
#endif
2810
}
2811

    
2812
void janus_plugin_close_pc(janus_plugin_session *plugin_session) {
2813
        /* A plugin asked to get rid of a PeerConnection */
2814
        if((plugin_session < (janus_plugin_session *)0x1000) || !janus_plugin_session_is_alive(plugin_session) || plugin_session->stopped)
2815
                return;
2816
        janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
2817
        if(!ice_handle)
2818
                return;
2819
        if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2820
                        || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
2821
                return;
2822
        janus_session *session = (janus_session *)ice_handle->session;
2823
        if(!session)
2824
                return;
2825
                
2826
        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Plugin asked to hangup PeerConnection: sending alert\n", ice_handle->handle_id);
2827
        /* Send an alert on all the DTLS connections */
2828
        janus_ice_webrtc_hangup(ice_handle);
2829
        /* Get rid of the PeerConnection */
2830
        if(ice_handle->iceloop) {
2831
                gint64 waited = 0;
2832
                while(ice_handle->iceloop && !g_main_loop_is_running(ice_handle->iceloop)) {
2833
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE loop exists but is not running, waiting for it to run\n", ice_handle->handle_id);
2834
                        g_usleep (100000);
2835
                        waited += 100000;
2836
                        if(waited >= G_USEC_PER_SEC) {
2837
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Waited a second, that's enough!\n", ice_handle->handle_id);
2838
                                break;
2839
                        }
2840
                }
2841
                if(ice_handle->iceloop && g_main_loop_is_running(ice_handle->iceloop)) {
2842
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Forcing ICE loop to quit (%s)\n", ice_handle->handle_id, g_main_loop_is_running(ice_handle->iceloop) ? "running" : "NOT running");
2843
                        g_main_loop_quit(ice_handle->iceloop);
2844
                        g_main_context_wakeup(ice_handle->icectx);
2845
                }
2846
        }
2847
}
2848

    
2849
void janus_plugin_end_session(janus_plugin_session *plugin_session) {
2850
        /* A plugin asked to get rid of a handle */
2851
        if((plugin_session < (janus_plugin_session *)0x1000) || !janus_plugin_session_is_alive(plugin_session) || plugin_session->stopped)
2852
                return;
2853
        janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
2854
        if(!ice_handle)
2855
                return;
2856
        janus_session *session = (janus_session *)ice_handle->session;
2857
        if(!session)
2858
                return;
2859
        /* Destroy the handle */
2860
        janus_ice_handle_destroy(session, ice_handle->handle_id);
2861
        janus_mutex_lock(&session->mutex);
2862
        g_hash_table_remove(session->ice_handles, GUINT_TO_POINTER(ice_handle->handle_id));
2863
        janus_mutex_unlock(&session->mutex);
2864
}
2865

    
2866

    
2867
static void janus_detect_local_ip(gchar *buf, size_t buflen) {
2868
        JANUS_LOG(LOG_VERB, "Autodetecting local IP...\n");
2869
        struct sockaddr_in addr;
2870
        socklen_t len;
2871
        int fd = socket(AF_INET, SOCK_DGRAM, 0);
2872
        if (fd == -1)
2873
                goto error;
2874
        addr.sin_family = AF_INET;
2875
        addr.sin_port = htons(1);
2876
        inet_pton(AF_INET, "1.2.3.4", &addr.sin_addr.s_addr);
2877
        if (connect(fd, (const struct sockaddr*) &addr, sizeof(addr)) < 0)
2878
                goto error;
2879
        len = sizeof(addr);
2880
        if (getsockname(fd, (struct sockaddr*) &addr, &len) < 0)
2881
                goto error;
2882
        if (getnameinfo((const struct sockaddr*) &addr, sizeof(addr),
2883
                        buf, buflen,
2884
                        NULL, 0, NI_NUMERICHOST) != 0)
2885
                goto error;
2886
        close(fd);
2887
        return;
2888
error:
2889
        if (fd != -1)
2890
                close(fd);
2891
        JANUS_LOG(LOG_VERB, "Couldn't find any address! using 127.0.0.1 as the local IP... (which is NOT going to work out of your machine)\n");
2892
        g_strlcpy(buf, "127.0.0.1", buflen);
2893
}
2894

    
2895

    
2896
/* Main */
2897
gint main(int argc, char *argv[])
2898
{
2899
        /* Core dumps may be disallowed by parent of this process; change that */
2900
        struct rlimit core_limits;
2901
        core_limits.rlim_cur = core_limits.rlim_max = RLIM_INFINITY;
2902
        setrlimit(RLIMIT_CORE, &core_limits);
2903

    
2904
        struct gengetopt_args_info args_info;
2905
        /* Let's call our cmdline parser */
2906
        if(cmdline_parser(argc, argv, &args_info) != 0)
2907
                exit(1);
2908
        
2909
        /* Any configuration to open? */
2910
        if(args_info.config_given) {
2911
                config_file = g_strdup(args_info.config_arg);
2912
        }
2913
        if(args_info.configs_folder_given) {
2914
                configs_folder = g_strdup(args_info.configs_folder_arg);
2915
        } else {
2916
                configs_folder = g_strdup (CONFDIR);
2917
        }
2918
        if(config_file == NULL) {
2919
                char file[255];
2920
                g_snprintf(file, 255, "%s/janus.cfg", configs_folder);
2921
                config_file = g_strdup(file);
2922
        }
2923
        if((config = janus_config_parse(config_file)) == NULL) {
2924
                if(args_info.config_given) {
2925
                        /* We only give up if the configuration file was explicitly provided */
2926
                        g_print("Error reading configuration from %s\n", config_file);
2927
                        exit(1);
2928
                }
2929
                g_print("Error reading/parsing the configuration file, going on with the defaults and the command line arguments\n");
2930
                config = janus_config_create("janus.cfg");
2931
                if(config == NULL) {
2932
                        /* If we can't even create an empty configuration, something's definitely wrong */
2933
                        exit(1);
2934
                }
2935
        }
2936

    
2937
        /* Check if we need to log to console and/or file */
2938
        gboolean use_stdout = TRUE;
2939
        if(args_info.disable_stdout_given) {
2940
                use_stdout = FALSE;
2941
        } else {
2942
                /* Check if the configuration file is saying anything about this */
2943
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "log_to_stdout");
2944
                if(item && item->value && !janus_is_true(item->value))
2945
                        use_stdout = FALSE;
2946
        }
2947
        const char *logfile = NULL;
2948
        if(args_info.log_file_given) {
2949
                logfile = args_info.log_file_arg;
2950
        } else {
2951
                /* Check if the configuration file is saying anything about this */
2952
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "log_to_file");
2953
                if(item && item->value)
2954
                        logfile = item->value;
2955
        }
2956

    
2957
        /* Check if we're going to daemonize Janus */
2958
        gboolean daemonize = FALSE;
2959
        if(args_info.daemon_given) {
2960
                daemonize = TRUE;
2961
        } else {
2962
                /* Check if the configuration file is saying anything about this */
2963
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "daemonize");
2964
                if(item && item->value && janus_is_true(item->value))
2965
                        daemonize = TRUE;
2966
        }
2967
        /* If we're going to daemonize, make sure logging to stdout is disabled and a log file has been specified */
2968
        if(daemonize && use_stdout) {
2969
                use_stdout = FALSE;
2970
        }
2971
        if(daemonize && logfile == NULL) {
2972
                g_print("Running Janus as a daemon but no log file provided, giving up...\n");
2973
                exit(1);
2974
        }
2975
        /* Daemonize now, if we need to */
2976
        if(daemonize) {
2977
                g_print("Running Janus as a daemon\n");
2978

    
2979
                /* Fork off the parent process */
2980
                pid_t pid = fork();
2981
                if(pid < 0) {
2982
                        g_print("Fork error!\n");
2983
                        exit(1);
2984
                }
2985
                if(pid > 0) {
2986
                        exit(0);
2987
                }
2988
                /* Change the file mode mask */
2989
                umask(0);
2990

    
2991
                /* Create a new SID for the child process */
2992
                pid_t sid = setsid();
2993
                if(sid < 0) {
2994
                        g_print("Error setting SID!\n");
2995
                        exit(1);
2996
                }
2997
                /* Change the current working directory */
2998
                if((chdir("/")) < 0) {
2999
                        g_print("Error changing the current working directory!\n");
3000
                        exit(1);
3001
                }
3002

    
3003
                /* Close out the standard file descriptors */
3004
                close(STDIN_FILENO);
3005
                close(STDOUT_FILENO);
3006
                close(STDERR_FILENO);
3007
        }
3008

    
3009
        /* Initialize logger */
3010
        if(janus_log_init(use_stdout, logfile) < 0)
3011
                exit(1);
3012

    
3013
        JANUS_PRINT("---------------------------------------------------\n");
3014
        JANUS_PRINT("  Starting Meetecho Janus (WebRTC Gateway) v%s\n", JANUS_VERSION_STRING);
3015
        JANUS_PRINT("---------------------------------------------------\n\n");
3016

    
3017
        /* Handle SIGINT (CTRL-C), SIGTERM (from service managers) */
3018
        signal(SIGINT, janus_handle_signal);
3019
        signal(SIGTERM, janus_handle_signal);
3020

    
3021
        /* Setup Glib */
3022
#if !GLIB_CHECK_VERSION(2, 36, 0)
3023
        g_type_init();
3024
#endif
3025

    
3026
        /* Logging level: default is info and no timestamps */
3027
        janus_log_level = LOG_INFO;
3028
        janus_log_timestamps = FALSE;
3029
        janus_log_colors = TRUE;
3030
        if(args_info.debug_level_given) {
3031
                if(args_info.debug_level_arg < LOG_NONE)
3032
                        args_info.debug_level_arg = 0;
3033
                else if(args_info.debug_level_arg > LOG_MAX)
3034
                        args_info.debug_level_arg = LOG_MAX;
3035
                janus_log_level = args_info.debug_level_arg;
3036
        }
3037

    
3038
        /* Proceed with the rest of the configuration */
3039
        janus_config_print(config);
3040
        if(args_info.debug_level_given) {
3041
                char debug[5];
3042
                g_snprintf(debug, 5, "%d", args_info.debug_level_arg);
3043
                janus_config_add_item(config, "general", "debug_level", debug);
3044
        } else {
3045
                /* No command line directive on logging, try the configuration file */
3046
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "debug_level");
3047
                if(item && item->value) {
3048
                        int temp_level = atoi(item->value);
3049
                        if(temp_level == 0 && strcmp(item->value, "0")) {
3050
                                JANUS_PRINT("Invalid debug level %s (configuration), using default (info=4)\n", item->value);
3051
                        } else {
3052
                                janus_log_level = temp_level;
3053
                                if(janus_log_level < LOG_NONE)
3054
                                        janus_log_level = 0;
3055
                                else if(janus_log_level > LOG_MAX)
3056
                                        janus_log_level = LOG_MAX;
3057
                        }
3058
                }
3059
        }
3060
        /* Any command line argument that should overwrite the configuration? */
3061
        JANUS_PRINT("Checking command line arguments...\n");
3062
        if(args_info.debug_timestamps_given) {
3063
                janus_config_add_item(config, "general", "debug_timestamps", "yes");
3064
        }
3065
        if(args_info.disable_colors_given) {
3066
                janus_config_add_item(config, "general", "debug_colors", "no");
3067
        }
3068
         if(args_info.interface_given) {
3069
                janus_config_add_item(config, "general", "interface", args_info.interface_arg);
3070
        }
3071
        if(args_info.configs_folder_given) {
3072
                janus_config_add_item(config, "general", "configs_folder", args_info.configs_folder_arg);
3073
        }
3074
        if(args_info.plugins_folder_given) {
3075
                janus_config_add_item(config, "general", "plugins_folder", args_info.plugins_folder_arg);
3076
        }
3077
        if(args_info.apisecret_given) {
3078
                janus_config_add_item(config, "general", "api_secret", args_info.apisecret_arg);
3079
        }
3080
        if(args_info.token_auth_given) {
3081
                janus_config_add_item(config, "general", "token_auth", "yes");
3082
        }
3083
        if(args_info.cert_pem_given) {
3084
                janus_config_add_item(config, "certificates", "cert_pem", args_info.cert_pem_arg);
3085
        }
3086
        if(args_info.cert_key_given) {
3087
                janus_config_add_item(config, "certificates", "cert_key", args_info.cert_key_arg);
3088
        }
3089
        if(args_info.stun_server_given) {
3090
                /* Split in server and port (if port missing, use 3478 as default) */
3091
                char *stunport = strrchr(args_info.stun_server_arg, ':');
3092
                if(stunport != NULL) {
3093
                        *stunport = '\0';
3094
                        stunport++;
3095
                        janus_config_add_item(config, "nat", "stun_server", args_info.stun_server_arg);
3096
                        janus_config_add_item(config, "nat", "stun_port", stunport);
3097
                } else {
3098
                        janus_config_add_item(config, "nat", "stun_server", args_info.stun_server_arg);
3099
                        janus_config_add_item(config, "nat", "stun_port", "3478");
3100
                }
3101
        }
3102
        if(args_info.nat_1_1_given) {
3103
                janus_config_add_item(config, "nat", "nat_1_1_mapping", args_info.nat_1_1_arg);
3104
        }
3105
        if(args_info.ice_enforce_list_given) {
3106
                janus_config_add_item(config, "nat", "ice_enforce_list", args_info.ice_enforce_list_arg);
3107
        }
3108
        if(args_info.ice_ignore_list_given) {
3109
                janus_config_add_item(config, "nat", "ice_ignore_list", args_info.ice_ignore_list_arg);
3110
        }
3111
        if(args_info.libnice_debug_given) {
3112
                janus_config_add_item(config, "nat", "nice_debug", "true");
3113
        }
3114
        if(args_info.ice_lite_given) {
3115
                janus_config_add_item(config, "nat", "ice_lite", "true");
3116
        }
3117
        if(args_info.ice_tcp_given) {
3118
                janus_config_add_item(config, "nat", "ice_tcp", "true");
3119
        }
3120
        if(args_info.ipv6_candidates_given) {
3121
                janus_config_add_item(config, "media", "ipv6", "true");
3122
        }
3123
        if(args_info.force_bundle_given) {
3124
                janus_config_add_item(config, "media", "force-bundle", "true");
3125
        }
3126
        if(args_info.force_rtcp_mux_given) {
3127
                janus_config_add_item(config, "media", "force-rtcp-mux", "true");
3128
        }
3129
        if(args_info.max_nack_queue_given) {
3130
                char mnq[20];
3131
                g_snprintf(mnq, 20, "%d", args_info.max_nack_queue_arg);
3132
                janus_config_add_item(config, "media", "max_nack_queue", mnq);
3133
        }
3134
        if(args_info.rtp_port_range_given) {
3135
                janus_config_add_item(config, "media", "rtp_port_range", args_info.rtp_port_range_arg);
3136
        }
3137
        janus_config_print(config);
3138

    
3139
        /* Logging/debugging */
3140
        JANUS_PRINT("Debug/log level is %d\n", janus_log_level);
3141
        janus_config_item *item = janus_config_get_item_drilldown(config, "general", "debug_timestamps");
3142
        if(item && item->value)
3143
                janus_log_timestamps = janus_is_true(item->value);
3144
        JANUS_PRINT("Debug/log timestamps are %s\n", janus_log_timestamps ? "enabled" : "disabled");
3145
        item = janus_config_get_item_drilldown(config, "general", "debug_colors");
3146
        if(item && item->value)
3147
                janus_log_colors = janus_is_true(item->value);
3148
        JANUS_PRINT("Debug/log colors are %s\n", janus_log_colors ? "enabled" : "disabled");
3149

    
3150
        /* Any IP/interface to enforce/ignore? */
3151
        item = janus_config_get_item_drilldown(config, "nat", "ice_enforce_list");
3152
        if(item && item->value) {
3153
                gchar **list = g_strsplit(item->value, ",", -1);
3154
                gchar *index = list[0];
3155
                if(index != NULL) {
3156
                        int i=0;
3157
                        while(index != NULL) {
3158
                                if(strlen(index) > 0) {
3159
                                        JANUS_LOG(LOG_INFO, "Adding '%s' to the ICE enforce list...\n", index);
3160
                                        janus_ice_enforce_interface(g_strdup(index));
3161
                                }
3162
                                i++;
3163
                                index = list[i];
3164
                        }
3165
                }
3166
                g_strfreev(list);
3167
                list = NULL;
3168
        }
3169
        item = janus_config_get_item_drilldown(config, "nat", "ice_ignore_list");
3170
        if(item && item->value) {
3171
                gchar **list = g_strsplit(item->value, ",", -1);
3172
                gchar *index = list[0];
3173
                if(index != NULL) {
3174
                        int i=0;
3175
                        while(index != NULL) {
3176
                                if(strlen(index) > 0) {
3177
                                        JANUS_LOG(LOG_INFO, "Adding '%s' to the ICE ignore list...\n", index);
3178
                                        janus_ice_ignore_interface(g_strdup(index));
3179
                                }
3180
                                i++;
3181
                                index = list[i];
3182
                        }
3183
                }
3184
                g_strfreev(list);
3185
                list = NULL;
3186
        }
3187
        /* What is the local IP? */
3188
        JANUS_LOG(LOG_VERB, "Selecting local IP address...\n");
3189
        gboolean local_ip_set = FALSE;
3190
        item = janus_config_get_item_drilldown(config, "general", "interface");
3191
        if(item && item->value) {
3192
                JANUS_LOG(LOG_VERB, "  -- Will try to use %s\n", item->value);
3193
                int family;
3194
                if (!janus_is_ip_valid(item->value, &family)) {
3195
                        JANUS_LOG(LOG_WARN, "Invalid local IP specified: %s, guessing the default...\n", item->value);
3196
                } else {
3197
                        /* Verify that we can actually bind to that address */
3198
                        int fd = socket(family, SOCK_DGRAM, 0);
3199
                        if (fd == -1) {
3200
                                JANUS_LOG(LOG_WARN, "Error creating test socket, falling back to detecting IP address...\n");
3201
                        } else {
3202
                                int r;
3203
                                struct sockaddr_storage ss;
3204
                                socklen_t addrlen;
3205
                                memset(&ss, 0, sizeof(ss));
3206
                                if (family == AF_INET) {
3207
                                        struct sockaddr_in *addr4 = (struct sockaddr_in*)&ss;
3208
                                        addr4->sin_family = AF_INET;
3209
                                        addr4->sin_port = 0;
3210
                                        inet_pton(AF_INET, item->value, &(addr4->sin_addr.s_addr));
3211
                                        addrlen = sizeof(struct sockaddr_in);
3212
                                } else {
3213
                                        struct sockaddr_in6 *addr6 = (struct sockaddr_in6*)&ss;
3214
                                        addr6->sin6_family = AF_INET6;
3215
                                        addr6->sin6_port = 0;
3216
                                        inet_pton(AF_INET6, item->value, &(addr6->sin6_addr.s6_addr));
3217
                                        addrlen = sizeof(struct sockaddr_in6);
3218
                                }
3219
                                r = bind(fd, (const struct sockaddr*)&ss, addrlen);
3220
                                close(fd);
3221
                                if (r < 0) {
3222
                                        JANUS_LOG(LOG_WARN, "Error setting local IP address to %s, falling back to detecting IP address...\n", item->value);
3223
                                } else {
3224
                                        g_strlcpy(local_ip, item->value, sizeof(local_ip));
3225
                                        local_ip_set = TRUE;
3226
                                }
3227
                        }
3228
                }
3229
        }
3230
        if (!local_ip_set)
3231
                janus_detect_local_ip(local_ip, sizeof(local_ip));
3232
        JANUS_LOG(LOG_INFO, "Using %s as local IP...\n", local_ip);
3233

    
3234
        /* Is there any API secret to consider? */
3235
        api_secret = NULL;
3236
        item = janus_config_get_item_drilldown(config, "general", "api_secret");
3237
        if(item && item->value) {
3238
                api_secret = g_strdup(item->value);
3239
        }
3240
        /* Is there any API secret to consider? */
3241
        admin_api_secret = NULL;
3242
        item = janus_config_get_item_drilldown(config, "general", "admin_secret");
3243
        if(item && item->value) {
3244
                admin_api_secret = g_strdup(item->value);
3245
        }
3246
        /* Also check if the token based authentication mechanism needs to be enabled */
3247
        item = janus_config_get_item_drilldown(config, "general", "token_auth");
3248
        janus_auth_init(item && item->value && janus_is_true(item->value));
3249

    
3250
        /* Setup ICE stuff (e.g., checking if the provided STUN server is correct) */
3251
        char *stun_server = NULL, *turn_server = NULL;
3252
        uint16_t stun_port = 0, turn_port = 0;
3253
        char *turn_type = NULL, *turn_user = NULL, *turn_pwd = NULL;
3254
        char *turn_rest_api = NULL, *turn_rest_api_key = NULL;
3255
        uint16_t rtp_min_port = 0, rtp_max_port = 0;
3256
        gboolean ice_lite = FALSE, ice_tcp = FALSE, ipv6 = FALSE;
3257
        item = janus_config_get_item_drilldown(config, "media", "ipv6");
3258
        ipv6 = (item && item->value) ? janus_is_true(item->value) : FALSE;
3259
        item = janus_config_get_item_drilldown(config, "media", "rtp_port_range");
3260
        if(item && item->value) {
3261
                /* Split in min and max port */
3262
                char *maxport = strrchr(item->value, '-');
3263
                if(maxport != NULL) {
3264
                        *maxport = '\0';
3265
                        maxport++;
3266
                        rtp_min_port = atoi(item->value);
3267
                        rtp_max_port = atoi(maxport);
3268
                        maxport--;
3269
                        *maxport = '-';
3270
                }
3271
                if(rtp_min_port > rtp_max_port) {
3272
                        int temp_port = rtp_min_port;
3273
                        rtp_min_port = rtp_max_port;
3274
                        rtp_max_port = temp_port;
3275
                }
3276
                if(rtp_max_port == 0)
3277
                        rtp_max_port = 65535;
3278
                JANUS_LOG(LOG_INFO, "RTP port range: %u -- %u\n", rtp_min_port, rtp_max_port);
3279
        }
3280
        /* Check if we need to enable the ICE Lite mode */
3281
        item = janus_config_get_item_drilldown(config, "nat", "ice_lite");
3282
        ice_lite = (item && item->value) ? janus_is_true(item->value) : FALSE;
3283
        /* Check if we need to enable ICE-TCP support (warning: still broken, for debugging only) */
3284
        item = janus_config_get_item_drilldown(config, "nat", "ice_tcp");
3285
        ice_tcp = (item && item->value) ? janus_is_true(item->value) : FALSE;
3286
        /* Any STUN server to use in Janus? */
3287
        item = janus_config_get_item_drilldown(config, "nat", "stun_server");
3288
        if(item && item->value)
3289
                stun_server = (char *)item->value;
3290
        item = janus_config_get_item_drilldown(config, "nat", "stun_port");
3291
        if(item && item->value)
3292
                stun_port = atoi(item->value);
3293
        /* Any 1:1 NAT mapping to take into account? */
3294
        item = janus_config_get_item_drilldown(config, "nat", "nat_1_1_mapping");
3295
        if(item && item->value) {
3296
                JANUS_LOG(LOG_VERB, "Using nat_1_1_mapping for public ip - %s\n", item->value);
3297
                janus_set_public_ip(item->value);
3298
                janus_ice_enable_nat_1_1();
3299
        }
3300
        /* Any TURN server to use in Janus? */
3301
        item = janus_config_get_item_drilldown(config, "nat", "turn_server");
3302
        if(item && item->value)
3303
                turn_server = (char *)item->value;
3304
        item = janus_config_get_item_drilldown(config, "nat", "turn_port");
3305
        if(item && item->value)
3306
                turn_port = atoi(item->value);
3307
        item = janus_config_get_item_drilldown(config, "nat", "turn_type");
3308
        if(item && item->value)
3309
                turn_type = (char *)item->value;
3310
        item = janus_config_get_item_drilldown(config, "nat", "turn_user");
3311
        if(item && item->value)
3312
                turn_user = (char *)item->value;
3313
        item = janus_config_get_item_drilldown(config, "nat", "turn_pwd");
3314
        if(item && item->value)
3315
                turn_pwd = (char *)item->value;
3316
        /* Check if there's any TURN REST API backend to use */
3317
        item = janus_config_get_item_drilldown(config, "nat", "turn_rest_api");
3318
        if(item && item->value)
3319
                turn_rest_api = (char *)item->value;
3320
        item = janus_config_get_item_drilldown(config, "nat", "turn_rest_api_key");
3321
        if(item && item->value)
3322
                turn_rest_api_key = (char *)item->value;
3323
        /* Initialize the ICE stack now */
3324
        janus_ice_init(ice_lite, ice_tcp, ipv6, rtp_min_port, rtp_max_port);
3325
        if(janus_ice_set_stun_server(stun_server, stun_port) < 0) {
3326
                JANUS_LOG(LOG_FATAL, "Invalid STUN address %s:%u\n", stun_server, stun_port);
3327
                exit(1);
3328
        }
3329
        if(janus_ice_set_turn_server(turn_server, turn_port, turn_type, turn_user, turn_pwd) < 0) {
3330
                JANUS_LOG(LOG_FATAL, "Invalid TURN address %s:%u\n", turn_server, turn_port);
3331
                exit(1);
3332
        }
3333
#ifndef HAVE_LIBCURL
3334
        if(turn_rest_api != NULL || turn_rest_api_key != NULL) {
3335
                JANUS_LOG(LOG_WARN, "A TURN REST API backend specified in the settings, but libcurl support has not been built\n");
3336
        }
3337
#else
3338
        if(janus_ice_set_turn_rest_api(turn_rest_api, turn_rest_api_key) < 0) {
3339
                JANUS_LOG(LOG_FATAL, "Invalid TURN REST API configuration: %s (%s)\n", turn_rest_api, turn_rest_api_key);
3340
                exit(1);
3341
        }
3342
#endif
3343
        item = janus_config_get_item_drilldown(config, "nat", "nice_debug");
3344
        if(item && item->value && janus_is_true(item->value)) {
3345
                /* Enable libnice debugging */
3346
                janus_ice_debugging_enable();
3347
        }
3348
        if(stun_server == NULL && turn_server == NULL) {
3349
                /* No STUN and TURN server provided for Janus: make sure it isn't on a private address */
3350
                gboolean private_address = FALSE;
3351
                struct sockaddr_in addr;
3352
                if(inet_pton(AF_INET, local_ip, &addr) > 0) {
3353
                        unsigned short int ip[4];
3354
                        sscanf(local_ip, "%hu.%hu.%hu.%hu", &ip[0], &ip[1], &ip[2], &ip[3]);
3355
                        if(ip[0] == 10) {
3356
                                /* Class A private address */
3357
                                private_address = TRUE;
3358
                        } else if(ip[0] == 172 && (ip[1] >= 16 && ip[1] <= 31)) {
3359
                                /* Class B private address */
3360
                                private_address = TRUE;
3361
                        } else if(ip[0] == 192 && ip[1] == 168) {
3362
                                /* Class C private address */
3363
                                private_address = TRUE;
3364
                        }
3365
                }
3366
                if(private_address) {
3367
                        JANUS_LOG(LOG_WARN, "Janus is deployed on a private address (%s) but you didn't specify any STUN server! Expect trouble if this is supposed to work over the internet and not just in a LAN...\n", local_ip);
3368
                }
3369
        }
3370
        /* Are we going to force BUNDLE and/or rtcp-mux? */
3371
        gboolean force_bundle = FALSE, force_rtcpmux = FALSE;
3372
        item = janus_config_get_item_drilldown(config, "media", "force-bundle");
3373
        force_bundle = (item && item->value) ? janus_is_true(item->value) : FALSE;
3374
        janus_ice_force_bundle(force_bundle);
3375
        item = janus_config_get_item_drilldown(config, "media", "force-rtcp-mux");
3376
        force_rtcpmux = (item && item->value) ? janus_is_true(item->value) : FALSE;
3377
        janus_ice_force_rtcpmux(force_rtcpmux);
3378
        /* NACK related stuff */
3379
        item = janus_config_get_item_drilldown(config, "media", "max_nack_queue");
3380
        if(item && item->value) {
3381
                int mnq = atoi(item->value);
3382
                if(mnq < 0) {
3383
                        JANUS_LOG(LOG_WARN, "Ignoring max_nack_queue value as it's not a positive integer\n");
3384
                } else {
3385
                        janus_set_max_nack_queue(mnq);
3386
                }
3387
        }
3388

    
3389
        /* Setup OpenSSL stuff */
3390
        item = janus_config_get_item_drilldown(config, "certificates", "cert_pem");
3391
        if(!item || !item->value) {
3392
                JANUS_LOG(LOG_FATAL, "Missing certificate/key path, use the command line or the configuration to provide one\n");
3393
                exit(1);
3394
        }
3395
        server_pem = (char *)item->value;
3396
        item = janus_config_get_item_drilldown(config, "certificates", "cert_key");
3397
        if(!item || !item->value) {
3398
                JANUS_LOG(LOG_FATAL, "Missing certificate/key path, use the command line or the configuration to provide one\n");
3399
                exit(1);
3400
        }
3401
        server_key = (char *)item->value;
3402
        JANUS_LOG(LOG_VERB, "Using certificates:\n\t%s\n\t%s\n", server_pem, server_key);
3403
        SSL_library_init();
3404
        SSL_load_error_strings();
3405
        OpenSSL_add_all_algorithms();
3406
        /* ... and DTLS-SRTP in particular */
3407
        if(janus_dtls_srtp_init(server_pem, server_key) < 0) {
3408
                exit(1);
3409
        }
3410
        /* Check if there's any custom value for the starting MTU to use in the BIO filter */
3411
        item = janus_config_get_item_drilldown(config, "media", "dtls_mtu");
3412
        if(item && item->value)
3413
                janus_dtls_bio_filter_set_mtu(atoi(item->value));
3414

    
3415
#ifdef HAVE_SCTP
3416
        /* Initialize SCTP for DataChannels */
3417
        if(janus_sctp_init() < 0) {
3418
                exit(1);
3419
        }
3420
#else
3421
        JANUS_LOG(LOG_WARN, "Data Channels support not compiled\n");
3422
#endif
3423

    
3424
        /* Initialize Sofia-SDP */
3425
        if(janus_sdp_init() < 0) {
3426
                exit(1);
3427
        }
3428

    
3429
        /* Load plugins */
3430
        const char *path = PLUGINDIR;
3431
        item = janus_config_get_item_drilldown(config, "general", "plugins_folder");
3432
        if(item && item->value)
3433
                path = (char *)item->value;
3434
        JANUS_LOG(LOG_INFO, "Plugins folder: %s\n", path);
3435
        DIR *dir = opendir(path);
3436
        if(!dir) {
3437
                JANUS_LOG(LOG_FATAL, "\tCouldn't access plugins folder...\n");
3438
                exit(1);
3439
        }
3440
        /* Any plugin to ignore? */
3441
        gchar **disabled_plugins = NULL;
3442
        item = janus_config_get_item_drilldown(config, "plugins", "disable");
3443
        if(item && item->value)
3444
                disabled_plugins = g_strsplit(item->value, ",", -1);
3445
        /* Open the shared objects */
3446
        struct dirent *pluginent = NULL;
3447
        char pluginpath[1024];
3448
        while((pluginent = readdir(dir))) {
3449
                int len = strlen(pluginent->d_name);
3450
                if (len < 4) {
3451
                        continue;
3452
                }
3453
                if (strcasecmp(pluginent->d_name+len-strlen(SHLIB_EXT), SHLIB_EXT)) {
3454
                        continue;
3455
                }
3456
                /* Check if this plugins has been disabled in the configuration file */
3457
                if(disabled_plugins != NULL) {
3458
                        gchar *index = disabled_plugins[0];
3459
                        if(index != NULL) {
3460
                                int i=0;
3461
                                gboolean skip = FALSE;
3462
                                while(index != NULL) {
3463
                                        while(isspace(*index))
3464
                                                index++;
3465
                                        if(strlen(index) && !strcmp(index, pluginent->d_name)) {
3466
                                                JANUS_LOG(LOG_WARN, "Plugin '%s' has been disabled, skipping...\n", pluginent->d_name);
3467
                                                skip = TRUE;
3468
                                                break;
3469
                                        }
3470
                                        i++;
3471
                                        index = disabled_plugins[i];
3472
                                }
3473
                                if(skip)
3474
                                        continue;
3475
                        }
3476
                }
3477
                JANUS_LOG(LOG_INFO, "Loading plugin '%s'...\n", pluginent->d_name);
3478
                memset(pluginpath, 0, 1024);
3479
                g_snprintf(pluginpath, 1024, "%s/%s", path, pluginent->d_name);
3480
                void *plugin = dlopen(pluginpath, RTLD_LAZY);
3481
                if (!plugin) {
3482
                        JANUS_LOG(LOG_ERR, "\tCouldn't load plugin '%s': %s\n", pluginent->d_name, dlerror());
3483
                } else {
3484
                        create_p *create = (create_p*) dlsym(plugin, "create");
3485
                        const char *dlsym_error = dlerror();
3486
                        if (dlsym_error) {
3487
                                JANUS_LOG(LOG_ERR, "\tCouldn't load symbol 'create': %s\n", dlsym_error);
3488
                                continue;
3489
                        }
3490
                        janus_plugin *janus_plugin = create();
3491
                        if(!janus_plugin) {
3492
                                JANUS_LOG(LOG_ERR, "\tCouldn't use function 'create'...\n");
3493
                                continue;
3494
                        }
3495
                        /* Are all the mandatory methods and callbacks implemented? */
3496
                        if(!janus_plugin->init || !janus_plugin->destroy ||
3497
                                        !janus_plugin->get_api_compatibility ||
3498
                                        !janus_plugin->get_version ||
3499
                                        !janus_plugin->get_version_string ||
3500
                                        !janus_plugin->get_description ||
3501
                                        !janus_plugin->get_package ||
3502
                                        !janus_plugin->get_name ||
3503
                                        !janus_plugin->create_session ||
3504
                                        !janus_plugin->query_session ||
3505
                                        !janus_plugin->destroy_session ||
3506
                                        !janus_plugin->handle_message ||
3507
                                        !janus_plugin->setup_media ||
3508
                                        !janus_plugin->hangup_media) {
3509
                                JANUS_LOG(LOG_ERR, "\tMissing some mandatory methods/callbacks, skipping this plugin...\n");
3510
                                continue;
3511
                        }
3512
                        if(janus_plugin->get_api_compatibility() < JANUS_PLUGIN_API_VERSION) {
3513
                                JANUS_LOG(LOG_ERR, "The '%s' plugin was compiled against an older version of the API (%d < %d), skipping it: update it to enable it again\n",
3514
                                        janus_plugin->get_package(), janus_plugin->get_api_compatibility(), JANUS_PLUGIN_API_VERSION);
3515
                                continue;
3516
                        }
3517
                        janus_plugin->init(&janus_handler_plugin, configs_folder);
3518
                        JANUS_LOG(LOG_VERB, "\tVersion: %d (%s)\n", janus_plugin->get_version(), janus_plugin->get_version_string());
3519
                        JANUS_LOG(LOG_VERB, "\t   [%s] %s\n", janus_plugin->get_package(), janus_plugin->get_name());
3520
                        JANUS_LOG(LOG_VERB, "\t   %s\n", janus_plugin->get_description());
3521
                        JANUS_LOG(LOG_VERB, "\t   Plugin API version: %d\n", janus_plugin->get_api_compatibility());
3522
                        if(!janus_plugin->incoming_rtp && !janus_plugin->incoming_rtcp && !janus_plugin->incoming_data) {
3523
                                JANUS_LOG(LOG_WARN, "The '%s' plugin doesn't implement any callback for RTP/RTCP/data... is this on purpose?\n",
3524
                                        janus_plugin->get_package());
3525
                        }
3526
                        if(!janus_plugin->incoming_rtp && !janus_plugin->incoming_rtcp && janus_plugin->incoming_data) {
3527
                                JANUS_LOG(LOG_WARN, "The '%s' plugin will only handle data channels (no RTP/RTCP)... is this on purpose?\n",
3528
                                        janus_plugin->get_package());
3529
                        }
3530
                        if(plugins == NULL)
3531
                                plugins = g_hash_table_new(g_str_hash, g_str_equal);
3532
                        g_hash_table_insert(plugins, (gpointer)janus_plugin->get_package(), janus_plugin);
3533
                        if(plugins_so == NULL)
3534
                                plugins_so = g_hash_table_new(g_str_hash, g_str_equal);
3535
                        g_hash_table_insert(plugins_so, (gpointer)janus_plugin->get_package(), plugin);
3536
                }
3537
        }
3538
        closedir(dir);
3539
        if(disabled_plugins != NULL)
3540
                g_strfreev(disabled_plugins);
3541
        disabled_plugins = NULL;
3542

    
3543
        /* Create a thread pool to handle incoming requests, no matter what the transport */
3544
        GError *error = NULL;
3545
        tasks = g_thread_pool_new(janus_transport_task, NULL, -1, FALSE, &error);
3546
        if(error != NULL) {
3547
                /* Something went wrong... */
3548
                JANUS_LOG(LOG_FATAL, "Got error %d (%s) trying to launch the request pool task thread...\n", error->code, error->message ? error->message : "??");
3549
                exit(1);
3550
        }
3551

    
3552
        /* Load transports */
3553
        gboolean janus_api_enabled = FALSE, admin_api_enabled = FALSE;
3554
        path = TRANSPORTDIR;
3555
        item = janus_config_get_item_drilldown(config, "general", "transports_folder");
3556
        if(item && item->value)
3557
                path = (char *)item->value;
3558
        JANUS_LOG(LOG_INFO, "Transpor plugins folder: %s\n", path);
3559
        dir = opendir(path);
3560
        if(!dir) {
3561
                JANUS_LOG(LOG_FATAL, "\tCouldn't access transport plugins folder...\n");
3562
                exit(1);
3563
        }
3564
        /* Any transport to ignore? */
3565
        gchar **disabled_transports = NULL;
3566
        item = janus_config_get_item_drilldown(config, "transports", "disable");
3567
        if(item && item->value)
3568
                disabled_transports = g_strsplit(item->value, ",", -1);
3569
        /* Open the shared objects */
3570
        struct dirent *transportent = NULL;
3571
        char transportpath[1024];
3572
        while((transportent = readdir(dir))) {
3573
                int len = strlen(transportent->d_name);
3574
                if (len < 4) {
3575
                        continue;
3576
                }
3577
                if (strcasecmp(transportent->d_name+len-strlen(SHLIB_EXT), SHLIB_EXT)) {
3578
                        continue;
3579
                }
3580
                /* Check if this transports has been disabled in the configuration file */
3581
                if(disabled_transports != NULL) {
3582
                        gchar *index = disabled_transports[0];
3583
                        if(index != NULL) {
3584
                                int i=0;
3585
                                gboolean skip = FALSE;
3586
                                while(index != NULL) {
3587
                                        while(isspace(*index))
3588
                                                index++;
3589
                                        if(strlen(index) && !strcmp(index, transportent->d_name)) {
3590
                                                JANUS_LOG(LOG_WARN, "Transport plugin '%s' has been disabled, skipping...\n", transportent->d_name);
3591
                                                skip = TRUE;
3592
                                                break;
3593
                                        }
3594
                                        i++;
3595
                                        index = disabled_transports[i];
3596
                                }
3597
                                if(skip)
3598
                                        continue;
3599
                        }
3600
                }
3601
                JANUS_LOG(LOG_INFO, "Loading transport plugin '%s'...\n", transportent->d_name);
3602
                memset(transportpath, 0, 1024);
3603
                g_snprintf(transportpath, 1024, "%s/%s", path, transportent->d_name);
3604
                void *transport = dlopen(transportpath, RTLD_LAZY);
3605
                if (!transport) {
3606
                        JANUS_LOG(LOG_ERR, "\tCouldn't load transport plugin '%s': %s\n", transportent->d_name, dlerror());
3607
                } else {
3608
                        create_t *create = (create_t*) dlsym(transport, "create");
3609
                        const char *dlsym_error = dlerror();
3610
                        if (dlsym_error) {
3611
                                JANUS_LOG(LOG_ERR, "\tCouldn't load symbol 'create': %s\n", dlsym_error);
3612
                                continue;
3613
                        }
3614
                        janus_transport *janus_transport = create();
3615
                        if(!janus_transport) {
3616
                                JANUS_LOG(LOG_ERR, "\tCouldn't use function 'create'...\n");
3617
                                continue;
3618
                        }
3619
                        /* Are all the mandatory methods and callbacks implemented? */
3620
                        if(!janus_transport->init || !janus_transport->destroy ||
3621
                                        !janus_transport->get_api_compatibility ||
3622
                                        !janus_transport->get_version ||
3623
                                        !janus_transport->get_version_string ||
3624
                                        !janus_transport->get_description ||
3625
                                        !janus_transport->get_package ||
3626
                                        !janus_transport->get_name ||
3627
                                        !janus_transport->send_message ||
3628
                                        !janus_transport->is_janus_api_enabled ||
3629
                                        !janus_transport->is_admin_api_enabled ||
3630
                                        !janus_transport->session_created ||
3631
                                        !janus_transport->session_over) {
3632
                                JANUS_LOG(LOG_ERR, "\tMissing some mandatory methods/callbacks, skipping this transport plugin...\n");
3633
                                continue;
3634
                        }
3635
                        if(janus_transport->get_api_compatibility() < JANUS_TRANSPORT_API_VERSION) {
3636
                                JANUS_LOG(LOG_ERR, "The '%s' transport plugin was compiled against an older version of the API (%d < %d), skipping it: update it to enable it again\n",
3637
                                        janus_transport->get_package(), janus_transport->get_api_compatibility(), JANUS_TRANSPORT_API_VERSION);
3638
                                continue;
3639
                        }
3640
                        janus_transport->init(&janus_handler_transport, configs_folder);
3641
                        JANUS_LOG(LOG_VERB, "\tVersion: %d (%s)\n", janus_transport->get_version(), janus_transport->get_version_string());
3642
                        JANUS_LOG(LOG_VERB, "\t   [%s] %s\n", janus_transport->get_package(), janus_transport->get_name());
3643
                        JANUS_LOG(LOG_VERB, "\t   %s\n", janus_transport->get_description());
3644
                        JANUS_LOG(LOG_VERB, "\t   Plugin API version: %d\n", janus_transport->get_api_compatibility());
3645
                        JANUS_LOG(LOG_VERB, "\t   Janus API: %s\n", janus_transport->is_janus_api_enabled() ? "enabled" : "disabled");
3646
                        JANUS_LOG(LOG_VERB, "\t   Admin API: %s\n", janus_transport->is_admin_api_enabled() ? "enabled" : "disabled");
3647
                        janus_api_enabled = janus_api_enabled || janus_transport->is_janus_api_enabled();
3648
                        admin_api_enabled = admin_api_enabled || janus_transport->is_admin_api_enabled();
3649
                        if(transports == NULL)
3650
                                transports = g_hash_table_new(g_str_hash, g_str_equal);
3651
                        g_hash_table_insert(transports, (gpointer)janus_transport->get_package(), janus_transport);
3652
                        if(transports_so == NULL)
3653
                                transports_so = g_hash_table_new(g_str_hash, g_str_equal);
3654
                        g_hash_table_insert(transports_so, (gpointer)janus_transport->get_package(), transport);
3655
                }
3656
        }
3657
        closedir(dir);
3658
        if(disabled_transports != NULL)
3659
                g_strfreev(disabled_transports);
3660
        disabled_transports = NULL;
3661
        /* Make sure at least a Janus API transport is available */
3662
        if(!janus_api_enabled) {
3663
                JANUS_LOG(LOG_FATAL, "No Janus API transport is available... enable at least one and restart Janus\n");
3664
                exit(1);        /* FIXME Should we really give up? */
3665
        }
3666
        /* Make sure at least an admin API transport is available, if the auth mechanism is enabled */
3667
        if(!admin_api_enabled && janus_auth_is_enabled()) {
3668
                JANUS_LOG(LOG_FATAL, "No Admin/monitor transport is available, but the token based authentication mechanism is enabled... this will cause all requests to fail, giving up! If you want to use tokens, enable the Admin/monitor API and restart Janus\n");
3669
                exit(1);        /* FIXME Should we really give up? */
3670
        }
3671

    
3672
        /* Sessions */
3673
        sessions = g_hash_table_new(NULL, NULL);
3674
        old_sessions = g_hash_table_new(NULL, NULL);
3675
        janus_mutex_init(&sessions_mutex);
3676
        /* Start the sessions watchdog */
3677
        sessions_watchdog_context = g_main_context_new();
3678
        GMainLoop *watchdog_loop = g_main_loop_new(sessions_watchdog_context, FALSE);
3679
        error = NULL;
3680
        GThread *watchdog = g_thread_try_new("watchdog", &janus_sessions_watchdog, watchdog_loop, &error);
3681
        if(error != NULL) {
3682
                JANUS_LOG(LOG_FATAL, "Got error %d (%s) trying to start sessions watchdog...\n", error->code, error->message ? error->message : "??");
3683
                exit(1);
3684
        }
3685

    
3686
        while(!g_atomic_int_get(&stop)) {
3687
                /* Loop until we have to stop */
3688
                usleep(250000); /* A signal will cancel usleep() but not g_usleep() */
3689
        }
3690

    
3691
        /* Done */
3692
        JANUS_LOG(LOG_INFO, "Ending watchdog mainloop...\n");
3693
        g_main_loop_quit(watchdog_loop);
3694
        g_thread_join(watchdog);
3695
        watchdog = NULL;
3696
        g_main_loop_unref(watchdog_loop);
3697
        g_main_context_unref(sessions_watchdog_context);
3698

    
3699
        if(config)
3700
                janus_config_destroy(config);
3701

    
3702
        JANUS_LOG(LOG_INFO, "Closing transport plugins:\n");
3703
        if(transports != NULL) {
3704
                g_hash_table_foreach(transports, janus_transport_close, NULL);
3705
                g_hash_table_destroy(transports);
3706
        }
3707
        if(transports_so != NULL) {
3708
                g_hash_table_foreach(transports_so, janus_transportso_close, NULL);
3709
                g_hash_table_destroy(transports_so);
3710
        }
3711
        g_thread_pool_free(tasks, FALSE, FALSE);
3712

    
3713
        JANUS_LOG(LOG_INFO, "Destroying sessions...\n");
3714
        if(sessions != NULL)
3715
                g_hash_table_destroy(sessions);
3716
        if(old_sessions != NULL)
3717
                g_hash_table_destroy(old_sessions);
3718
        JANUS_LOG(LOG_INFO, "Freeing crypto resources...\n");
3719
        SSL_CTX_free(janus_dtls_get_ssl_ctx());
3720
        EVP_cleanup();
3721
        ERR_free_strings();
3722
        JANUS_LOG(LOG_INFO, "Cleaning SDP structures...\n");
3723
        janus_sdp_deinit();
3724
#ifdef HAVE_SCTP
3725
        JANUS_LOG(LOG_INFO, "De-initializing SCTP...\n");
3726
        janus_sctp_deinit();
3727
#endif
3728
        janus_ice_deinit();
3729
        janus_auth_deinit();
3730

    
3731
        JANUS_LOG(LOG_INFO, "Closing plugins:\n");
3732
        if(plugins != NULL) {
3733
                g_hash_table_foreach(plugins, janus_plugin_close, NULL);
3734
                g_hash_table_destroy(plugins);
3735
        }
3736
        if(plugins_so != NULL) {
3737
                g_hash_table_foreach(plugins_so, janus_pluginso_close, NULL);
3738
                g_hash_table_destroy(plugins_so);
3739
        }
3740

    
3741
        JANUS_PRINT("Bye!\n");
3742

    
3743
        janus_log_destroy();
3744

    
3745
        exit(0);
3746
}