Statistics
| Branch: | Revision:

janus-gateway / janus.c @ 3f113250

History | View | Annotate | Download (182 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
#include <poll.h>
27

    
28
#include "janus.h"
29
#include "version.h"
30
#include "cmdline.h"
31
#include "config.h"
32
#include "apierror.h"
33
#include "log.h"
34
#include "debug.h"
35
#include "ip-utils.h"
36
#include "rtcp.h"
37
#include "auth.h"
38
#include "record.h"
39
#include "events.h"
40

    
41

    
42
#define JANUS_NAME                                "Janus WebRTC Gateway"
43
#define JANUS_AUTHOR                        "Meetecho s.r.l."
44
#define JANUS_VERSION                        24
45
#define JANUS_VERSION_STRING        "0.2.4"
46
#define JANUS_SERVER_NAME                "MyJanusInstance"
47

    
48
#ifdef __MACH__
49
#define SHLIB_EXT "0.dylib"
50
#else
51
#define SHLIB_EXT ".so"
52
#endif
53

    
54

    
55
static janus_config *config = NULL;
56
static char *config_file = NULL;
57
static char *configs_folder = NULL;
58

    
59
static GHashTable *transports = NULL;
60
static GHashTable *transports_so = NULL;
61

    
62
static GHashTable *eventhandlers = NULL;
63
static GHashTable *eventhandlers_so = NULL;
64

    
65
static GHashTable *plugins = NULL;
66
static GHashTable *plugins_so = NULL;
67

    
68

    
69
/* Daemonization */
70
static gboolean daemonize = FALSE;
71
static int pipefd[2];
72

    
73

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

    
77
/* JSON parameters */
78
static int janus_process_error_string(janus_request *request, uint64_t session_id, const char *transaction, gint error, gchar *error_string);
79

    
80
static struct janus_json_parameter incoming_request_parameters[] = {
81
        {"transaction", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
82
        {"janus", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
83
        {"id", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE}
84
};
85
static struct janus_json_parameter attach_parameters[] = {
86
        {"plugin", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
87
        {"opaque_id", JSON_STRING, 0},
88
        {"force-bundle", JANUS_JSON_BOOL, 0},
89
        {"force-rtcp-mux", JANUS_JSON_BOOL, 0}
90
};
91
static struct janus_json_parameter body_parameters[] = {
92
        {"body", JSON_OBJECT, JANUS_JSON_PARAM_REQUIRED}
93
};
94
static struct janus_json_parameter jsep_parameters[] = {
95
        {"type", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
96
        {"trickle", JANUS_JSON_BOOL, 0},
97
        {"sdp", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
98
};
99
static struct janus_json_parameter allow_token_parameters[] = {
100
        {"token", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
101
        {"plugins", JSON_ARRAY, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_NONEMPTY}
102
};
103
static struct janus_json_parameter add_token_parameters[] = {
104
        {"token", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
105
        {"plugins", JSON_ARRAY, 0}
106
};
107
static struct janus_json_parameter token_parameters[] = {
108
        {"token", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
109
};
110
static struct janus_json_parameter admin_parameters[] = {
111
        {"transaction", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
112
        {"janus", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
113
};
114
static struct janus_json_parameter debug_parameters[] = {
115
        {"debug", JANUS_JSON_BOOL, JANUS_JSON_PARAM_REQUIRED}
116
};
117
static struct janus_json_parameter timeout_parameters[] = {
118
        {"timeout", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
119
};
120
static struct janus_json_parameter level_parameters[] = {
121
        {"level", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
122
};
123
static struct janus_json_parameter timestamps_parameters[] = {
124
        {"timestamps", JANUS_JSON_BOOL, JANUS_JSON_PARAM_REQUIRED}
125
};
126
static struct janus_json_parameter colors_parameters[] = {
127
        {"colors", JANUS_JSON_BOOL, JANUS_JSON_PARAM_REQUIRED}
128
};
129
static struct janus_json_parameter mnq_parameters[] = {
130
        {"max_nack_queue", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
131
};
132
static struct janus_json_parameter nmt_parameters[] = {
133
        {"no_media_timer", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
134
};
135

    
136
/* Admin/Monitor helpers */
137
json_t *janus_admin_stream_summary(janus_ice_stream *stream);
138
json_t *janus_admin_component_summary(janus_ice_component *component);
139

    
140

    
141
/* IP addresses */
142
static gchar *local_ip = NULL;
143
gchar *janus_get_local_ip(void) {
144
        return local_ip;
145
}
146
static gchar *public_ip = NULL;
147
gchar *janus_get_public_ip(void) {
148
        /* Fallback to the local IP, if we have no public one */
149
        return public_ip ? public_ip : local_ip;
150
}
151
void janus_set_public_ip(const char *ip) {
152
        /* once set do not override */
153
        if(ip == NULL || public_ip != NULL)
154
                return;
155
        public_ip = g_strdup(ip);
156
}
157
static volatile gint stop = 0;
158
static gint stop_signal = 0;
159
gint janus_is_stopping(void) {
160
        return g_atomic_int_get(&stop);
161
}
162

    
163

    
164
/* Public instance name */
165
static gchar *server_name = NULL;
166

    
167

    
168
/* The default timeout for sessions is 60 seconds: this means that, if
169
 * we don't get any activity (i.e., no request) on this session for more
170
 * than 60 seconds, then it's considered expired and we destroy it. That's
171
 * why we have a keep-alive method in the API. This can be overridden in
172
 * either janus.cfg or from the command line. Setting this to 0 will
173
 * disable the timeout mechanism, which is NOT suggested as it may risk
174
 * having orphaned sessions (sessions not controlled by any transport
175
 * and never freed). Besides, notice that if you make this shorter than
176
 * 30s, you'll have to update the timers in janus.js when the long
177
 * polling mechanism is used and shorten them as well, or you'll risk
178
 * incurring in unexpected timeouts (when HTTP is used in janus.js, the
179
 * long poll is used as a keepalive mechanism). */
180
#define DEFAULT_SESSION_TIMEOUT                60
181
static uint session_timeout = DEFAULT_SESSION_TIMEOUT;
182

    
183

    
184
/* Information */
185
static json_t *janus_info(const char *transaction) {
186
        /* Prepare a summary on the gateway */
187
        json_t *info = json_object();
188
        json_object_set_new(info, "janus", json_string("server_info"));
189
        if(transaction != NULL)
190
                json_object_set_new(info, "transaction", json_string(transaction));
191
        json_object_set_new(info, "name", json_string(JANUS_NAME));
192
        json_object_set_new(info, "version", json_integer(JANUS_VERSION));
193
        json_object_set_new(info, "version_string", json_string(JANUS_VERSION_STRING));
194
        json_object_set_new(info, "author", json_string(JANUS_AUTHOR));
195
        json_object_set_new(info, "commit-hash", json_string(janus_build_git_sha));
196
        json_object_set_new(info, "compile-time", json_string(janus_build_git_time));
197
        json_object_set_new(info, "log-to-stdout", janus_log_is_stdout_enabled() ? json_true() : json_false());
198
        json_object_set_new(info, "log-to-file", janus_log_is_logfile_enabled() ? json_true() : json_false());
199
        if(janus_log_is_logfile_enabled())
200
                json_object_set_new(info, "log-path", json_string(janus_log_get_logfile_path()));
201
#ifdef HAVE_SCTP
202
        json_object_set_new(info, "data_channels", json_true());
203
#else
204
        json_object_set_new(info, "data_channels", json_false());
205
#endif
206
        json_object_set_new(info, "session-timeout", json_integer(session_timeout));
207
        json_object_set_new(info, "server-name", json_string(server_name ? server_name : JANUS_SERVER_NAME));
208
        json_object_set_new(info, "local-ip", json_string(local_ip));
209
        if(public_ip != NULL)
210
                json_object_set_new(info, "public-ip", json_string(public_ip));
211
        json_object_set_new(info, "ipv6", janus_ice_is_ipv6_enabled() ? json_true() : json_false());
212
        json_object_set_new(info, "ice-lite", janus_ice_is_ice_lite_enabled() ? json_true() : json_false());
213
        json_object_set_new(info, "ice-tcp", janus_ice_is_ice_tcp_enabled() ? json_true() : json_false());
214
        if(janus_ice_get_stun_server() != NULL) {
215
                char server[255];
216
                g_snprintf(server, 255, "%s:%"SCNu16, janus_ice_get_stun_server(), janus_ice_get_stun_port());
217
                json_object_set_new(info, "stun-server", json_string(server));
218
        }
219
        if(janus_ice_get_turn_server() != NULL) {
220
                char server[255];
221
                g_snprintf(server, 255, "%s:%"SCNu16, janus_ice_get_turn_server(), janus_ice_get_turn_port());
222
                json_object_set_new(info, "turn-server", json_string(server));
223
        }
224
        json_object_set_new(info, "api_secret", api_secret ? json_true() : json_false());
225
        json_object_set_new(info, "auth_token", janus_auth_is_enabled() ? json_true() : json_false());
226
        json_object_set_new(info, "event_handlers", janus_events_is_enabled() ? json_true() : json_false());
227
        /* Available transports */
228
        json_t *t_data = json_object();
229
        if(transports && g_hash_table_size(transports) > 0) {
230
                GHashTableIter iter;
231
                gpointer value;
232
                g_hash_table_iter_init(&iter, transports);
233
                while (g_hash_table_iter_next(&iter, NULL, &value)) {
234
                        janus_transport *t = value;
235
                        if(t == NULL) {
236
                                continue;
237
                        }
238
                        json_t *transport = json_object();
239
                        json_object_set_new(transport, "name", json_string(t->get_name()));
240
                        json_object_set_new(transport, "author", json_string(t->get_author()));
241
                        json_object_set_new(transport, "description", json_string(t->get_description()));
242
                        json_object_set_new(transport, "version_string", json_string(t->get_version_string()));
243
                        json_object_set_new(transport, "version", json_integer(t->get_version()));
244
                        json_object_set_new(t_data, t->get_package(), transport);
245
                }
246
        }
247
        json_object_set_new(info, "transports", t_data);
248
        /* Available event handlers */
249
        json_t *e_data = json_object();
250
        if(eventhandlers && g_hash_table_size(eventhandlers) > 0) {
251
                GHashTableIter iter;
252
                gpointer value;
253
                g_hash_table_iter_init(&iter, eventhandlers);
254
                while (g_hash_table_iter_next(&iter, NULL, &value)) {
255
                        janus_eventhandler *e = value;
256
                        if(e == NULL) {
257
                                continue;
258
                        }
259
                        json_t *eventhandler = json_object();
260
                        json_object_set_new(eventhandler, "name", json_string(e->get_name()));
261
                        json_object_set_new(eventhandler, "author", json_string(e->get_author()));
262
                        json_object_set_new(eventhandler, "description", json_string(e->get_description()));
263
                        json_object_set_new(eventhandler, "version_string", json_string(e->get_version_string()));
264
                        json_object_set_new(eventhandler, "version", json_integer(e->get_version()));
265
                        json_object_set_new(e_data, e->get_package(), eventhandler);
266
                }
267
        }
268
        json_object_set_new(info, "events", e_data);
269
        /* Available plugins */
270
        json_t *p_data = json_object();
271
        if(plugins && g_hash_table_size(plugins) > 0) {
272
                GHashTableIter iter;
273
                gpointer value;
274
                g_hash_table_iter_init(&iter, plugins);
275
                while (g_hash_table_iter_next(&iter, NULL, &value)) {
276
                        janus_plugin *p = value;
277
                        if(p == NULL) {
278
                                continue;
279
                        }
280
                        json_t *plugin = json_object();
281
                        json_object_set_new(plugin, "name", json_string(p->get_name()));
282
                        json_object_set_new(plugin, "author", json_string(p->get_author()));
283
                        json_object_set_new(plugin, "description", json_string(p->get_description()));
284
                        json_object_set_new(plugin, "version_string", json_string(p->get_version_string()));
285
                        json_object_set_new(plugin, "version", json_integer(p->get_version()));
286
                        json_object_set_new(p_data, p->get_package(), plugin);
287
                }
288
        }
289
        json_object_set_new(info, "plugins", p_data);
290

    
291
        return info;
292
}
293

    
294

    
295
/* Logging */
296
int janus_log_level = LOG_INFO;
297
gboolean janus_log_timestamps = FALSE;
298
gboolean janus_log_colors = FALSE;
299
int lock_debug = 0;
300

    
301

    
302
/*! \brief Signal handler (just used to intercept CTRL+C and SIGTERM) */
303
static void janus_handle_signal(int signum) {
304
        stop_signal = signum;
305
        switch(g_atomic_int_get(&stop)) {
306
                case 0:
307
                        JANUS_PRINT("Stopping gateway, please wait...\n");
308
                        break;
309
                case 1:
310
                        JANUS_PRINT("In a hurry? I'm trying to free resources cleanly, here!\n");
311
                        break;
312
                default:
313
                        JANUS_PRINT("Ok, leaving immediately...\n");
314
                        break;
315
        }
316
        g_atomic_int_inc(&stop);
317
        if(g_atomic_int_get(&stop) > 2)
318
                exit(1);
319
}
320

    
321
/*! \brief Termination handler (atexit) */
322
static void janus_termination_handler(void) {
323
        /* Free the instance name, if provided */
324
        g_free(server_name);
325
        /* Remove the PID file if we created it */
326
        janus_pidfile_remove();
327
        /* Close the logger */
328
        janus_log_destroy();
329
        /* If we're daemonizing, we send an error code to the parent */
330
        if(daemonize) {
331
                int code = 1;
332
                ssize_t res = 0;
333
                do {
334
                        res = write(pipefd[1], &code, sizeof(int));
335
                } while(res == -1 && errno == EINTR);
336
        }
337
}
338

    
339

    
340
/** @name Transport plugin callback interface
341
 * These are the callbacks implemented by the gateway core, as part of
342
 * the janus_transport_callbacks interface. Everything the transport
343
 * plugins send the gateway is handled here.
344
 */
345
///@{
346
void janus_transport_incoming_request(janus_transport *plugin, void *transport, void *request_id, gboolean admin, json_t *message, json_error_t *error);
347
void janus_transport_gone(janus_transport *plugin, void *transport);
348
gboolean janus_transport_is_api_secret_needed(janus_transport *plugin);
349
gboolean janus_transport_is_api_secret_valid(janus_transport *plugin, const char *apisecret);
350
gboolean janus_transport_is_auth_token_needed(janus_transport *plugin);
351
gboolean janus_transport_is_auth_token_valid(janus_transport *plugin, const char *token);
352
void janus_transport_notify_event(janus_transport *plugin, void *transport, json_t *event);
353

    
354
static janus_transport_callbacks janus_handler_transport =
355
        {
356
                .incoming_request = janus_transport_incoming_request,
357
                .transport_gone = janus_transport_gone,
358
                .is_api_secret_needed = janus_transport_is_api_secret_needed,
359
                .is_api_secret_valid = janus_transport_is_api_secret_valid,
360
                .is_auth_token_needed = janus_transport_is_auth_token_needed,
361
                .is_auth_token_valid = janus_transport_is_auth_token_valid,
362
                .events_is_enabled = janus_events_is_enabled,
363
                .notify_event = janus_transport_notify_event,
364
        };
365
GThreadPool *tasks = NULL;
366
void janus_transport_task(gpointer data, gpointer user_data);
367
///@}
368

    
369

    
370
/** @name Plugin callback interface
371
 * These are the callbacks implemented by the gateway core, as part of
372
 * the janus_callbacks interface. Everything the plugins send the
373
 * gateway is handled here.
374
 */
375
///@{
376
int janus_plugin_push_event(janus_plugin_session *plugin_session, janus_plugin *plugin, const char *transaction, json_t *message, json_t *jsep);
377
json_t *janus_plugin_handle_sdp(janus_plugin_session *plugin_session, janus_plugin *plugin, const char *sdp_type, const char *sdp);
378
void janus_plugin_relay_rtp(janus_plugin_session *plugin_session, int video, char *buf, int len);
379
void janus_plugin_relay_rtcp(janus_plugin_session *plugin_session, int video, char *buf, int len);
380
void janus_plugin_relay_data(janus_plugin_session *plugin_session, char *buf, int len);
381
void janus_plugin_close_pc(janus_plugin_session *plugin_session);
382
void janus_plugin_end_session(janus_plugin_session *plugin_session);
383
void janus_plugin_notify_event(janus_plugin *plugin, janus_plugin_session *plugin_session, json_t *event);
384
static janus_callbacks janus_handler_plugin =
385
        {
386
                .push_event = janus_plugin_push_event,
387
                .relay_rtp = janus_plugin_relay_rtp,
388
                .relay_rtcp = janus_plugin_relay_rtcp,
389
                .relay_data = janus_plugin_relay_data,
390
                .close_pc = janus_plugin_close_pc,
391
                .end_session = janus_plugin_end_session,
392
                .events_is_enabled = janus_events_is_enabled,
393
                .notify_event = janus_plugin_notify_event,
394
        }; 
395
///@}
396

    
397

    
398
/* Gateway Sessions */
399
static janus_mutex sessions_mutex;
400
static GHashTable *sessions = NULL, *old_sessions = NULL;
401
static GMainContext *sessions_watchdog_context = NULL;
402

    
403

    
404
static gboolean janus_cleanup_session(gpointer user_data) {
405
        janus_session *session = (janus_session *) user_data;
406

    
407
        JANUS_LOG(LOG_DBG, "Cleaning up session %"SCNu64"...\n", session->session_id);
408
        janus_session_destroy(session->session_id);
409

    
410
        return G_SOURCE_REMOVE;
411
}
412

    
413
static gboolean janus_check_sessions(gpointer user_data) {
414
        if(session_timeout < 1)                /* Session timeouts are disabled */
415
                return G_SOURCE_CONTINUE;
416
        GMainContext *watchdog_context = (GMainContext *) user_data;
417
        janus_mutex_lock(&sessions_mutex);
418
        if(sessions && g_hash_table_size(sessions) > 0) {
419
                GHashTableIter iter;
420
                gpointer value;
421
                g_hash_table_iter_init(&iter, sessions);
422
                while (g_hash_table_iter_next(&iter, NULL, &value)) {
423
                        janus_session *session = (janus_session *) value;
424
                        if (!session || session->destroy) {
425
                                continue;
426
                        }
427
                        gint64 now = janus_get_monotonic_time();
428
                        if (now - session->last_activity >= session_timeout * G_USEC_PER_SEC && !session->timeout) {
429
                                JANUS_LOG(LOG_INFO, "Timeout expired for session %"SCNu64"...\n", session->session_id);
430

    
431
                                /* Notify the transport */
432
                                if(session->source) {
433
                                        json_t *event = json_object();
434
                                        json_object_set_new(event, "janus", json_string("timeout"));
435
                                        json_object_set_new(event, "session_id", json_integer(session->session_id));
436
                                        /* Send this to the transport client */
437
                                        session->source->transport->send_message(session->source->instance, NULL, FALSE, event);
438
                                        /* Notify the transport plugin about the session timeout */
439
                                        session->source->transport->session_over(session->source->instance, session->session_id, TRUE);
440
                                }
441
                                /* Notify event handlers as well */
442
                                if(janus_events_is_enabled())
443
                                        janus_events_notify_handlers(JANUS_EVENT_TYPE_SESSION, session->session_id, "timeout", NULL);
444

    
445
                                /* Mark the session as over, we'll deal with it later */
446
                                session->timeout = 1;
447
                                /* FIXME Is this safe? apparently it causes hash table errors on the console */
448
                                g_hash_table_iter_remove(&iter);
449
                                g_hash_table_insert(old_sessions, janus_uint64_dup(session->session_id), session);
450

    
451
                                /* Schedule the session for deletion */
452
                                GSource *timeout_source = g_timeout_source_new_seconds(3);
453
                                g_source_set_callback(timeout_source, janus_cleanup_session, session, NULL);
454
                                g_source_attach(timeout_source, watchdog_context);
455
                                g_source_unref(timeout_source);
456
                        }
457
                }
458
        }
459
        janus_mutex_unlock(&sessions_mutex);
460

    
461
        return G_SOURCE_CONTINUE;
462
}
463

    
464
static gpointer janus_sessions_watchdog(gpointer user_data) {
465
        GMainLoop *loop = (GMainLoop *) user_data;
466
        GMainContext *watchdog_context = g_main_loop_get_context(loop);
467
        GSource *timeout_source;
468

    
469
        timeout_source = g_timeout_source_new_seconds(2);
470
        g_source_set_callback(timeout_source, janus_check_sessions, watchdog_context, NULL);
471
        g_source_attach(timeout_source, watchdog_context);
472
        g_source_unref(timeout_source);
473

    
474
        JANUS_LOG(LOG_INFO, "Sessions watchdog started\n");
475

    
476
        g_main_loop_run(loop);
477

    
478
        return NULL;
479
}
480

    
481
janus_session *janus_session_create(guint64 session_id) {
482
        if(session_id == 0) {
483
                while(session_id == 0) {
484
                        session_id = janus_random_uint64();
485
                        if(janus_session_find(session_id) != NULL) {
486
                                /* Session ID already taken, try another one */
487
                                session_id = 0;
488
                        }
489
                }
490
        }
491
        JANUS_LOG(LOG_INFO, "Creating new session: %"SCNu64"\n", session_id);
492
        janus_session *session = (janus_session *)g_malloc0(sizeof(janus_session));
493
        if(session == NULL) {
494
                JANUS_LOG(LOG_FATAL, "Memory error!\n");
495
                return NULL;
496
        }
497
        session->session_id = session_id;
498
        session->source = NULL;
499
        session->destroy = 0;
500
        session->timeout = 0;
501
        session->last_activity = janus_get_monotonic_time();
502
        janus_mutex_init(&session->mutex);
503
        janus_mutex_lock(&sessions_mutex);
504
        g_hash_table_insert(sessions, janus_uint64_dup(session->session_id), session);
505
        janus_mutex_unlock(&sessions_mutex);
506
        return session;
507
}
508

    
509
janus_session *janus_session_find(guint64 session_id) {
510
        janus_mutex_lock(&sessions_mutex);
511
        janus_session *session = g_hash_table_lookup(sessions, &session_id);
512
        janus_mutex_unlock(&sessions_mutex);
513
        return session;
514
}
515

    
516
janus_session *janus_session_find_destroyed(guint64 session_id) {
517
        janus_mutex_lock(&sessions_mutex);
518
        janus_session *session = g_hash_table_lookup(old_sessions, &session_id);
519
        janus_mutex_unlock(&sessions_mutex);
520
        return session;
521
}
522

    
523
void janus_session_notify_event(guint64 session_id, json_t *event) {
524
        janus_mutex_lock(&sessions_mutex);
525
        janus_session *session = sessions ? g_hash_table_lookup(sessions, &session_id) : NULL;
526
        if(session != NULL && !session->destroy && session->source != NULL && session->source->transport != NULL) {
527
                janus_mutex_unlock(&sessions_mutex);
528
                /* Send this to the transport client */
529
                JANUS_LOG(LOG_HUGE, "Sending event to %s (%p)\n", session->source->transport->get_package(), session->source->instance);
530
                session->source->transport->send_message(session->source->instance, NULL, FALSE, event);
531
        } else {
532
                janus_mutex_unlock(&sessions_mutex);
533
                /* No transport, free the event */
534
                json_decref(event);
535
        }
536
}
537

    
538

    
539
/* Destroys a session but does not remove it from the sessions hash table. */
540
gint janus_session_destroy(guint64 session_id) {
541
        janus_session *session = janus_session_find_destroyed(session_id);
542
        if(session == NULL) {
543
                JANUS_LOG(LOG_ERR, "Couldn't find session to destroy: %"SCNu64"\n", session_id);
544
                return -1;
545
        }
546
        JANUS_LOG(LOG_VERB, "Destroying session %"SCNu64"\n", session_id);
547
        session->destroy = 1;
548
        if (session->ice_handles != NULL && g_hash_table_size(session->ice_handles) > 0) {
549
                GHashTableIter iter;
550
                gpointer value;
551
                /* Remove all handles */
552
                g_hash_table_iter_init(&iter, session->ice_handles);
553
                while (g_hash_table_iter_next(&iter, NULL, &value)) {
554
                        janus_ice_handle *handle = value;
555
                        if(!handle || g_atomic_int_get(&stop)) {
556
                                continue;
557
                        }
558
                        janus_ice_handle_destroy(session, handle->handle_id);
559
                        g_hash_table_iter_remove(&iter);
560
                }
561
        }
562

    
563
        /* FIXME Actually destroy session */
564
        janus_session_free(session);
565

    
566
        return 0;
567
}
568

    
569
void janus_session_free(janus_session *session) {
570
        if(session == NULL)
571
                return;
572
        janus_mutex_lock(&session->mutex);
573
        if(session->ice_handles != NULL) {
574
                g_hash_table_destroy(session->ice_handles);
575
                session->ice_handles = NULL;
576
        }
577
        if(session->source != NULL) {
578
                janus_request_destroy(session->source);
579
                session->source = NULL;
580
        }
581
        janus_mutex_unlock(&session->mutex);
582
        g_free(session);
583
        session = NULL;
584
}
585

    
586

    
587
/* Requests management */
588
janus_request *janus_request_new(janus_transport *transport, void *instance, void *request_id, gboolean admin, json_t *message) {
589
        janus_request *request = (janus_request *)g_malloc0(sizeof(janus_request));
590
        request->transport = transport;
591
        request->instance = instance;
592
        request->request_id = request_id;
593
        request->admin = admin;
594
        request->message = message;
595
        return request;
596
}
597

    
598
void janus_request_destroy(janus_request *request) {
599
        if(request == NULL)
600
                return;
601
        request->transport = NULL;
602
        request->instance = NULL;
603
        request->request_id = NULL;
604
        if(request->message)
605
                json_decref(request->message);
606
        request->message = NULL;
607
        g_free(request);
608
}
609

    
610
int janus_process_incoming_request(janus_request *request) {
611
        int ret = -1;
612
        if(request == NULL) {
613
                JANUS_LOG(LOG_ERR, "Missing request or payload to process, giving up...\n");
614
                return ret;
615
        }
616
        int error_code = 0;
617
        char error_cause[100];
618
        json_t *root = request->message;
619
        /* Ok, let's start with the ids */
620
        guint64 session_id = 0, handle_id = 0;
621
        json_t *s = json_object_get(root, "session_id");
622
        if(s && json_is_integer(s))
623
                session_id = json_integer_value(s);
624
        json_t *h = json_object_get(root, "handle_id");
625
        if(h && json_is_integer(h))
626
                handle_id = json_integer_value(h);
627

    
628
        /* Get transaction and message request */
629
        JANUS_VALIDATE_JSON_OBJECT(root, incoming_request_parameters,
630
                error_code, error_cause, FALSE,
631
                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
632
        if(error_code != 0) {
633
                ret = janus_process_error_string(request, session_id, NULL, error_code, error_cause);
634
                goto jsondone;
635
        }
636
        json_t *transaction = json_object_get(root, "transaction");
637
        const gchar *transaction_text = json_string_value(transaction);
638
        json_t *message = json_object_get(root, "janus");
639
        const gchar *message_text = json_string_value(message);
640

    
641
        if(session_id == 0 && handle_id == 0) {
642
                /* Can only be a 'Create new session', a 'Get info' or a 'Ping/Pong' request */
643
                if(!strcasecmp(message_text, "info")) {
644
                        ret = janus_process_success(request, janus_info(transaction_text));
645
                        goto jsondone;
646
                }
647
                if(!strcasecmp(message_text, "ping")) {
648
                        /* Prepare JSON reply */
649
                        json_t *reply = json_object();
650
                        json_object_set_new(reply, "janus", json_string("pong"));
651
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
652
                        ret = janus_process_success(request, reply);
653
                        goto jsondone;
654
                }
655
                if(strcasecmp(message_text, "create")) {
656
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
657
                        goto jsondone;
658
                }
659
                /* Any secret/token to check? */
660
                gboolean secret_authorized = FALSE, token_authorized = FALSE;
661
                if(api_secret == NULL && !janus_auth_is_enabled()) {
662
                        /* Nothing to check */
663
                        secret_authorized = TRUE;
664
                        token_authorized = TRUE;
665
                } else {
666
                        if(api_secret != NULL) {
667
                                /* There's an API secret, check that the client provided it */
668
                                json_t *secret = json_object_get(root, "apisecret");
669
                                if(secret && json_is_string(secret) && janus_strcmp_const_time(json_string_value(secret), api_secret)) {
670
                                        secret_authorized = TRUE;
671
                                }
672
                        }
673
                        if(janus_auth_is_enabled()) {
674
                                /* The token based authentication mechanism is enabled, check that the client provided it */
675
                                json_t *token = json_object_get(root, "token");
676
                                if(token && json_is_string(token) && janus_auth_check_token(json_string_value(token))) {
677
                                        token_authorized = TRUE;
678
                                }
679
                        }
680
                        /* We consider a request authorized if either the proper API secret or a valid token has been provided */
681
                        if(!secret_authorized && !token_authorized) {
682
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED, NULL);
683
                                goto jsondone;
684
                        }
685
                }
686
                session_id = 0;
687
                json_t *id = json_object_get(root, "id");
688
                if(id != NULL) {
689
                        /* The application provided the session ID to use */
690
                        session_id = json_integer_value(id);
691
                        if(session_id > 0 && janus_session_find(session_id) != NULL) {
692
                                /* Session ID already taken */
693
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_CONFLICT, "Session ID already in use");
694
                                goto jsondone;
695
                        }
696
                }
697
                /* Handle it */
698
                janus_session *session = janus_session_create(session_id);
699
                if(session == NULL) {
700
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Memory error");
701
                        goto jsondone;
702
                }
703
                session_id = session->session_id;
704
                /* Take note of the request source that originated this session (HTTP, WebSockets, RabbitMQ?) */
705
                session->source = janus_request_new(request->transport, request->instance, NULL, FALSE, NULL);
706
                /* Notify the source that a new session has been created */
707
                request->transport->session_created(request->instance, session->session_id);
708
                /* Notify event handlers */
709
                if(janus_events_is_enabled()) {
710
                        /* Session created, add info on the transport that originated it */
711
                        json_t *transport = json_object();
712
                        json_object_set_new(transport, "transport", json_string(session->source->transport->get_package()));
713
                        char id[32];
714
                        memset(id, 0, sizeof(id));
715
                        g_snprintf(id, sizeof(id), "%p", session->source->instance);
716
                        json_object_set_new(transport, "id", json_string(id));
717
                        janus_events_notify_handlers(JANUS_EVENT_TYPE_SESSION, session_id, "created", transport);
718
                }
719
                /* Prepare JSON reply */
720
                json_t *reply = json_object();
721
                json_object_set_new(reply, "janus", json_string("success"));
722
                json_object_set_new(reply, "transaction", json_string(transaction_text));
723
                json_t *data = json_object();
724
                json_object_set_new(data, "id", json_integer(session_id));
725
                json_object_set_new(reply, "data", data);
726
                /* Send the success reply */
727
                ret = janus_process_success(request, reply);
728
                goto jsondone;
729
        }
730
        if(session_id < 1) {
731
                JANUS_LOG(LOG_ERR, "Invalid session\n");
732
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, NULL);
733
                goto jsondone;
734
        }
735
        if(h && handle_id < 1) {
736
                JANUS_LOG(LOG_ERR, "Invalid handle\n");
737
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, NULL);
738
                goto jsondone;
739
        }
740

    
741
        /* Go on with the processing */
742
        gboolean secret_authorized = FALSE, token_authorized = FALSE;
743
        if(api_secret == NULL && !janus_auth_is_enabled()) {
744
                /* Nothing to check */
745
                secret_authorized = TRUE;
746
                token_authorized = TRUE;
747
        } else {
748
                if(api_secret != NULL) {
749
                        /* There's an API secret, check that the client provided it */
750
                        json_t *secret = json_object_get(root, "apisecret");
751
                        if(secret && json_is_string(secret) && janus_strcmp_const_time(json_string_value(secret), api_secret)) {
752
                                secret_authorized = TRUE;
753
                        }
754
                }
755
                if(janus_auth_is_enabled()) {
756
                        /* The token based authentication mechanism is enabled, check that the client provided it */
757
                        json_t *token = json_object_get(root, "token");
758
                        if(token && json_is_string(token) && janus_auth_check_token(json_string_value(token))) {
759
                                token_authorized = TRUE;
760
                        }
761
                }
762
                /* We consider a request authorized if either the proper API secret or a valid token has been provided */
763
                if(!secret_authorized && !token_authorized) {
764
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED, NULL);
765
                        goto jsondone;
766
                }
767
        }
768

    
769
        /* If we got here, make sure we have a session (and/or a handle) */
770
        janus_session *session = janus_session_find(session_id);
771
        if(!session) {
772
                JANUS_LOG(LOG_ERR, "Couldn't find any session %"SCNu64"...\n", session_id);
773
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, "No such session %"SCNu64"", session_id);
774
                goto jsondone;
775
        }
776
        /* Update the last activity timer */
777
        session->last_activity = janus_get_monotonic_time();
778
        janus_ice_handle *handle = NULL;
779
        if(handle_id > 0) {
780
                handle = janus_ice_handle_find(session, handle_id);
781
                if(!handle) {
782
                        JANUS_LOG(LOG_ERR, "Couldn't find any handle %"SCNu64" in session %"SCNu64"...\n", handle_id, session_id);
783
                        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);
784
                        goto jsondone;
785
                }
786
        }
787

    
788
        /* What is this? */
789
        if(!strcasecmp(message_text, "keepalive")) {
790
                /* Just a keep-alive message, reply with an ack */
791
                JANUS_LOG(LOG_VERB, "Got a keep-alive on session %"SCNu64"\n", session_id);
792
                json_t *reply = json_object();
793
                json_object_set_new(reply, "janus", json_string("ack"));
794
                json_object_set_new(reply, "session_id", json_integer(session_id));
795
                json_object_set_new(reply, "transaction", json_string(transaction_text));
796
                /* Send the success reply */
797
                ret = janus_process_success(request, reply);
798
        } else if(!strcasecmp(message_text, "attach")) {
799
                if(handle != NULL) {
800
                        /* Attach is a session-level command */
801
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
802
                        goto jsondone;
803
                }
804
                JANUS_VALIDATE_JSON_OBJECT(root, attach_parameters,
805
                        error_code, error_cause, FALSE,
806
                        JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
807
                if(error_code != 0) {
808
                        ret = janus_process_error_string(request, session_id, NULL, error_code, error_cause);
809
                        goto jsondone;
810
                }
811
                json_t *plugin = json_object_get(root, "plugin");
812
                gboolean force_bundle = json_is_true(json_object_get(root, "force-bundle"));
813
                gboolean force_rtcp_mux = json_is_true(json_object_get(root, "force-rtcp-mux"));
814
                const gchar *plugin_text = json_string_value(plugin);
815
                janus_plugin *plugin_t = janus_plugin_find(plugin_text);
816
                if(plugin_t == NULL) {
817
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_NOT_FOUND, "No such plugin '%s'", plugin_text);
818
                        goto jsondone;
819
                }
820
                /* If the auth token mechanism is enabled, we should check if this token can access this plugin */
821
                if(janus_auth_is_enabled()) {
822
                        json_t *token = json_object_get(root, "token");
823
                        if(token != NULL) {
824
                                const char *token_value = json_string_value(token);
825
                                if(token_value && !janus_auth_check_plugin(token_value, plugin_t)) {
826
                                        JANUS_LOG(LOG_ERR, "Token '%s' can't access plugin '%s'\n", token_value, plugin_text);
827
                                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED_PLUGIN, "Provided token can't access plugin '%s'", plugin_text);
828
                                        goto jsondone;
829
                                }
830
                        }
831
                }
832
                json_t *opaque = json_object_get(root, "opaque_id");
833
                const char *opaque_id = opaque ? json_string_value(opaque) : NULL;
834
                /* Create handle */
835
                handle = janus_ice_handle_create(session, opaque_id);
836
                if(handle == NULL) {
837
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Memory error");
838
                        goto jsondone;
839
                }
840
                handle_id = handle->handle_id;
841
                handle->force_bundle = force_bundle;
842
                handle->force_rtcp_mux = force_rtcp_mux;
843
                /* Attach to the plugin */
844
                int error = 0;
845
                if((error = janus_ice_handle_attach_plugin(session, handle_id, plugin_t)) != 0) {
846
                        /* TODO Make error struct to pass verbose information */
847
                        janus_ice_handle_destroy(session, handle_id);
848
                        janus_mutex_lock(&session->mutex);
849
                        g_hash_table_remove(session->ice_handles, &handle_id);
850
                        janus_mutex_unlock(&session->mutex);
851
                        JANUS_LOG(LOG_ERR, "Couldn't attach to plugin '%s', error '%d'\n", plugin_text, error);
852
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_ATTACH, "Couldn't attach to plugin: error '%d'", error);
853
                        goto jsondone;
854
                }
855
                /* Prepare JSON reply */
856
                json_t *reply = json_object();
857
                json_object_set_new(reply, "janus", json_string("success"));
858
                json_object_set_new(reply, "session_id", json_integer(session_id));
859
                json_object_set_new(reply, "transaction", json_string(transaction_text));
860
                json_t *data = json_object();
861
                json_object_set_new(data, "id", json_integer(handle_id));
862
                json_object_set_new(reply, "data", data);
863
                /* Send the success reply */
864
                ret = janus_process_success(request, reply);
865
        } else if(!strcasecmp(message_text, "destroy")) {
866
                if(handle != NULL) {
867
                        /* Query is a session-level command */
868
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
869
                        goto jsondone;
870
                }
871
                /* Schedule the session for deletion */
872
                session->destroy = 1;
873
                janus_mutex_lock(&sessions_mutex);
874
                g_hash_table_remove(sessions, &session->session_id);
875
                g_hash_table_insert(old_sessions, janus_uint64_dup(session->session_id), session);
876
                GSource *timeout_source = g_timeout_source_new_seconds(3);
877
                g_source_set_callback(timeout_source, janus_cleanup_session, session, NULL);
878
                g_source_attach(timeout_source, sessions_watchdog_context);
879
                g_source_unref(timeout_source);
880
                janus_mutex_unlock(&sessions_mutex);
881
                /* Notify the source that the session has been destroyed */
882
                if(session->source && session->source->transport)
883
                        session->source->transport->session_over(session->source->instance, session->session_id, FALSE);
884

    
885
                /* Prepare JSON reply */
886
                json_t *reply = json_object();
887
                json_object_set_new(reply, "janus", json_string("success"));
888
                json_object_set_new(reply, "session_id", json_integer(session_id));
889
                json_object_set_new(reply, "transaction", json_string(transaction_text));
890
                /* Send the success reply */
891
                ret = janus_process_success(request, reply);
892
                /* Notify event handlers as well */
893
                if(janus_events_is_enabled())
894
                        janus_events_notify_handlers(JANUS_EVENT_TYPE_SESSION, session_id, "destroyed", NULL);
895
        } else if(!strcasecmp(message_text, "detach")) {
896
                if(handle == NULL) {
897
                        /* Query is an handle-level command */
898
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
899
                        goto jsondone;
900
                }
901
                if(handle->app == NULL || handle->app_handle == NULL) {
902
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_DETACH, "No plugin to detach from");
903
                        goto jsondone;
904
                }
905
                int error = janus_ice_handle_destroy(session, handle_id);
906
                janus_mutex_lock(&session->mutex);
907
                g_hash_table_remove(session->ice_handles, &handle_id);
908
                janus_mutex_unlock(&session->mutex);
909

    
910
                if(error != 0) {
911
                        /* TODO Make error struct to pass verbose information */
912
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_DETACH, "Couldn't detach from plugin: error '%d'", error);
913
                        /* TODO Delete handle instance */
914
                        goto jsondone;
915
                }
916
                /* Prepare JSON reply */
917
                json_t *reply = json_object();
918
                json_object_set_new(reply, "janus", json_string("success"));
919
                json_object_set_new(reply, "session_id", json_integer(session_id));
920
                json_object_set_new(reply, "transaction", json_string(transaction_text));
921
                /* Send the success reply */
922
                ret = janus_process_success(request, reply);
923
        } else if(!strcasecmp(message_text, "hangup")) {
924
                if(handle == NULL) {
925
                        /* Query is an handle-level command */
926
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
927
                        goto jsondone;
928
                }
929
                if(handle->app == NULL || handle->app_handle == NULL) {
930
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_DETACH, "No plugin attached");
931
                        goto jsondone;
932
                }
933
                janus_ice_webrtc_hangup(handle, "Janus API");
934
                /* Prepare JSON reply */
935
                json_t *reply = json_object();
936
                json_object_set_new(reply, "janus", json_string("success"));
937
                json_object_set_new(reply, "session_id", json_integer(session_id));
938
                json_object_set_new(reply, "transaction", json_string(transaction_text));
939
                /* Send the success reply */
940
                ret = janus_process_success(request, reply);
941
        } else if(!strcasecmp(message_text, "message")) {
942
                if(handle == NULL) {
943
                        /* Query is an handle-level command */
944
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
945
                        goto jsondone;
946
                }
947
                if(handle->app == NULL || handle->app_handle == NULL) {
948
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "No plugin to handle this message");
949
                        goto jsondone;
950
                }
951
                janus_plugin *plugin_t = (janus_plugin *)handle->app;
952
                JANUS_LOG(LOG_VERB, "[%"SCNu64"] There's a message for %s\n", handle->handle_id, plugin_t->get_name());
953
                JANUS_VALIDATE_JSON_OBJECT(root, body_parameters,
954
                        error_code, error_cause, FALSE,
955
                        JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
956
                if(error_code != 0) {
957
                        ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
958
                        goto jsondone;
959
                }
960
                json_t *body = json_object_get(root, "body");
961
                /* Is there an SDP attached? */
962
                json_t *jsep = json_object_get(root, "jsep");
963
                char *jsep_type = NULL;
964
                char *jsep_sdp = NULL, *jsep_sdp_stripped = NULL;
965
                if(jsep != NULL) {
966
                        if(!json_is_object(jsep)) {
967
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_JSON_OBJECT, "Invalid jsep object");
968
                                goto jsondone;
969
                        }
970
                        JANUS_VALIDATE_JSON_OBJECT_FORMAT("JSEP error: missing mandatory element (%s)",
971
                                "JSEP error: invalid element type (%s should be %s)",
972
                                jsep, jsep_parameters, error_code, error_cause, FALSE,
973
                                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
974
                        if(error_code != 0) {
975
                                ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
976
                                goto jsondone;
977
                        }
978
                        json_t *type = json_object_get(jsep, "type");
979
                        jsep_type = g_strdup(json_string_value(type));
980
                        type = NULL;
981
                        gboolean do_trickle = TRUE;
982
                        json_t *jsep_trickle = json_object_get(jsep, "trickle");
983
                        do_trickle = jsep_trickle ? json_is_true(jsep_trickle) : TRUE;
984
                        /* Are we still cleaning up from a previous media session? */
985
                        if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING)) {
986
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Still cleaning up from a previous media session, let's wait a bit...\n", handle->handle_id);
987
                                gint64 waited = 0;
988
                                while(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING)) {
989
                                        g_usleep(100000);
990
                                        waited += 100000;
991
                                        if(waited >= 3*G_USEC_PER_SEC) {
992
                                                JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Waited 3 seconds, that's enough!\n", handle->handle_id);
993
                                                break;
994
                                        }
995
                                }
996
                        }
997
                        /* Check the JSEP type */
998
                        janus_mutex_lock(&handle->mutex);
999
                        int offer = 0;
1000
                        if(!strcasecmp(jsep_type, "offer")) {
1001
                                offer = 1;
1002
                                janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1003
                                janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER);
1004
                                janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER);
1005
                        } else if(!strcasecmp(jsep_type, "answer")) {
1006
                                janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER);
1007
                                offer = 0;
1008
                        } else {
1009
                                /* TODO Handle other message types as well */
1010
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_JSEP_UNKNOWN_TYPE, "JSEP error: unknown message type '%s'", jsep_type);
1011
                                g_free(jsep_type);
1012
                                janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1013
                                janus_mutex_unlock(&handle->mutex);
1014
                                goto jsondone;
1015
                        }
1016
                        json_t *sdp = json_object_get(jsep, "sdp");
1017
                        jsep_sdp = (char *)json_string_value(sdp);
1018
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Remote SDP:\n%s", handle->handle_id, jsep_sdp);
1019
                        /* Is this valid SDP? */
1020
                        char error_str[512];
1021
                        int audio = 0, video = 0, data = 0, bundle = 0, rtcpmux = 0, trickle = 0;
1022
                        janus_sdp *parsed_sdp = janus_sdp_preparse(jsep_sdp, error_str, sizeof(error_str), &audio, &video, &data, &bundle, &rtcpmux, &trickle);
1023
                        trickle = trickle && do_trickle;
1024
                        if(parsed_sdp == NULL) {
1025
                                /* Invalid SDP */
1026
                                ret = janus_process_error_string(request, session_id, transaction_text, JANUS_ERROR_JSEP_INVALID_SDP, error_str);
1027
                                g_free(jsep_type);
1028
                                janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1029
                                janus_mutex_unlock(&handle->mutex);
1030
                                goto jsondone;
1031
                        }
1032
                        /* Notify event handlers */
1033
                        if(janus_events_is_enabled()) {
1034
                                janus_events_notify_handlers(JANUS_EVENT_TYPE_JSEP,
1035
                                        session_id, handle_id, "remote", jsep_type, jsep_sdp);
1036
                        }
1037
                        /* FIXME We're only handling single audio/video lines for now... */
1038
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio %s been negotiated, Video %s been negotiated, SCTP/DataChannels %s been negotiated\n",
1039
                                            handle->handle_id,
1040
                                            audio ? "has" : "has NOT",
1041
                                            video ? "has" : "has NOT",
1042
                                            data ? "have" : "have NOT");
1043
                        if(audio > 1) {
1044
                                JANUS_LOG(LOG_WARN, "[%"SCNu64"] More than one audio line? only going to negotiate one...\n", handle->handle_id);
1045
                        }
1046
                        if(video > 1) {
1047
                                JANUS_LOG(LOG_WARN, "[%"SCNu64"] More than one video line? only going to negotiate one...\n", handle->handle_id);
1048
                        }
1049
                        if(data > 1) {
1050
                                JANUS_LOG(LOG_WARN, "[%"SCNu64"] More than one data line? only going to negotiate one...\n", handle->handle_id);
1051
                        }
1052
#ifndef HAVE_SCTP
1053
                        if(data) {
1054
                                JANUS_LOG(LOG_WARN, "[%"SCNu64"]   -- DataChannels have been negotiated, but support for them has not been compiled...\n", handle->handle_id);
1055
                        }
1056
#endif
1057
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] The browser: %s BUNDLE, %s rtcp-mux, %s doing Trickle ICE\n", handle->handle_id,
1058
                                            bundle  ? "supports" : "does NOT support",
1059
                                            rtcpmux ? "supports" : "does NOT support",
1060
                                            trickle ? "is"       : "is NOT");
1061
                        /* Check if it's a new session, or an update... */
1062
                        if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_READY)
1063
                                        || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)) {
1064
                                /* New session */
1065
                                if(offer) {
1066
                                        /* Setup ICE locally (we received an offer) */
1067
                                        if(janus_ice_setup_local(handle, offer, audio, video, data, bundle, rtcpmux, trickle) < 0) {
1068
                                                JANUS_LOG(LOG_ERR, "Error setting ICE locally\n");
1069
                                                janus_sdp_free(parsed_sdp);
1070
                                                g_free(jsep_type);
1071
                                                janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1072
                                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Error setting ICE locally");
1073
                                                janus_mutex_unlock(&handle->mutex);
1074
                                                goto jsondone;
1075
                                        }
1076
                                } else {
1077
                                        /* Make sure we're waiting for an ANSWER in the first place */
1078
                                        if(!handle->agent) {
1079
                                                JANUS_LOG(LOG_ERR, "Unexpected ANSWER (did we offer?)\n");
1080
                                                janus_sdp_free(parsed_sdp);
1081
                                                g_free(jsep_type);
1082
                                                janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1083
                                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNEXPECTED_ANSWER, "Unexpected ANSWER (did we offer?)");
1084
                                                janus_mutex_unlock(&handle->mutex);
1085
                                                goto jsondone;
1086
                                        }
1087
                                }
1088
                                if(janus_sdp_process(handle, parsed_sdp) < 0) {
1089
                                        JANUS_LOG(LOG_ERR, "Error processing SDP\n");
1090
                                        janus_sdp_free(parsed_sdp);
1091
                                        g_free(jsep_type);
1092
                                        janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1093
                                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNEXPECTED_ANSWER, "Error processing SDP");
1094
                                        janus_mutex_unlock(&handle->mutex);
1095
                                        goto jsondone;
1096
                                }
1097
                                if(!offer) {
1098
                                        /* Set remote candidates now (we received an answer) */
1099
                                        if(bundle) {
1100
                                                janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE);
1101
                                        } else {
1102
                                                janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE);
1103
                                        }
1104
                                        if(rtcpmux) {
1105
                                                janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX);
1106
                                        } else {
1107
                                                janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX);
1108
                                        }
1109
                                        if(trickle) {
1110
                                                janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE);
1111
                                        } else {
1112
                                                janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE);
1113
                                        }
1114
                                        if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
1115
                                                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);
1116
                                                if(audio) {
1117
                                                        /* Get rid of video and data, if present */
1118
                                                        if(handle->streams && handle->video_stream) {
1119
                                                                handle->audio_stream->video_ssrc = handle->video_stream->video_ssrc;
1120
                                                                handle->audio_stream->video_ssrc_peer = handle->video_stream->video_ssrc_peer;
1121
                                                                handle->audio_stream->video_ssrc_peer_rtx = handle->video_stream->video_ssrc_peer_rtx;
1122
                                                                nice_agent_attach_recv(handle->agent, handle->video_stream->stream_id, 1, g_main_loop_get_context (handle->iceloop), NULL, NULL);
1123
                                                                if(!handle->force_rtcp_mux && !janus_ice_is_rtcpmux_forced())
1124
                                                                        nice_agent_attach_recv(handle->agent, handle->video_stream->stream_id, 2, g_main_loop_get_context (handle->iceloop), NULL, NULL);
1125
                                                                nice_agent_remove_stream(handle->agent, handle->video_stream->stream_id);
1126
                                                                janus_ice_stream_free(handle->streams, handle->video_stream);
1127
                                                        }
1128
                                                        handle->video_stream = NULL;
1129
                                                        handle->video_id = 0;
1130
                                                        if(handle->streams && handle->data_stream) {
1131
                                                                nice_agent_attach_recv(handle->agent, handle->data_stream->stream_id, 1, g_main_loop_get_context (handle->iceloop), NULL, NULL);
1132
                                                                nice_agent_remove_stream(handle->agent, handle->data_stream->stream_id);
1133
                                                                janus_ice_stream_free(handle->streams, handle->data_stream);
1134
                                                        }
1135
                                                        handle->data_stream = NULL;
1136
                                                        handle->data_id = 0;
1137
                                                        if(!video) {
1138
                                                                handle->audio_stream->video_ssrc = 0;
1139
                                                                handle->audio_stream->video_ssrc_peer = 0;
1140
                                                                g_free(handle->audio_stream->video_rtcp_ctx);
1141
                                                                handle->audio_stream->video_rtcp_ctx = NULL;
1142
                                                        }
1143
                                                } else if(video) {
1144
                                                        /* Get rid of data, if present */
1145
                                                        if(handle->streams && handle->data_stream) {
1146
                                                                nice_agent_attach_recv(handle->agent, handle->data_stream->stream_id, 1, g_main_loop_get_context (handle->iceloop), NULL, NULL);
1147
                                                                nice_agent_remove_stream(handle->agent, handle->data_stream->stream_id);
1148
                                                                janus_ice_stream_free(handle->streams, handle->data_stream);
1149
                                                        }
1150
                                                        handle->data_stream = NULL;
1151
                                                        handle->data_id = 0;
1152
                                                }
1153
                                        }
1154
                                        if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX) && !handle->force_rtcp_mux && !janus_ice_is_rtcpmux_forced()) {
1155
                                                JANUS_LOG(LOG_HUGE, "[%"SCNu64"]   -- rtcp-mux is supported by the browser, getting rid of RTCP components, if any...\n", handle->handle_id);
1156
                                                if(handle->audio_stream && handle->audio_stream->components != NULL) {
1157
                                                        nice_agent_attach_recv(handle->agent, handle->audio_id, 2, g_main_loop_get_context (handle->iceloop), NULL, NULL);
1158
                                                        /* Free the component */
1159
                                                        janus_ice_component_free(handle->audio_stream->components, handle->audio_stream->rtcp_component);
1160
                                                        handle->audio_stream->rtcp_component = NULL;
1161
                                                        /* Create a dummy candidate and enforce it as the one to use for this now unneeded component */
1162
                                                        NiceCandidate *c = nice_candidate_new(NICE_CANDIDATE_TYPE_HOST);
1163
                                                        c->component_id = 2;
1164
                                                        c->stream_id = handle->audio_stream->stream_id;
1165
#ifndef HAVE_LIBNICE_TCP
1166
                                                        c->transport = NICE_CANDIDATE_TRANSPORT_UDP;
1167
#endif
1168
                                                        strncpy(c->foundation, "1", NICE_CANDIDATE_MAX_FOUNDATION);
1169
                                                        c->priority = 1;
1170
                                                        nice_address_set_from_string(&c->addr, "127.0.0.1");
1171
                                                        nice_address_set_port(&c->addr, janus_ice_get_rtcpmux_blackhole_port());
1172
                                                        c->username = g_strdup(handle->audio_stream->ruser);
1173
                                                        c->password = g_strdup(handle->audio_stream->rpass);
1174
                                                        if(!nice_agent_set_selected_remote_candidate(handle->agent, handle->audio_stream->stream_id, 2, c)) {
1175
                                                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error forcing dummy candidate on RTCP component of stream %d\n", handle->handle_id, handle->audio_stream->stream_id);
1176
                                                                nice_candidate_free(c);
1177
                                                        }
1178
                                                }
1179
                                                if(handle->video_stream && handle->video_stream->components != NULL) {
1180
                                                        nice_agent_attach_recv(handle->agent, handle->video_id, 2, g_main_loop_get_context (handle->iceloop), NULL, NULL);
1181
                                                        /* Free the component */
1182
                                                        janus_ice_component_free(handle->video_stream->components, handle->video_stream->rtcp_component);
1183
                                                        handle->video_stream->rtcp_component = NULL;
1184
                                                        /* Create a dummy candidate and enforce it as the one to use for this now unneeded component */
1185
                                                        NiceCandidate *c = nice_candidate_new(NICE_CANDIDATE_TYPE_HOST);
1186
                                                        c->component_id = 2;
1187
                                                        c->stream_id = handle->video_stream->stream_id;
1188
#ifndef HAVE_LIBNICE_TCP
1189
                                                        c->transport = NICE_CANDIDATE_TRANSPORT_UDP;
1190
#endif
1191
                                                        strncpy(c->foundation, "1", NICE_CANDIDATE_MAX_FOUNDATION);
1192
                                                        c->priority = 1;
1193
                                                        nice_address_set_from_string(&c->addr, "127.0.0.1");
1194
                                                        nice_address_set_port(&c->addr, janus_ice_get_rtcpmux_blackhole_port());
1195
                                                        c->username = g_strdup(handle->video_stream->ruser);
1196
                                                        c->password = g_strdup(handle->video_stream->rpass);
1197
                                                        if(!nice_agent_set_selected_remote_candidate(handle->agent, handle->video_stream->stream_id, 2, c)) {
1198
                                                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error forcing dummy candidate on RTCP component of stream %d\n", handle->handle_id, handle->video_stream->stream_id);
1199
                                                                nice_candidate_free(c);
1200
                                                        }
1201
                                                }
1202
                                        }
1203
                                        /* FIXME Any disabled m-line? */
1204
                                        if(strstr(jsep_sdp, "m=audio 0")) {
1205
                                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio disabled via SDP\n", handle->handle_id);
1206
                                                if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)
1207
                                                                || (!video && !data)) {
1208
                                                        JANUS_LOG(LOG_HUGE, "  -- Marking audio stream as disabled\n");
1209
                                                        janus_ice_stream *stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->audio_id));
1210
                                                        if(stream)
1211
                                                                stream->disabled = TRUE;
1212
                                                }
1213
                                        }
1214
                                        if(strstr(jsep_sdp, "m=video 0")) {
1215
                                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Video disabled via SDP\n", handle->handle_id);
1216
                                                if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)
1217
                                                                || (!audio && !data)) {
1218
                                                        JANUS_LOG(LOG_HUGE, "  -- Marking video stream as disabled\n");
1219
                                                        janus_ice_stream *stream = NULL;
1220
                                                        if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
1221
                                                                stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->video_id));
1222
                                                        } else {
1223
                                                                gint id = handle->audio_id > 0 ? handle->audio_id : handle->video_id;
1224
                                                                stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(id));
1225
                                                        }
1226
                                                        if(stream)
1227
                                                                stream->disabled = TRUE;
1228
                                                }
1229
                                        }
1230
                                        if(strstr(jsep_sdp, "m=application 0 DTLS/SCTP")) {
1231
                                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Data Channel disabled via SDP\n", handle->handle_id);
1232
                                                if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)
1233
                                                                || (!audio && !video)) {
1234
                                                        JANUS_LOG(LOG_HUGE, "  -- Marking data channel stream as disabled\n");
1235
                                                        janus_ice_stream *stream = NULL;
1236
                                                        if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
1237
                                                                stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->data_id));
1238
                                                        } else {
1239
                                                                gint id = handle->audio_id > 0 ? handle->audio_id : (handle->video_id > 0 ? handle->video_id : handle->data_id);
1240
                                                                stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(id));
1241
                                                        }
1242
                                                        if(stream)
1243
                                                                stream->disabled = TRUE;
1244
                                                }
1245
                                        }
1246
                                        /* We got our answer */
1247
                                        janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1248
                                        /* Any pending trickles? */
1249
                                        if(handle->pending_trickles) {
1250
                                                JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Processing %d pending trickle candidates\n", handle->handle_id, g_list_length(handle->pending_trickles));
1251
                                                GList *temp = NULL;
1252
                                                while(handle->pending_trickles) {
1253
                                                        temp = g_list_first(handle->pending_trickles);
1254
                                                        handle->pending_trickles = g_list_remove_link(handle->pending_trickles, temp);
1255
                                                        janus_ice_trickle *trickle = (janus_ice_trickle *)temp->data;
1256
                                                        g_list_free(temp);
1257
                                                        if(trickle == NULL)
1258
                                                                continue;
1259
                                                        if((janus_get_monotonic_time() - trickle->received) > 45*G_USEC_PER_SEC) {
1260
                                                                /* FIXME Candidate is too old, discard it */
1261
                                                                janus_ice_trickle_destroy(trickle);
1262
                                                                /* FIXME We should report that */
1263
                                                                continue;
1264
                                                        }
1265
                                                        json_t *candidate = trickle->candidate;
1266
                                                        if(candidate == NULL) {
1267
                                                                janus_ice_trickle_destroy(trickle);
1268
                                                                continue;
1269
                                                        }
1270
                                                        if(json_is_object(candidate)) {
1271
                                                                /* We got a single candidate */
1272
                                                                int error = 0;
1273
                                                                const char *error_string = NULL;
1274
                                                                if((error = janus_ice_trickle_parse(handle, candidate, &error_string)) != 0) {
1275
                                                                        /* FIXME We should report the error parsing the trickle candidate */
1276
                                                                }
1277
                                                        } else if(json_is_array(candidate)) {
1278
                                                                /* We got multiple candidates in an array */
1279
                                                                JANUS_LOG(LOG_VERB, "Got multiple candidates (%zu)\n", json_array_size(candidate));
1280
                                                                if(json_array_size(candidate) > 0) {
1281
                                                                        /* Handle remote candidates */
1282
                                                                        size_t i = 0;
1283
                                                                        for(i=0; i<json_array_size(candidate); i++) {
1284
                                                                                json_t *c = json_array_get(candidate, i);
1285
                                                                                /* FIXME We don't care if any trickle fails to parse */
1286
                                                                                janus_ice_trickle_parse(handle, c, NULL);
1287
                                                                        }
1288
                                                                }
1289
                                                        }
1290
                                                        /* Done, free candidate */
1291
                                                        janus_ice_trickle_destroy(trickle);
1292
                                                }
1293
                                        }
1294
                                        /* This was an answer, check if it's time to start ICE */
1295
                                        if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE) &&
1296
                                                        !janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALL_TRICKLES)) {
1297
                                                JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- ICE Trickling is supported by the browser, waiting for remote candidates...\n", handle->handle_id);
1298
                                                janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_START);
1299
                                        } else {
1300
                                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Done! Sending connectivity checks...\n", handle->handle_id);
1301
                                                if(handle->audio_id > 0) {
1302
                                                        janus_ice_setup_remote_candidates(handle, handle->audio_id, 1);
1303
                                                        if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX))        /* http://tools.ietf.org/html/rfc5761#section-5.1.3 */
1304
                                                                janus_ice_setup_remote_candidates(handle, handle->audio_id, 2);
1305
                                                }
1306
                                                if(handle->video_id > 0) {
1307
                                                        janus_ice_setup_remote_candidates(handle, handle->video_id, 1);
1308
                                                        if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX))        /* http://tools.ietf.org/html/rfc5761#section-5.1.3 */
1309
                                                                janus_ice_setup_remote_candidates(handle, handle->video_id, 2);
1310
                                                }
1311
                                                if(handle->data_id > 0) {
1312
                                                        janus_ice_setup_remote_candidates(handle, handle->data_id, 1);
1313
                                                }
1314
                                        }
1315
                                }
1316
                        } else {
1317
                                /* TODO Actually handle session updates: for now we ignore them, and just relay them to plugins */
1318
                                JANUS_LOG(LOG_WARN, "[%"SCNu64"] Ignoring negotiation update, we don't support them yet...\n", handle->handle_id);
1319
                        }
1320
                        handle->remote_sdp = g_strdup(jsep_sdp);
1321
                        janus_mutex_unlock(&handle->mutex);
1322
                        /* Anonymize SDP */
1323
                        if(janus_sdp_anonymize(parsed_sdp) < 0) {
1324
                                /* Invalid SDP */
1325
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_JSEP_INVALID_SDP, "JSEP error: invalid SDP");
1326
                                janus_sdp_free(parsed_sdp);
1327
                                g_free(jsep_type);
1328
                                janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1329
                                goto jsondone;
1330
                        }
1331
                        jsep_sdp_stripped = janus_sdp_write(parsed_sdp);
1332
                        janus_sdp_free(parsed_sdp);
1333
                        sdp = NULL;
1334
                        janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1335
                }
1336

    
1337
                /* Make sure the app handle is still valid */
1338
                if(handle->app == NULL || handle->app_handle == NULL || !janus_plugin_session_is_alive(handle->app_handle)) {
1339
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "No plugin to handle this message");
1340
                        g_free(jsep_type);
1341
                        g_free(jsep_sdp_stripped);
1342
                        janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1343
                        goto jsondone;
1344
                }
1345

    
1346
                /* Send the message to the plugin (which must eventually free transaction_text and unref the two objects, body and jsep) */
1347
                json_incref(body);
1348
                janus_plugin_result *result = plugin_t->handle_message(handle->app_handle,
1349
                        g_strdup((char *)transaction_text), body,
1350
                        jsep_sdp_stripped ? json_pack("{ssss}", "type", jsep_type, "sdp", jsep_sdp_stripped) : NULL);
1351
                g_free(jsep_type);
1352
                g_free(jsep_sdp_stripped);
1353
                if(result == NULL) {
1354
                        /* Something went horribly wrong! */
1355
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "Plugin didn't give a result");
1356
                        goto jsondone;
1357
                }
1358
                if(result->type == JANUS_PLUGIN_OK) {
1359
                        /* The plugin gave a result already (synchronous request/response) */
1360
                        if(result->content == NULL || !json_is_object(result->content)) {
1361
                                /* Missing content, or not a JSON object */
1362
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE,
1363
                                        result->content == NULL ?
1364
                                                "Plugin didn't provide any content for this synchronous response" :
1365
                                                "Plugin returned an invalid JSON response");
1366
                                janus_plugin_result_destroy(result);
1367
                                goto jsondone;
1368
                        }
1369
                        /* Reference the content, as destroying the result instance will decref it */
1370
                        json_incref(result->content);
1371
                        /* Prepare JSON response */
1372
                        json_t *reply = json_object();
1373
                        json_object_set_new(reply, "janus", json_string("success"));
1374
                        json_object_set_new(reply, "session_id", json_integer(session->session_id));
1375
                        json_object_set_new(reply, "sender", json_integer(handle->handle_id));
1376
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1377
                        json_t *plugin_data = json_object();
1378
                        json_object_set_new(plugin_data, "plugin", json_string(plugin_t->get_package()));
1379
                        json_object_set_new(plugin_data, "data", result->content);
1380
                        json_object_set_new(reply, "plugindata", plugin_data);
1381
                        /* Send the success reply */
1382
                        ret = janus_process_success(request, reply);
1383
                } else if(result->type == JANUS_PLUGIN_OK_WAIT) {
1384
                        /* The plugin received the request but didn't process it yet, send an ack (asynchronous notifications may follow) */
1385
                        json_t *reply = json_object();
1386
                        json_object_set_new(reply, "janus", json_string("ack"));
1387
                        json_object_set_new(reply, "session_id", json_integer(session_id));
1388
                        if(result->text)
1389
                                json_object_set_new(reply, "hint", json_string(result->text));
1390
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1391
                        /* Send the success reply */
1392
                        ret = janus_process_success(request, reply);
1393
                } else {
1394
                        /* Something went horribly wrong! */
1395
                        ret = janus_process_error_string(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE,
1396
                                (char *)(result->text ? result->text : "Plugin returned a severe (unknown) error"));
1397
                        janus_plugin_result_destroy(result);
1398
                        goto jsondone;
1399
                }
1400
                janus_plugin_result_destroy(result);
1401
        } else if(!strcasecmp(message_text, "trickle")) {
1402
                if(handle == NULL) {
1403
                        /* Trickle is an handle-level command */
1404
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
1405
                        goto jsondone;
1406
                }
1407
                if(handle->app == NULL || handle->app_handle == NULL || !janus_plugin_session_is_alive(handle->app_handle)) {
1408
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "No plugin to handle this trickle candidate");
1409
                        goto jsondone;
1410
                }
1411
                json_t *candidate = json_object_get(root, "candidate");
1412
                json_t *candidates = json_object_get(root, "candidates");
1413
                if(candidate == NULL && candidates == NULL) {
1414
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "Missing mandatory element (candidate|candidates)");
1415
                        goto jsondone;
1416
                }
1417
                if(candidate != NULL && candidates != NULL) {
1418
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_JSON, "Can't have both candidate and candidates");
1419
                        goto jsondone;
1420
                }
1421
                janus_mutex_lock(&handle->mutex);
1422
                if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE)) {
1423
                        /* It looks like this peer supports Trickle, after all */
1424
                        JANUS_LOG(LOG_VERB, "Handle %"SCNu64" supports trickle even if it didn't negotiate it...\n", handle->handle_id);
1425
                        janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE);
1426
                }
1427
                /* Is there any stream ready? this trickle may get here before the SDP it relates to */
1428
                if(handle->audio_stream == NULL && handle->video_stream == NULL && handle->data_stream == NULL) {
1429
                        JANUS_LOG(LOG_WARN, "[%"SCNu64"] No stream, queueing this trickle as it got here before the SDP...\n", handle->handle_id);
1430
                        /* Enqueue this trickle candidate(s), we'll process this later */
1431
                        janus_ice_trickle *early_trickle = janus_ice_trickle_new(handle, transaction_text, candidate ? candidate : candidates);
1432
                        handle->pending_trickles = g_list_append(handle->pending_trickles, early_trickle);
1433
                        /* Send the ack right away, an event will tell the application if the candidate(s) failed */
1434
                        goto trickledone;
1435
                }
1436
                /* Is the ICE stack ready already? */
1437
                if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER) ||
1438
                                !janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER) ||
1439
                                !janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER)) {
1440
                        const char *cause = NULL;
1441
                        if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER))
1442
                                cause = "processing the offer";
1443
                        else if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER))
1444
                                cause = "waiting for the answer";
1445
                        else if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER))
1446
                                cause = "waiting for the offer";
1447
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Still %s, queueing this trickle to wait until we're done there...\n",
1448
                                handle->handle_id, cause);
1449
                        /* Enqueue this trickle candidate(s), we'll process this later */
1450
                        janus_ice_trickle *early_trickle = janus_ice_trickle_new(handle, transaction_text, candidate ? candidate : candidates);
1451
                        handle->pending_trickles = g_list_append(handle->pending_trickles, early_trickle);
1452
                        /* Send the ack right away, an event will tell the application if the candidate(s) failed */
1453
                        goto trickledone;
1454
                }
1455
                if(candidate != NULL) {
1456
                        /* We got a single candidate */
1457
                        int error = 0;
1458
                        const char *error_string = NULL;
1459
                        if((error = janus_ice_trickle_parse(handle, candidate, &error_string)) != 0) {
1460
                                ret = janus_process_error(request, session_id, transaction_text, error, "%s", error_string);
1461
                                janus_mutex_unlock(&handle->mutex);
1462
                                goto jsondone;
1463
                        }
1464
                } else {
1465
                        /* We got multiple candidates in an array */
1466
                        if(!json_is_array(candidates)) {
1467
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "candidates is not an array");
1468
                                janus_mutex_unlock(&handle->mutex);
1469
                                goto jsondone;
1470
                        }
1471
                        JANUS_LOG(LOG_VERB, "Got multiple candidates (%zu)\n", json_array_size(candidates));
1472
                        if(json_array_size(candidates) > 0) {
1473
                                /* Handle remote candidates */
1474
                                size_t i = 0;
1475
                                for(i=0; i<json_array_size(candidates); i++) {
1476
                                        json_t *c = json_array_get(candidates, i);
1477
                                        /* FIXME We don't care if any trickle fails to parse */
1478
                                        janus_ice_trickle_parse(handle, c, NULL);
1479
                                }
1480
                        }
1481
                }
1482

    
1483
trickledone:
1484
                janus_mutex_unlock(&handle->mutex);
1485
                /* We reply right away, not to block the web server... */
1486
                json_t *reply = json_object();
1487
                json_object_set_new(reply, "janus", json_string("ack"));
1488
                json_object_set_new(reply, "session_id", json_integer(session_id));
1489
                json_object_set_new(reply, "transaction", json_string(transaction_text));
1490
                /* Send the success reply */
1491
                ret = janus_process_success(request, reply);
1492
        } else {
1493
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN_REQUEST, "Unknown request '%s'", message_text);
1494
        }
1495

    
1496
jsondone:
1497
        /* Done processing */
1498
        return ret;
1499
}
1500

    
1501
/* Admin/monitor WebServer requests handler */
1502
int janus_process_incoming_admin_request(janus_request *request) {
1503
        int ret = -1;
1504
        int error_code = 0;
1505
        char error_cause[100];
1506
        if(request == NULL) {
1507
                JANUS_LOG(LOG_ERR, "Missing request or payload to process, giving up...\n");
1508
                return ret;
1509
        }
1510
        json_t *root = request->message;
1511
        /* Ok, let's start with the ids */
1512
        guint64 session_id = 0, handle_id = 0;
1513
        json_t *s = json_object_get(root, "session_id");
1514
        if(s && json_is_integer(s))
1515
                session_id = json_integer_value(s);
1516
        json_t *h = json_object_get(root, "handle_id");
1517
        if(h && json_is_integer(h))
1518
                handle_id = json_integer_value(h);
1519

    
1520
        /* Get transaction and message request */
1521
        JANUS_VALIDATE_JSON_OBJECT(root, admin_parameters,
1522
                error_code, error_cause, FALSE,
1523
                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1524
        if(error_code != 0) {
1525
                ret = janus_process_error_string(request, session_id, NULL, error_code, error_cause);
1526
                goto jsondone;
1527
        }
1528
        json_t *transaction = json_object_get(root, "transaction");
1529
        const gchar *transaction_text = json_string_value(transaction);
1530
        json_t *message = json_object_get(root, "janus");
1531
        if(!message) {
1532
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "Missing mandatory element (janus)");
1533
                goto jsondone;
1534
        }
1535
        if(!json_is_string(message)) {
1536
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (janus should be a string)");
1537
                goto jsondone;
1538
        }
1539
        const gchar *message_text = json_string_value(message);
1540

    
1541
        if(session_id == 0 && handle_id == 0) {
1542
                /* Can only be a 'Get all sessions' or some general setting manipulation request */
1543
                if(!strcasecmp(message_text, "info")) {
1544
                        /* The generic info request */
1545
                        ret = janus_process_success(request, janus_info(transaction_text));
1546
                        goto jsondone;
1547
                }
1548
                if(admin_api_secret != NULL) {
1549
                        /* There's an admin/monitor secret, check that the client provided it */
1550
                        json_t *secret = json_object_get(root, "admin_secret");
1551
                        if(!secret || !json_is_string(secret) || !janus_strcmp_const_time(json_string_value(secret), admin_api_secret)) {
1552
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED, NULL);
1553
                                goto jsondone;
1554
                        }
1555
                }
1556
                if(!strcasecmp(message_text, "get_status")) {
1557
                        /* Return some info on the settings (mostly debug-related, at the moment) */
1558
                        json_t *reply = json_object();
1559
                        json_object_set_new(reply, "janus", json_string("success"));
1560
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1561
                        json_t *status = json_object();
1562
                        json_object_set_new(status, "token_auth", janus_auth_is_enabled() ? json_true() : json_false());
1563
                        json_object_set_new(status, "session_timeout", json_integer(session_timeout));
1564
                        json_object_set_new(status, "log_level", json_integer(janus_log_level));
1565
                        json_object_set_new(status, "log_timestamps", janus_log_timestamps ? json_true() : json_false());
1566
                        json_object_set_new(status, "log_colors", janus_log_colors ? json_true() : json_false());
1567
                        json_object_set_new(status, "locking_debug", lock_debug ? json_true() : json_false());
1568
                        json_object_set_new(status, "libnice_debug", janus_ice_is_ice_debugging_enabled() ? json_true() : json_false());
1569
                        json_object_set_new(status, "max_nack_queue", json_integer(janus_get_max_nack_queue()));
1570
                        json_object_set_new(status, "no_media_timer", json_integer(janus_get_no_media_timer()));
1571
                        json_object_set_new(reply, "status", status);
1572
                        /* Send the success reply */
1573
                        ret = janus_process_success(request, reply);
1574
                        goto jsondone;
1575
                } else if(!strcasecmp(message_text, "set_session_timeout")) {
1576
                        /* Change the session timeout value */
1577
                        JANUS_VALIDATE_JSON_OBJECT(root, timeout_parameters,
1578
                                error_code, error_cause, FALSE,
1579
                                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1580
                        if(error_code != 0) {
1581
                                ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1582
                                goto jsondone;
1583
                        }
1584
                        json_t *timeout = json_object_get(root, "timeout");
1585
                        int timeout_num = json_integer_value(timeout);
1586
                        if(timeout_num < 0) {
1587
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (timeout should be a positive integer)");
1588
                                goto jsondone;
1589
                        }
1590
                        session_timeout = timeout_num;
1591
                        /* Prepare JSON reply */
1592
                        json_t *reply = json_object();
1593
                        json_object_set_new(reply, "janus", json_string("success"));
1594
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1595
                        json_object_set_new(reply, "timeout", json_integer(session_timeout));
1596
                        /* Send the success reply */
1597
                        ret = janus_process_success(request, reply);
1598
                        goto jsondone;
1599
                } else if(!strcasecmp(message_text, "set_log_level")) {
1600
                        /* Change the debug logging level */
1601
                        JANUS_VALIDATE_JSON_OBJECT(root, level_parameters,
1602
                                error_code, error_cause, FALSE,
1603
                                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1604
                        if(error_code != 0) {
1605
                                ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1606
                                goto jsondone;
1607
                        }
1608
                        json_t *level = json_object_get(root, "level");
1609
                        int level_num = json_integer_value(level);
1610
                        if(level_num < LOG_NONE || level_num > LOG_MAX) {
1611
                                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);
1612
                                goto jsondone;
1613
                        }
1614
                        janus_log_level = level_num;
1615
                        /* Prepare JSON reply */
1616
                        json_t *reply = json_object();
1617
                        json_object_set_new(reply, "janus", json_string("success"));
1618
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1619
                        json_object_set_new(reply, "level", json_integer(janus_log_level));
1620
                        /* Send the success reply */
1621
                        ret = janus_process_success(request, reply);
1622
                        goto jsondone;
1623
                } else if(!strcasecmp(message_text, "set_locking_debug")) {
1624
                        /* Enable/disable the locking debug (would show a message on the console for every lock attempt) */
1625
                        JANUS_VALIDATE_JSON_OBJECT(root, debug_parameters,
1626
                                error_code, error_cause, FALSE,
1627
                                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1628
                        if(error_code != 0) {
1629
                                ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1630
                                goto jsondone;
1631
                        }
1632
                        json_t *debug = json_object_get(root, "debug");
1633
                        lock_debug = json_is_true(debug);
1634
                        /* Prepare JSON reply */
1635
                        json_t *reply = json_object();
1636
                        json_object_set_new(reply, "janus", json_string("success"));
1637
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1638
                        json_object_set_new(reply, "locking_debug", lock_debug ? json_true() : json_false());
1639
                        /* Send the success reply */
1640
                        ret = janus_process_success(request, reply);
1641
                        goto jsondone;
1642
                } else if(!strcasecmp(message_text, "set_log_timestamps")) {
1643
                        /* Enable/disable the log timestamps */
1644
                        JANUS_VALIDATE_JSON_OBJECT(root, timestamps_parameters,
1645
                                error_code, error_cause, FALSE,
1646
                                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1647
                        if(error_code != 0) {
1648
                                ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1649
                                goto jsondone;
1650
                        }
1651
                        json_t *timestamps = json_object_get(root, "timestamps");
1652
                        janus_log_timestamps = json_is_true(timestamps);
1653
                        /* Prepare JSON reply */
1654
                        json_t *reply = json_object();
1655
                        json_object_set_new(reply, "janus", json_string("success"));
1656
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1657
                        json_object_set_new(reply, "log_timestamps", janus_log_timestamps ? json_true() : json_false());
1658
                        /* Send the success reply */
1659
                        ret = janus_process_success(request, reply);
1660
                        goto jsondone;
1661
                } else if(!strcasecmp(message_text, "set_log_colors")) {
1662
                        /* Enable/disable the log colors */
1663
                        JANUS_VALIDATE_JSON_OBJECT(root, colors_parameters,
1664
                                error_code, error_cause, FALSE,
1665
                                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1666
                        if(error_code != 0) {
1667
                                ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1668
                                goto jsondone;
1669
                        }
1670
                        json_t *colors = json_object_get(root, "colors");
1671
                        janus_log_colors = json_is_true(colors);
1672
                        /* Prepare JSON reply */
1673
                        json_t *reply = json_object();
1674
                        json_object_set_new(reply, "janus", json_string("success"));
1675
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1676
                        json_object_set_new(reply, "log_colors", janus_log_colors ? json_true() : json_false());
1677
                        /* Send the success reply */
1678
                        ret = janus_process_success(request, reply);
1679
                        goto jsondone;
1680
                } else if(!strcasecmp(message_text, "set_libnice_debug")) {
1681
                        /* Enable/disable the libnice debugging (http://nice.freedesktop.org/libnice/libnice-Debug-messages.html) */
1682
                        JANUS_VALIDATE_JSON_OBJECT(root, debug_parameters,
1683
                                error_code, error_cause, FALSE,
1684
                                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1685
                        if(error_code != 0) {
1686
                                ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1687
                                goto jsondone;
1688
                        }
1689
                        json_t *debug = json_object_get(root, "debug");
1690
                        if(json_is_true(debug)) {
1691
                                janus_ice_debugging_enable();
1692
                        } else {
1693
                                janus_ice_debugging_disable();
1694
                        }
1695
                        /* Prepare JSON reply */
1696
                        json_t *reply = json_object();
1697
                        json_object_set_new(reply, "janus", json_string("success"));
1698
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1699
                        json_object_set_new(reply, "libnice_debug", janus_ice_is_ice_debugging_enabled() ? json_true() : json_false());
1700
                        /* Send the success reply */
1701
                        ret = janus_process_success(request, reply);
1702
                        goto jsondone;
1703
                } else if(!strcasecmp(message_text, "set_max_nack_queue")) {
1704
                        /* Change the current value for the max NACK queue */
1705
                        JANUS_VALIDATE_JSON_OBJECT(root, mnq_parameters,
1706
                                error_code, error_cause, FALSE,
1707
                                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1708
                        if(error_code != 0) {
1709
                                ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1710
                                goto jsondone;
1711
                        }
1712
                        json_t *mnq = json_object_get(root, "max_nack_queue");
1713
                        int mnq_num = json_integer_value(mnq);
1714
                        if(mnq_num < 0 || (mnq_num > 0 && mnq_num < 200)) {
1715
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (max_nack_queue, if provided, should be greater than 200)");
1716
                                goto jsondone;
1717
                        }
1718
                        janus_set_max_nack_queue(mnq_num);
1719
                        /* Prepare JSON reply */
1720
                        json_t *reply = json_object();
1721
                        json_object_set_new(reply, "janus", json_string("success"));
1722
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1723
                        json_object_set_new(reply, "max_nack_queue", json_integer(janus_get_max_nack_queue()));
1724
                        /* Send the success reply */
1725
                        ret = janus_process_success(request, reply);
1726
                        goto jsondone;
1727
                } else if(!strcasecmp(message_text, "set_no_media_timer")) {
1728
                        /* Change the current value for the no-media timer */
1729
                        JANUS_VALIDATE_JSON_OBJECT(root, nmt_parameters,
1730
                                error_code, error_cause, FALSE,
1731
                                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1732
                        if(error_code != 0) {
1733
                                ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1734
                                goto jsondone;
1735
                        }
1736
                        json_t *nmt = json_object_get(root, "no_media_timer");
1737
                        int nmt_num = json_integer_value(nmt);
1738
                        janus_set_no_media_timer(nmt_num);
1739
                        /* Prepare JSON reply */
1740
                        json_t *reply = json_object();
1741
                        json_object_set_new(reply, "janus", json_string("success"));
1742
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1743
                        json_object_set_new(reply, "no_media_timer", json_integer(janus_get_no_media_timer()));
1744
                        /* Send the success reply */
1745
                        ret = janus_process_success(request, reply);
1746
                        goto jsondone;
1747
                } else if(!strcasecmp(message_text, "list_sessions")) {
1748
                        /* List sessions */
1749
                        session_id = 0;
1750
                        json_t *list = json_array();
1751
                        if(sessions != NULL && g_hash_table_size(sessions) > 0) {
1752
                                janus_mutex_lock(&sessions_mutex);
1753
                                GHashTableIter iter;
1754
                                gpointer value;
1755
                                g_hash_table_iter_init(&iter, sessions);
1756
                                while (g_hash_table_iter_next(&iter, NULL, &value)) {
1757
                                        janus_session *session = value;
1758
                                        if(session == NULL) {
1759
                                                continue;
1760
                                        }
1761
                                        json_array_append_new(list, json_integer(session->session_id));
1762
                                }
1763
                                janus_mutex_unlock(&sessions_mutex);
1764
                        }
1765
                        /* Prepare JSON reply */
1766
                        json_t *reply = json_object();
1767
                        json_object_set_new(reply, "janus", json_string("success"));
1768
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1769
                        json_object_set_new(reply, "sessions", list);
1770
                        /* Send the success reply */
1771
                        ret = janus_process_success(request, reply);
1772
                        goto jsondone;
1773
                } else if(!strcasecmp(message_text, "add_token")) {
1774
                        /* Add a token valid for authentication */
1775
                        if(!janus_auth_is_enabled()) {
1776
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Token based authentication disabled");
1777
                                goto jsondone;
1778
                        }
1779
                        JANUS_VALIDATE_JSON_OBJECT(root, add_token_parameters,
1780
                                error_code, error_cause, FALSE,
1781
                                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1782
                        if(error_code != 0) {
1783
                                ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1784
                                goto jsondone;
1785
                        }
1786
                        json_t *token = json_object_get(root, "token");
1787
                        const char *token_value = json_string_value(token);
1788
                        /* Any plugin this token should be limited to? */
1789
                        json_t *allowed = json_object_get(root, "plugins");
1790
                        /* First of all, add the new token */
1791
                        if(!janus_auth_add_token(token_value)) {
1792
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Error adding token");
1793
                                goto jsondone;
1794
                        }
1795
                        /* Then take care of the plugins access limitations, if any */
1796
                        if(allowed && json_array_size(allowed) > 0) {
1797
                                /* Specify which plugins this token has access to */
1798
                                size_t i = 0;
1799
                                for(i=0; i<json_array_size(allowed); i++) {
1800
                                        json_t *p = json_array_get(allowed, i);
1801
                                        if(!p || !json_is_string(p)) {
1802
                                                /* FIXME Should we fail here? */
1803
                                                JANUS_LOG(LOG_WARN, "Invalid plugin passed to the new token request, skipping...\n");
1804
                                                continue;
1805
                                        }
1806
                                        const gchar *plugin_text = json_string_value(p);
1807
                                        janus_plugin *plugin_t = janus_plugin_find(plugin_text);
1808
                                        if(plugin_t == NULL) {
1809
                                                /* FIXME Should we fail here? */
1810
                                                JANUS_LOG(LOG_WARN, "No such plugin '%s' passed to the new token request, skipping...\n", plugin_text);
1811
                                                continue;
1812
                                        }
1813
                                        if(!janus_auth_allow_plugin(token_value, plugin_t)) {
1814
                                                JANUS_LOG(LOG_WARN, "Error allowing access to '%s' to the new token, bad things may happen...\n", plugin_text);
1815
                                        }
1816
                                }
1817
                        } else {
1818
                                /* No plugin limitation specified, allow all plugins */
1819
                                if(plugins && g_hash_table_size(plugins) > 0) {
1820
                                        GHashTableIter iter;
1821
                                        gpointer value;
1822
                                        g_hash_table_iter_init(&iter, plugins);
1823
                                        while (g_hash_table_iter_next(&iter, NULL, &value)) {
1824
                                                janus_plugin *plugin_t = value;
1825
                                                if(plugin_t == NULL)
1826
                                                        continue;
1827
                                                if(!janus_auth_allow_plugin(token_value, plugin_t)) {
1828
                                                        JANUS_LOG(LOG_WARN, "Error allowing access to '%s' to the new token, bad things may happen...\n", plugin_t->get_package());
1829
                                                }
1830
                                        }
1831
                                }
1832
                        }
1833
                        /* Get the list of plugins this new token can access */
1834
                        json_t *plugins_list = json_array();
1835
                        GList *plugins = janus_auth_list_plugins(token_value);
1836
                        if(plugins != NULL) {
1837
                                GList *tmp = plugins;
1838
                                while(tmp) {
1839
                                        janus_plugin *p = (janus_plugin *)tmp->data;
1840
                                        if(p != NULL)
1841
                                                json_array_append_new(plugins_list, json_string(p->get_package()));
1842
                                        tmp = tmp->next;
1843
                                }
1844
                                g_list_free(plugins);
1845
                                plugins = NULL;
1846
                        }
1847
                        /* Prepare JSON reply */
1848
                        json_t *reply = json_object();
1849
                        json_object_set_new(reply, "janus", json_string("success"));
1850
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1851
                        json_t *data = json_object();
1852
                        json_object_set_new(data, "plugins", plugins_list);
1853
                        json_object_set_new(reply, "data", data);
1854
                        /* Send the success reply */
1855
                        ret = janus_process_success(request, reply);
1856
                        goto jsondone;
1857
                } else if(!strcasecmp(message_text, "list_tokens")) {
1858
                        /* List all the valid tokens */
1859
                        if(!janus_auth_is_enabled()) {
1860
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Token based authentication disabled");
1861
                                goto jsondone;
1862
                        }
1863
                        json_t *tokens_list = json_array();
1864
                        GList *list = janus_auth_list_tokens();
1865
                        if(list != NULL) {
1866
                                GList *tmp = list;
1867
                                while(tmp) {
1868
                                        char *token = (char *)tmp->data;
1869
                                        if(token != NULL) {
1870
                                                GList *plugins = janus_auth_list_plugins(token);
1871
                                                if(plugins != NULL) {
1872
                                                        json_t *t = json_object();
1873
                                                        json_object_set_new(t, "token", json_string(token));
1874
                                                        json_t *plugins_list = json_array();
1875
                                                        GList *tmp2 = plugins;
1876
                                                        while(tmp2) {
1877
                                                                janus_plugin *p = (janus_plugin *)tmp2->data;
1878
                                                                if(p != NULL)
1879
                                                                        json_array_append_new(plugins_list, json_string(p->get_package()));
1880
                                                                tmp2 = tmp2->next;
1881
                                                        }
1882
                                                        g_list_free(plugins);
1883
                                                        plugins = NULL;
1884
                                                        json_object_set_new(t, "allowed_plugins", plugins_list);
1885
                                                        json_array_append_new(tokens_list, t);
1886
                                                }
1887
                                                tmp->data = NULL;
1888
                                                g_free(token);
1889
                                        }
1890
                                        tmp = tmp->next;
1891
                                }
1892
                                g_list_free(list);
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, "tokens", tokens_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, "allow_token")) {
1905
                        /* Allow a valid token valid to access a plugin */
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
                        JANUS_VALIDATE_JSON_OBJECT(root, allow_token_parameters,
1911
                                error_code, error_cause, FALSE,
1912
                                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1913
                        if(error_code != 0) {
1914
                                ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1915
                                goto jsondone;
1916
                        }
1917
                        json_t *token = json_object_get(root, "token");
1918
                        const char *token_value = json_string_value(token);
1919
                        /* Check if the token is valid, first */
1920
                        if(!janus_auth_check_token(token_value)) {
1921
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_TOKEN_NOT_FOUND, "Token %s not found", token_value);
1922
                                goto jsondone;
1923
                        }
1924
                        /* Any plugin this token should be limited to? */
1925
                        json_t *allowed = json_object_get(root, "plugins");
1926
                        /* Check the list first */
1927
                        size_t i = 0;
1928
                        gboolean ok = TRUE;
1929
                        for(i=0; i<json_array_size(allowed); i++) {
1930
                                json_t *p = json_array_get(allowed, i);
1931
                                if(!p || !json_is_string(p)) {
1932
                                        /* FIXME Should we fail here? */
1933
                                        JANUS_LOG(LOG_ERR, "Invalid plugin passed to the new token request...\n");
1934
                                        ok = FALSE;
1935
                                        break;
1936
                                }
1937
                                const gchar *plugin_text = json_string_value(p);
1938
                                janus_plugin *plugin_t = janus_plugin_find(plugin_text);
1939
                                if(plugin_t == NULL) {
1940
                                        /* FIXME Should we fail here? */
1941
                                        JANUS_LOG(LOG_ERR, "No such plugin '%s' passed to the new token request...\n", plugin_text);
1942
                                        ok = FALSE;
1943
                                        break;
1944
                                }
1945
                        }
1946
                        if(!ok) {
1947
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (some of the provided plugins are invalid)");
1948
                                goto jsondone;
1949
                        }
1950
                        /* Take care of the plugins access limitations */
1951
                        i = 0;
1952
                        for(i=0; i<json_array_size(allowed); i++) {
1953
                                json_t *p = json_array_get(allowed, i);
1954
                                const gchar *plugin_text = json_string_value(p);
1955
                                janus_plugin *plugin_t = janus_plugin_find(plugin_text);
1956
                                if(!janus_auth_allow_plugin(token_value, plugin_t)) {
1957
                                        /* FIXME Should we notify individual failures? */
1958
                                        JANUS_LOG(LOG_WARN, "Error allowing access to '%s' to the new token, bad things may happen...\n", plugin_text);
1959
                                }
1960
                        }
1961
                        /* Get the list of plugins this new token can now access */
1962
                        json_t *plugins_list = json_array();
1963
                        GList *plugins = janus_auth_list_plugins(token_value);
1964
                        if(plugins != NULL) {
1965
                                GList *tmp = plugins;
1966
                                while(tmp) {
1967
                                        janus_plugin *p = (janus_plugin *)tmp->data;
1968
                                        if(p != NULL)
1969
                                                json_array_append_new(plugins_list, json_string(p->get_package()));
1970
                                        tmp = tmp->next;
1971
                                }
1972
                                g_list_free(plugins);
1973
                                plugins = NULL;
1974
                        }
1975
                        /* Prepare JSON reply */
1976
                        json_t *reply = json_object();
1977
                        json_object_set_new(reply, "janus", json_string("success"));
1978
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1979
                        json_t *data = json_object();
1980
                        json_object_set_new(data, "plugins", plugins_list);
1981
                        json_object_set_new(reply, "data", data);
1982
                        /* Send the success reply */
1983
                        ret = janus_process_success(request, reply);
1984
                        goto jsondone;
1985
                } else if(!strcasecmp(message_text, "disallow_token")) {
1986
                        /* Disallow a valid token valid from accessing a plugin */
1987
                        if(!janus_auth_is_enabled()) {
1988
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Token based authentication disabled");
1989
                                goto jsondone;
1990
                        }
1991
                        JANUS_VALIDATE_JSON_OBJECT(root, allow_token_parameters,
1992
                                error_code, error_cause, FALSE,
1993
                                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1994
                        if(error_code != 0) {
1995
                                ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1996
                                goto jsondone;
1997
                        }
1998
                        json_t *token = json_object_get(root, "token");
1999
                        const char *token_value = json_string_value(token);
2000
                        /* Check if the token is valid, first */
2001
                        if(!janus_auth_check_token(token_value)) {
2002
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_TOKEN_NOT_FOUND, "Token %s not found", token_value);
2003
                                goto jsondone;
2004
                        }
2005
                        /* Any plugin this token should be prevented access to? */
2006
                        json_t *allowed = json_object_get(root, "plugins");
2007
                        /* Check the list first */
2008
                        size_t i = 0;
2009
                        gboolean ok = TRUE;
2010
                        for(i=0; i<json_array_size(allowed); i++) {
2011
                                json_t *p = json_array_get(allowed, i);
2012
                                if(!p || !json_is_string(p)) {
2013
                                        /* FIXME Should we fail here? */
2014
                                        JANUS_LOG(LOG_ERR, "Invalid plugin passed to the new token request...\n");
2015
                                        ok = FALSE;
2016
                                        break;
2017
                                }
2018
                                const gchar *plugin_text = json_string_value(p);
2019
                                janus_plugin *plugin_t = janus_plugin_find(plugin_text);
2020
                                if(plugin_t == NULL) {
2021
                                        /* FIXME Should we fail here? */
2022
                                        JANUS_LOG(LOG_ERR, "No such plugin '%s' passed to the new token request...\n", plugin_text);
2023
                                        ok = FALSE;
2024
                                        break;
2025
                                }
2026
                        }
2027
                        if(!ok) {
2028
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (some of the provided plugins are invalid)");
2029
                                goto jsondone;
2030
                        }
2031
                        /* Take care of the plugins access limitations */
2032
                        i = 0;
2033
                        for(i=0; i<json_array_size(allowed); i++) {
2034
                                json_t *p = json_array_get(allowed, i);
2035
                                const gchar *plugin_text = json_string_value(p);
2036
                                janus_plugin *plugin_t = janus_plugin_find(plugin_text);
2037
                                if(!janus_auth_disallow_plugin(token_value, plugin_t)) {
2038
                                        /* FIXME Should we notify individual failures? */
2039
                                        JANUS_LOG(LOG_WARN, "Error allowing access to '%s' to the new token, bad things may happen...\n", plugin_text);
2040
                                }
2041
                        }
2042
                        /* Get the list of plugins this new token can now access */
2043
                        json_t *plugins_list = json_array();
2044
                        GList *plugins = janus_auth_list_plugins(token_value);
2045
                        if(plugins != NULL) {
2046
                                GList *tmp = plugins;
2047
                                while(tmp) {
2048
                                        janus_plugin *p = (janus_plugin *)tmp->data;
2049
                                        if(p != NULL)
2050
                                                json_array_append_new(plugins_list, json_string(p->get_package()));
2051
                                        tmp = tmp->next;
2052
                                }
2053
                                g_list_free(plugins);
2054
                                plugins = NULL;
2055
                        }
2056
                        /* Prepare JSON reply */
2057
                        json_t *reply = json_object();
2058
                        json_object_set_new(reply, "janus", json_string("success"));
2059
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
2060
                        json_t *data = json_object();
2061
                        json_object_set_new(data, "plugins", plugins_list);
2062
                        json_object_set_new(reply, "data", data);
2063
                        /* Send the success reply */
2064
                        ret = janus_process_success(request, reply);
2065
                        goto jsondone;
2066
                } else if(!strcasecmp(message_text, "remove_token")) {
2067
                        /* Invalidate a token for authentication purposes */
2068
                        if(!janus_auth_is_enabled()) {
2069
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Token based authentication disabled");
2070
                                goto jsondone;
2071
                        }
2072
                        JANUS_VALIDATE_JSON_OBJECT(root, token_parameters,
2073
                                error_code, error_cause, FALSE,
2074
                                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2075
                        if(error_code != 0) {
2076
                                ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2077
                                goto jsondone;
2078
                        }
2079
                        json_t *token = json_object_get(root, "token");
2080
                        const char *token_value = json_string_value(token);
2081
                        if(!janus_auth_remove_token(token_value)) {
2082
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Error removing token");
2083
                                goto jsondone;
2084
                        }
2085
                        /* Prepare JSON reply */
2086
                        json_t *reply = json_object();
2087
                        json_object_set_new(reply, "janus", json_string("success"));
2088
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
2089
                        /* Send the success reply */
2090
                        ret = janus_process_success(request, reply);
2091
                        goto jsondone;
2092
                } else {
2093
                        /* No message we know of */
2094
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
2095
                        goto jsondone;
2096
                }
2097
        }
2098
        if(session_id < 1) {
2099
                JANUS_LOG(LOG_ERR, "Invalid session\n");
2100
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, NULL);
2101
                goto jsondone;
2102
        }
2103
        if(h && handle_id < 1) {
2104
                JANUS_LOG(LOG_ERR, "Invalid handle\n");
2105
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, NULL);
2106
                goto jsondone;
2107
        }
2108

    
2109
        /* Go on with the processing */
2110
        if(admin_api_secret != NULL) {
2111
                /* There's an API secret, check that the client provided it */
2112
                json_t *secret = json_object_get(root, "admin_secret");
2113
                if(!secret || !json_is_string(secret) || !janus_strcmp_const_time(json_string_value(secret), admin_api_secret)) {
2114
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED, NULL);
2115
                        goto jsondone;
2116
                }
2117
        }
2118

    
2119
        /* If we got here, make sure we have a session (and/or a handle) */
2120
        janus_session *session = janus_session_find(session_id);
2121
        if(!session) {
2122
                JANUS_LOG(LOG_ERR, "Couldn't find any session %"SCNu64"...\n", session_id);
2123
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, "No such session %"SCNu64"", session_id);
2124
                goto jsondone;
2125
        }
2126
        janus_ice_handle *handle = NULL;
2127
        if(handle_id > 0) {
2128
                handle = janus_ice_handle_find(session, handle_id);
2129
                if(!handle) {
2130
                        JANUS_LOG(LOG_ERR, "Couldn't find any handle %"SCNu64" in session %"SCNu64"...\n", handle_id, session_id);
2131
                        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);
2132
                        goto jsondone;
2133
                }
2134
        }
2135

    
2136
        /* What is this? */
2137
        if(handle == NULL) {
2138
                /* Session-related */
2139
                if(strcasecmp(message_text, "list_handles")) {
2140
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
2141
                        goto jsondone;
2142
                }
2143
                /* List handles */
2144
                json_t *list = json_array();
2145
                if(session->ice_handles != NULL && g_hash_table_size(session->ice_handles) > 0) {
2146
                        GHashTableIter iter;
2147
                        gpointer value;
2148
                        janus_mutex_lock(&session->mutex);
2149
                        g_hash_table_iter_init(&iter, session->ice_handles);
2150
                        while (g_hash_table_iter_next(&iter, NULL, &value)) {
2151
                                janus_ice_handle *handle = value;
2152
                                if(handle == NULL) {
2153
                                        continue;
2154
                                }
2155
                                json_array_append_new(list, json_integer(handle->handle_id));
2156
                        }
2157
                        janus_mutex_unlock(&session->mutex);
2158
                }
2159
                /* Prepare JSON reply */
2160
                json_t *reply = json_object();
2161
                json_object_set_new(reply, "janus", json_string("success"));
2162
                json_object_set_new(reply, "transaction", json_string(transaction_text));
2163
                json_object_set_new(reply, "session_id", json_integer(session_id));
2164
                json_object_set_new(reply, "handles", list);
2165
                /* Send the success reply */
2166
                ret = janus_process_success(request, reply);
2167
                goto jsondone;
2168
        } else {
2169
                /* Handle-related */
2170
                if(strcasecmp(message_text, "handle_info")) {
2171
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
2172
                        goto jsondone;
2173
                }
2174
                /* Prepare info */
2175
                janus_mutex_lock(&handle->mutex);
2176
                json_t *info = json_object();
2177
                json_object_set_new(info, "session_id", json_integer(session_id));
2178
                json_object_set_new(info, "session_last_activity", json_integer(session->last_activity));
2179
                if(session->source && session->source->transport)
2180
                        json_object_set_new(info, "session_transport", json_string(session->source->transport->get_package()));
2181
                json_object_set_new(info, "handle_id", json_integer(handle_id));
2182
                if(handle->opaque_id)
2183
                        json_object_set_new(info, "opaque_id", json_string(handle->opaque_id));
2184
                json_object_set_new(info, "created", json_integer(handle->created));
2185
                json_object_set_new(info, "send_thread_created", g_atomic_int_get(&handle->send_thread_created) ? json_true() : json_false());
2186
                json_object_set_new(info, "current_time", json_integer(janus_get_monotonic_time()));
2187
                if(handle->app && handle->app_handle && janus_plugin_session_is_alive(handle->app_handle)) {
2188
                        janus_plugin *plugin = (janus_plugin *)handle->app;
2189
                        json_object_set_new(info, "plugin", json_string(plugin->get_package()));
2190
                        if(plugin->query_session) {
2191
                                /* FIXME This check will NOT work with legacy plugins that were compiled BEFORE the method was specified in plugin.h */
2192
                                json_t *query = plugin->query_session(handle->app_handle);
2193
                                if(query != NULL) {
2194
                                        /* Make sure this is a JSON object */
2195
                                        if(!json_is_object(query)) {
2196
                                                JANUS_LOG(LOG_WARN, "Ignoring invalid query response from the plugin (not an object)\n");
2197
                                                json_decref(query);
2198
                                        } else {
2199
                                                json_object_set_new(info, "plugin_specific", query);
2200
                                        }
2201
                                        query = NULL;
2202
                                }
2203
                        }
2204
                }
2205
                json_t *flags = json_object();
2206
                json_object_set_new(flags, "got-offer", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER) ? json_true() : json_false());
2207
                json_object_set_new(flags, "got-answer", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER) ? json_true() : json_false());
2208
                json_object_set_new(flags, "processing-offer", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER) ? json_true() : json_false());
2209
                json_object_set_new(flags, "starting", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_START) ? json_true() : json_false());
2210
                json_object_set_new(flags, "ready", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_READY) ? json_true() : json_false());
2211
                json_object_set_new(flags, "stopped", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP) ? json_true() : json_false());
2212
                json_object_set_new(flags, "alert", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT) ? json_true() : json_false());
2213
                json_object_set_new(flags, "bundle", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE) ? json_true() : json_false());
2214
                json_object_set_new(flags, "rtcp-mux", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX) ? json_true() : json_false());
2215
                json_object_set_new(flags, "trickle", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE) ? json_true() : json_false());
2216
                json_object_set_new(flags, "all-trickles", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALL_TRICKLES) ? json_true() : json_false());
2217
                json_object_set_new(flags, "trickle-synced", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE_SYNCED) ? json_true() : json_false());
2218
                json_object_set_new(flags, "data-channels", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS) ? json_true() : json_false());
2219
                json_object_set_new(flags, "has-audio", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO) ? json_true() : json_false());
2220
                json_object_set_new(flags, "has-video", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO) ? json_true() : json_false());
2221
                json_object_set_new(flags, "plan-b", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PLAN_B) ? json_true() : json_false());
2222
                json_object_set_new(flags, "cleaning", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING) ? json_true() : json_false());
2223
                json_object_set_new(info, "flags", flags);
2224
                if(handle->agent) {
2225
                        json_object_set_new(info, "agent-created", json_integer(handle->agent_created));
2226
                        json_object_set_new(info, "ice-mode", json_string(janus_ice_is_ice_lite_enabled() ? "lite" : "full"));
2227
                        json_object_set_new(info, "ice-role", json_string(handle->controlling ? "controlling" : "controlled"));
2228
                }
2229
                if(handle->force_bundle)
2230
                        json_object_set_new(info, "force-bundle", json_true());
2231
                if(handle->force_rtcp_mux)
2232
                        json_object_set_new(info, "force-rtcp-mux", json_true());
2233
                json_t *sdps = json_object();
2234
                if(handle->rtp_profile)
2235
                        json_object_set_new(sdps, "profile", json_string(handle->rtp_profile));
2236
                if(handle->local_sdp)
2237
                        json_object_set_new(sdps, "local", json_string(handle->local_sdp));
2238
                if(handle->remote_sdp)
2239
                        json_object_set_new(sdps, "remote", json_string(handle->remote_sdp));
2240
                json_object_set_new(info, "sdps", sdps);
2241
                if(handle->pending_trickles)
2242
                        json_object_set_new(info, "pending-trickles", json_integer(g_list_length(handle->pending_trickles)));
2243
                if(handle->queued_packets)
2244
                        json_object_set_new(info, "queued-packets", json_integer(g_async_queue_length(handle->queued_packets)));
2245
                json_t *streams = json_array();
2246
                if(handle->audio_stream) {
2247
                        json_t *s = janus_admin_stream_summary(handle->audio_stream);
2248
                        if(s)
2249
                                json_array_append_new(streams, s);
2250
                }
2251
                if(handle->video_stream) {
2252
                        json_t *s = janus_admin_stream_summary(handle->video_stream);
2253
                        if(s)
2254
                                json_array_append_new(streams, s);
2255
                }
2256
                if(handle->data_stream) {
2257
                        json_t *s = janus_admin_stream_summary(handle->data_stream);
2258
                        if(s)
2259
                                json_array_append_new(streams, s);
2260
                }
2261
                json_object_set_new(info, "streams", streams);
2262
                janus_mutex_unlock(&handle->mutex);
2263
                /* Prepare JSON reply */
2264
                json_t *reply = json_object();
2265
                json_object_set_new(reply, "janus", json_string("success"));
2266
                json_object_set_new(reply, "transaction", json_string(transaction_text));
2267
                json_object_set_new(reply, "session_id", json_integer(session_id));
2268
                json_object_set_new(reply, "handle_id", json_integer(handle_id));
2269
                json_object_set_new(reply, "info", info);
2270
                /* Send the success reply */
2271
                ret = janus_process_success(request, reply);
2272
                goto jsondone;
2273
        }
2274

    
2275
jsondone:
2276
        /* Done processing */
2277
        return ret;
2278
}
2279

    
2280
int janus_process_success(janus_request *request, json_t *payload)
2281
{
2282
        if(!request || !payload)
2283
                return -1;
2284
        /* Pass to the right transport plugin */
2285
        JANUS_LOG(LOG_HUGE, "Sending %s API response to %s (%p)\n", request->admin ? "admin" : "Janus", request->transport->get_package(), request->instance);
2286
        return request->transport->send_message(request->instance, request->request_id, request->admin, payload);
2287
}
2288

    
2289
static int janus_process_error_string(janus_request *request, uint64_t session_id, const char *transaction, gint error, gchar *error_string)
2290
{
2291
        if(!request)
2292
                return -1;
2293
        /* Done preparing error */
2294
        JANUS_LOG(LOG_VERB, "[%s] Returning %s API error %d (%s)\n", transaction, request->admin ? "admin" : "Janus", error, error_string);
2295
        /* Prepare JSON error */
2296
        json_t *reply = json_object();
2297
        json_object_set_new(reply, "janus", json_string("error"));
2298
        if(session_id > 0)
2299
                json_object_set_new(reply, "session_id", json_integer(session_id));
2300
        if(transaction != NULL)
2301
                json_object_set_new(reply, "transaction", json_string(transaction));
2302
        json_t *error_data = json_object();
2303
        json_object_set_new(error_data, "code", json_integer(error));
2304
        json_object_set_new(error_data, "reason", json_string(error_string));
2305
        json_object_set_new(reply, "error", error_data);
2306
        /* Pass to the right transport plugin */
2307
        return request->transport->send_message(request->instance, request->request_id, request->admin, reply);
2308
}
2309

    
2310
int janus_process_error(janus_request *request, uint64_t session_id, const char *transaction, gint error, const char *format, ...)
2311
{
2312
        if(!request)
2313
                return -1;
2314
        gchar *error_string = NULL;
2315
        gchar error_buf[512];
2316
        if(format == NULL) {
2317
                /* No error string provided, use the default one */
2318
                error_string = (gchar *)janus_get_api_error(error);
2319
        } else {
2320
                /* This callback has variable arguments (error string) */
2321
                va_list ap;
2322
                va_start(ap, format);
2323
                g_vsnprintf(error_buf, sizeof(error_buf), format, ap);
2324
                va_end(ap);
2325
                error_string = error_buf;
2326
        }
2327
        return janus_process_error_string(request, session_id, transaction, error, error_string);
2328
}
2329

    
2330
/* Admin/monitor helpers */
2331
json_t *janus_admin_stream_summary(janus_ice_stream *stream) {
2332
        if(stream == NULL)
2333
                return NULL;
2334
        json_t *s = json_object();
2335
        json_object_set_new(s, "id", json_integer(stream->stream_id));
2336
        json_object_set_new(s, "ready", json_integer(stream->cdone));
2337
        json_object_set_new(s, "disabled", stream->disabled ? json_true() : json_false());
2338
        json_t *ss = json_object();
2339
        if(stream->audio_ssrc)
2340
                json_object_set_new(ss, "audio", json_integer(stream->audio_ssrc));
2341
        if(stream->video_ssrc)
2342
                json_object_set_new(ss, "video", json_integer(stream->video_ssrc));
2343
        if(stream->audio_ssrc_peer)
2344
                json_object_set_new(ss, "audio-peer", json_integer(stream->audio_ssrc_peer));
2345
        if(stream->video_ssrc_peer)
2346
                json_object_set_new(ss, "video-peer", json_integer(stream->video_ssrc_peer));
2347
        if(stream->video_ssrc_peer_rtx)
2348
                json_object_set_new(ss, "video-peer-rtx", json_integer(stream->video_ssrc_peer_rtx));
2349
        json_object_set_new(s, "ssrc", ss);
2350
        json_t *components = json_array();
2351
        if(stream->rtp_component) {
2352
                json_t *c = janus_admin_component_summary(stream->rtp_component);
2353
                if(c)
2354
                        json_array_append_new(components, c);
2355
        }
2356
        if(stream->rtcp_component) {
2357
                json_t *c = janus_admin_component_summary(stream->rtcp_component);
2358
                if(c)
2359
                        json_array_append_new(components, c);
2360
        }
2361
        json_t *rtcp_stats = NULL;
2362
        if(stream->audio_rtcp_ctx != NULL) {
2363
                rtcp_stats = json_object();
2364
                json_t *audio_rtcp_stats = json_object();
2365
                json_object_set_new(audio_rtcp_stats, "base", json_integer(stream->audio_rtcp_ctx->tb));
2366
                json_object_set_new(audio_rtcp_stats, "lsr", json_integer(janus_rtcp_context_get_lsr(stream->audio_rtcp_ctx)));
2367
                json_object_set_new(audio_rtcp_stats, "lost", json_integer(janus_rtcp_context_get_lost_all(stream->audio_rtcp_ctx, FALSE)));
2368
                json_object_set_new(audio_rtcp_stats, "lost-by-remote", json_integer(janus_rtcp_context_get_lost_all(stream->audio_rtcp_ctx, TRUE)));
2369
                json_object_set_new(audio_rtcp_stats, "jitter-local", json_integer(janus_rtcp_context_get_jitter(stream->audio_rtcp_ctx, FALSE)));
2370
                json_object_set_new(audio_rtcp_stats, "jitter-remote", json_integer(janus_rtcp_context_get_jitter(stream->audio_rtcp_ctx, TRUE)));
2371
                json_object_set_new(rtcp_stats, "audio", audio_rtcp_stats);
2372
        }
2373
        if(stream->video_rtcp_ctx != NULL) {
2374
                if(rtcp_stats == NULL)
2375
                        rtcp_stats = json_object();
2376
                json_t *video_rtcp_stats = json_object();
2377
                json_object_set_new(video_rtcp_stats, "base", json_integer(stream->video_rtcp_ctx->tb));
2378
                json_object_set_new(video_rtcp_stats, "lsr", json_integer(janus_rtcp_context_get_lsr(stream->video_rtcp_ctx)));
2379
                json_object_set_new(video_rtcp_stats, "lost", json_integer(janus_rtcp_context_get_lost_all(stream->video_rtcp_ctx, FALSE)));
2380
                json_object_set_new(video_rtcp_stats, "lost-by-remote", json_integer(janus_rtcp_context_get_lost_all(stream->video_rtcp_ctx, TRUE)));
2381
                json_object_set_new(video_rtcp_stats, "jitter-local", json_integer(janus_rtcp_context_get_jitter(stream->video_rtcp_ctx, FALSE)));
2382
                json_object_set_new(video_rtcp_stats, "jitter-remote", json_integer(janus_rtcp_context_get_jitter(stream->video_rtcp_ctx, TRUE)));
2383
                json_object_set_new(rtcp_stats, "video", video_rtcp_stats);
2384
        }
2385
        if(rtcp_stats != NULL)
2386
                json_object_set_new(s, "rtcp_stats", rtcp_stats);
2387
        json_object_set_new(s, "components", components);
2388
        return s;
2389
}
2390

    
2391
json_t *janus_admin_component_summary(janus_ice_component *component) {
2392
        if(component == NULL)
2393
                return NULL;
2394
        janus_ice_handle *handle = component->stream ? component->stream->handle : NULL;
2395
        json_t *c = json_object();
2396
        json_object_set_new(c, "id", json_integer(component->component_id));
2397
        json_object_set_new(c, "state", json_string(janus_get_ice_state_name(component->state)));
2398
        if(component->component_connected > 0)
2399
                json_object_set_new(c, "connected", json_integer(component->component_connected));
2400
        if(component->local_candidates) {
2401
                json_t *cs = json_array();
2402
                GSList *candidates = component->local_candidates, *i = NULL;
2403
                for (i = candidates; i; i = i->next) {
2404
                        gchar *lc = (gchar *) i->data;
2405
                        if(lc)
2406
                                json_array_append_new(cs, json_string(lc));
2407
                }
2408
                json_object_set_new(c, "local-candidates", cs);
2409
        }
2410
        if(component->remote_candidates) {
2411
                json_t *cs = json_array();
2412
                GSList *candidates = component->remote_candidates, *i = NULL;
2413
                for (i = candidates; i; i = i->next) {
2414
                        gchar *rc = (gchar *) i->data;
2415
                        if(rc)
2416
                                json_array_append_new(cs, json_string(rc));
2417
                }
2418
                json_object_set_new(c, "remote-candidates", cs);
2419
        }
2420
        if(component->selected_pair) {
2421
                json_object_set_new(c, "selected-pair", json_string(component->selected_pair));
2422
        }
2423
        json_t *d = json_object();
2424
        json_t *in_stats = json_object();
2425
        json_t *out_stats = json_object();
2426
        if(component->dtls) {
2427
                janus_dtls_srtp *dtls = component->dtls;
2428
                json_object_set_new(d, "fingerprint", json_string(janus_dtls_get_local_fingerprint()));
2429
                json_object_set_new(d, "remote-fingerprint", json_string(component->stream->remote_fingerprint));
2430
                json_object_set_new(d, "remote-fingerprint-hash", json_string(component->stream->remote_hashing));
2431
                json_object_set_new(d, "dtls-role", json_string(janus_get_dtls_srtp_role(component->stream->dtls_role)));
2432
                json_object_set_new(d, "dtls-state", json_string(janus_get_dtls_srtp_state(dtls->dtls_state)));
2433
                json_object_set_new(d, "retransmissions", json_integer(dtls->retransmissions));
2434
                json_object_set_new(d, "valid", dtls->srtp_valid ? json_true() : json_false());
2435
                json_object_set_new(d, "ready", dtls->ready ? json_true() : json_false());
2436
                if(dtls->dtls_connected > 0)
2437
                        json_object_set_new(d, "connected", json_integer(dtls->dtls_connected));
2438
                if(handle && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO)) {
2439
                        json_object_set_new(in_stats, "audio_packets", json_integer(component->in_stats.audio_packets));
2440
                        json_object_set_new(in_stats, "audio_bytes", json_integer(component->in_stats.audio_bytes));
2441
                        json_object_set_new(in_stats, "audio_nacks", json_integer(component->in_stats.audio_nacks));
2442
                        /* Compute the last second stuff too */
2443
                        gint64 now = janus_get_monotonic_time();
2444
                        guint64 bytes = 0;
2445
                        if(component->in_stats.audio_bytes_lastsec) {
2446
                                GList *lastsec = component->in_stats.audio_bytes_lastsec;
2447
                                while(lastsec) {
2448
                                        janus_ice_stats_item *s = (janus_ice_stats_item *)lastsec->data;
2449
                                        if(s && now-s->when < G_USEC_PER_SEC)
2450
                                                bytes += s->bytes;
2451
                                        lastsec = lastsec->next;
2452
                                }
2453
                        }
2454
                        json_object_set_new(in_stats, "audio_bytes_lastsec", json_integer(bytes));
2455
                }
2456
                if(handle && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO)) {
2457
                        json_object_set_new(in_stats, "video_packets", json_integer(component->in_stats.video_packets));
2458
                        json_object_set_new(in_stats, "video_bytes", json_integer(component->in_stats.video_bytes));
2459
                        json_object_set_new(in_stats, "video_nacks", json_integer(component->in_stats.video_nacks));
2460
                        /* Compute the last second stuff too */
2461
                        gint64 now = janus_get_monotonic_time();
2462
                        guint64 bytes = 0;
2463
                        if(component->in_stats.video_bytes_lastsec) {
2464
                                GList *lastsec = component->in_stats.video_bytes_lastsec;
2465
                                while(lastsec) {
2466
                                        janus_ice_stats_item *s = (janus_ice_stats_item *)lastsec->data;
2467
                                        if(s && now-s->when < G_USEC_PER_SEC)
2468
                                                bytes += s->bytes;
2469
                                        lastsec = lastsec->next;
2470
                                }
2471
                        }
2472
                        json_object_set_new(in_stats, "video_bytes_lastsec", json_integer(bytes));
2473
                }
2474
                json_object_set_new(in_stats, "data_packets", json_integer(component->in_stats.data_packets));
2475
                json_object_set_new(in_stats, "data_bytes", json_integer(component->in_stats.data_bytes));
2476
                if(handle && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO)) {
2477
                        json_object_set_new(out_stats, "audio_packets", json_integer(component->out_stats.audio_packets));
2478
                        json_object_set_new(out_stats, "audio_bytes", json_integer(component->out_stats.audio_bytes));
2479
                        json_object_set_new(out_stats, "audio_nacks", json_integer(component->out_stats.audio_nacks));
2480
                }
2481
                if(handle && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO)) {
2482
                        json_object_set_new(out_stats, "video_packets", json_integer(component->out_stats.video_packets));
2483
                        json_object_set_new(out_stats, "video_bytes", json_integer(component->out_stats.video_bytes));
2484
                        json_object_set_new(out_stats, "video_nacks", json_integer(component->out_stats.video_nacks));
2485
                }
2486
                json_object_set_new(out_stats, "data_packets", json_integer(component->out_stats.data_packets));
2487
                json_object_set_new(out_stats, "data_bytes", json_integer(component->out_stats.data_bytes));
2488
#ifdef HAVE_SCTP
2489
                /* FIXME Actually check if this succeeded? */
2490
                json_object_set_new(d, "sctp-association", dtls->sctp ? json_true() : json_false());
2491
#endif
2492
        }
2493
        json_object_set_new(c, "dtls", d);
2494
        json_object_set_new(c, "in_stats", in_stats);
2495
        json_object_set_new(c, "out_stats", out_stats);
2496
        return c;
2497
}
2498

    
2499

    
2500
/* Transports */
2501
void janus_transport_close(gpointer key, gpointer value, gpointer user_data) {
2502
        janus_transport *transport = (janus_transport *)value;
2503
        if(!transport)
2504
                return;
2505
        transport->destroy();
2506
}
2507

    
2508
void janus_transportso_close(gpointer key, gpointer value, gpointer user_data) {
2509
        void *transport = value;
2510
        if(!transport)
2511
                return;
2512
        /* FIXME We don't dlclose transports to be sure we can detect leaks */
2513
        //~ dlclose(transport);
2514
}
2515

    
2516
/* Transport callback interface */
2517
void janus_transport_incoming_request(janus_transport *plugin, void *transport, void *request_id, gboolean admin, json_t *message, json_error_t *error) {
2518
        JANUS_LOG(LOG_VERB, "Got %s API request from %s (%p)\n", admin ? "an admin" : "a Janus", plugin->get_package(), transport);
2519
        /* Create a janus_request instance to handle the request */
2520
        janus_request *request = janus_request_new(plugin, transport, request_id, admin, message);
2521
        GError *tperror = NULL;
2522
        g_thread_pool_push(tasks, request, &tperror);
2523
        if(tperror != NULL) {
2524
                /* Something went wrong... */
2525
                JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to push task in thread pool...\n", tperror->code, tperror->message ? tperror->message : "??");
2526
                json_t *transaction = json_object_get(message, "transaction");
2527
                const char *transaction_text = json_is_string(transaction) ? json_string_value(transaction) : NULL;
2528
                janus_process_error(request, 0, transaction_text, JANUS_ERROR_UNKNOWN, "Thread pool error");
2529
                janus_request_destroy(request);
2530
        }
2531
}
2532

    
2533
void janus_transport_gone(janus_transport *plugin, void *transport) {
2534
        /* Get rid of sessions this transport was handling */
2535
        JANUS_LOG(LOG_VERB, "A %s transport instance has gone away (%p)\n", plugin->get_package(), transport);
2536
        janus_mutex_lock(&sessions_mutex);
2537
        if(sessions && g_hash_table_size(sessions) > 0) {
2538
                GHashTableIter iter;
2539
                gpointer value;
2540
                g_hash_table_iter_init(&iter, sessions);
2541
                while(g_hash_table_iter_next(&iter, NULL, &value)) {
2542
                        janus_session *session = (janus_session *) value;
2543
                        if(!session || session->destroy || session->timeout || session->last_activity == 0)
2544
                                continue;
2545
                        if(session->source && session->source->instance == transport) {
2546
                                JANUS_LOG(LOG_VERB, "  -- Marking Session %"SCNu64" as over\n", session->session_id);
2547
                                session->last_activity = 0;        /* This will trigger a timeout */
2548
                        }
2549
                }
2550
        }
2551
        janus_mutex_unlock(&sessions_mutex);
2552
}
2553

    
2554
gboolean janus_transport_is_api_secret_needed(janus_transport *plugin) {
2555
        return api_secret != NULL;
2556
}
2557

    
2558
gboolean janus_transport_is_api_secret_valid(janus_transport *plugin, const char *apisecret) {
2559
        if(api_secret == NULL)
2560
                return TRUE;
2561
        return apisecret && janus_strcmp_const_time(apisecret, api_secret);
2562
}
2563

    
2564
gboolean janus_transport_is_auth_token_needed(janus_transport *plugin) {
2565
        return janus_auth_is_enabled();
2566
}
2567

    
2568
gboolean janus_transport_is_auth_token_valid(janus_transport *plugin, const char *token) {
2569
        if(!janus_auth_is_enabled())
2570
                return TRUE;
2571
        return token && janus_auth_check_token(token);
2572
}
2573

    
2574
void janus_transport_notify_event(janus_transport *plugin, void *transport, json_t *event) {
2575
        /* A plugin asked to notify an event to the handlers */
2576
        if(!plugin || !event || !json_is_object(event))
2577
                return;
2578
        /* Notify event handlers */
2579
        if(janus_events_is_enabled()) {
2580
                janus_events_notify_handlers(JANUS_EVENT_TYPE_TRANSPORT,
2581
                        0, plugin->get_package(), transport, event);
2582
        } else {
2583
                json_decref(event);
2584
        }
2585
}
2586

    
2587
void janus_transport_task(gpointer data, gpointer user_data) {
2588
        JANUS_LOG(LOG_VERB, "Transport task pool, serving request\n");
2589
        janus_request *request = (janus_request *)data;
2590
        if(request == NULL) {
2591
                JANUS_LOG(LOG_ERR, "Missing request\n");
2592
                return;
2593
        }
2594
        if(!request->admin)
2595
                janus_process_incoming_request(request);
2596
        else
2597
                janus_process_incoming_admin_request(request);
2598
        /* Done */
2599
        janus_request_destroy(request);
2600
}
2601

    
2602

    
2603
/* Event handlers */
2604
void janus_eventhandler_close(gpointer key, gpointer value, gpointer user_data) {
2605
        janus_eventhandler *eventhandler = (janus_eventhandler *)value;
2606
        if(!eventhandler)
2607
                return;
2608
        eventhandler->destroy();
2609
}
2610

    
2611
void janus_eventhandlerso_close(gpointer key, gpointer value, gpointer user_data) {
2612
        void *eventhandler = (janus_eventhandler *)value;
2613
        if(!eventhandler)
2614
                return;
2615
        //~ dlclose(eventhandler);
2616
}
2617

    
2618

    
2619
/* Plugins */
2620
void janus_plugin_close(gpointer key, gpointer value, gpointer user_data) {
2621
        janus_plugin *plugin = (janus_plugin *)value;
2622
        if(!plugin)
2623
                return;
2624
        plugin->destroy();
2625
}
2626

    
2627
void janus_pluginso_close(gpointer key, gpointer value, gpointer user_data) {
2628
        void *plugin = value;
2629
        if(!plugin)
2630
                return;
2631
        /* FIXME We don't dlclose plugins to be sure we can detect leaks */
2632
        //~ dlclose(plugin);
2633
}
2634

    
2635
janus_plugin *janus_plugin_find(const gchar *package) {
2636
        if(package != NULL && plugins != NULL)        /* FIXME Do we need to fix the key pointer? */
2637
                return g_hash_table_lookup(plugins, package);
2638
        return NULL;
2639
}
2640

    
2641

    
2642
/* Plugin callback interface */
2643
int janus_plugin_push_event(janus_plugin_session *plugin_session, janus_plugin *plugin, const char *transaction, json_t *message, json_t *jsep) {
2644
        if(!plugin || !message)
2645
                return -1;
2646
        if(!plugin_session || plugin_session < (janus_plugin_session *)0x1000 ||
2647
                        !janus_plugin_session_is_alive(plugin_session) || plugin_session->stopped)
2648
                return -2;
2649
        janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
2650
        if(!ice_handle || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP))
2651
                return JANUS_ERROR_SESSION_NOT_FOUND;
2652
        janus_session *session = ice_handle->session;
2653
        if(!session || session->destroy)
2654
                return JANUS_ERROR_SESSION_NOT_FOUND;
2655
        /* Make sure this is a JSON object */
2656
        if(!json_is_object(message)) {
2657
                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (JSON error: not an object)\n", ice_handle->handle_id);
2658
                return JANUS_ERROR_INVALID_JSON_OBJECT;
2659
        }
2660
        /* Attach JSEP if possible? */
2661
        const char *sdp_type = json_string_value(json_object_get(jsep, "type"));
2662
        const char *sdp = json_string_value(json_object_get(jsep, "sdp"));
2663
        json_t *merged_jsep = NULL;
2664
        if(sdp_type != NULL && sdp != NULL) {
2665
                merged_jsep = janus_plugin_handle_sdp(plugin_session, plugin, sdp_type, sdp);
2666
                if(merged_jsep == NULL) {
2667
                        if(ice_handle == NULL || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2668
                                        || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)) {
2669
                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (handle not available anymore or negotiation stopped)\n", ice_handle->handle_id);
2670
                                return JANUS_ERROR_HANDLE_NOT_FOUND;
2671
                        } else {
2672
                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (JSON error: problem with the SDP)\n", ice_handle->handle_id);
2673
                                return JANUS_ERROR_JSEP_INVALID_SDP;
2674
                        }
2675
                }
2676
        }
2677
        /* Reference the payload, as the plugin may still need it and will do a decref itself */
2678
        json_incref(message);
2679
        /* Prepare JSON event */
2680
        json_t *event = json_object();
2681
        json_object_set_new(event, "janus", json_string("event"));
2682
        json_object_set_new(event, "session_id", json_integer(session->session_id));
2683
        json_object_set_new(event, "sender", json_integer(ice_handle->handle_id));
2684
        if(transaction != NULL)
2685
                json_object_set_new(event, "transaction", json_string(transaction));
2686
        json_t *plugin_data = json_object();
2687
        json_object_set_new(plugin_data, "plugin", json_string(plugin->get_package()));
2688
        json_object_set_new(plugin_data, "data", message);
2689
        json_object_set_new(event, "plugindata", plugin_data);
2690
        if(merged_jsep != NULL)
2691
                json_object_set_new(event, "jsep", merged_jsep);
2692
        /* Send the event */
2693
        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Sending event to transport...\n", ice_handle->handle_id);
2694
        janus_session_notify_event(session->session_id, event);
2695

    
2696
        if(jsep != NULL && janus_events_is_enabled()) {
2697
                /* Notify event handlers as well */
2698
                janus_events_notify_handlers(JANUS_EVENT_TYPE_JSEP,
2699
                        session->session_id, ice_handle->handle_id, "local", sdp_type, sdp);
2700
        }
2701

    
2702
        return JANUS_OK;
2703
}
2704

    
2705
json_t *janus_plugin_handle_sdp(janus_plugin_session *plugin_session, janus_plugin *plugin, const char *sdp_type, const char *sdp) {
2706
        if(!plugin_session || plugin_session < (janus_plugin_session *)0x1000 ||
2707
                        !janus_plugin_session_is_alive(plugin_session) || plugin_session->stopped ||
2708
                        plugin == NULL || sdp_type == NULL || sdp == NULL) {
2709
                JANUS_LOG(LOG_ERR, "Invalid arguments\n");
2710
                return NULL;
2711
        }
2712
        janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
2713
        //~ if(ice_handle == NULL || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_READY)) {
2714
        if(ice_handle == NULL) {
2715
                JANUS_LOG(LOG_ERR, "Invalid ICE handle\n");
2716
                return NULL;
2717
        }
2718
        int offer = 0;
2719
        if(!strcasecmp(sdp_type, "offer")) {
2720
                /* This is an offer from a plugin */
2721
                offer = 1;
2722
                janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER);
2723
                janus_flags_clear(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER);
2724
        } else if(!strcasecmp(sdp_type, "answer")) {
2725
                /* This is an answer from a plugin */
2726
                janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER);
2727
        } else {
2728
                /* TODO Handle other messages */
2729
                JANUS_LOG(LOG_ERR, "Unknown type '%s'\n", sdp_type);
2730
                return NULL;
2731
        }
2732
        /* Is this valid SDP? */
2733
        char error_str[512];
2734
        int audio = 0, video = 0, data = 0, bundle = 0, rtcpmux = 0, trickle = 0;
2735
        janus_sdp *parsed_sdp = janus_sdp_preparse(sdp, error_str, sizeof(error_str), &audio, &video, &data, &bundle, &rtcpmux, &trickle);
2736
        if(parsed_sdp == NULL) {
2737
                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Couldn't parse SDP... %s\n", ice_handle->handle_id, error_str);
2738
                return NULL;
2739
        }
2740
        gboolean updating = FALSE;
2741
        if(offer) {
2742
                /* We still don't have a local ICE setup */
2743
                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio %s been negotiated\n", ice_handle->handle_id, audio ? "has" : "has NOT");
2744
                if(audio > 1) {
2745
                        JANUS_LOG(LOG_ERR, "[%"SCNu64"] More than one audio line? only going to negotiate one...\n", ice_handle->handle_id);
2746
                }
2747
                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Video %s been negotiated\n", ice_handle->handle_id, video ? "has" : "has NOT");
2748
                if(video > 1) {
2749
                        JANUS_LOG(LOG_ERR, "[%"SCNu64"] More than one video line? only going to negotiate one...\n", ice_handle->handle_id);
2750
                }
2751
                JANUS_LOG(LOG_VERB, "[%"SCNu64"] SCTP/DataChannels %s been negotiated\n", ice_handle->handle_id, data ? "have" : "have NOT");
2752
                if(data > 1) {
2753
                        JANUS_LOG(LOG_ERR, "[%"SCNu64"] More than one data line? only going to negotiate one...\n", ice_handle->handle_id);
2754
                }
2755
#ifndef HAVE_SCTP
2756
                if(data) {
2757
                        JANUS_LOG(LOG_WARN, "[%"SCNu64"]   -- DataChannels have been negotiated, but support for them has not been compiled...\n", ice_handle->handle_id);
2758
                }
2759
#endif
2760
                /* Are we still cleaning up from a previous media session? */
2761
                if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING)) {
2762
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Still cleaning up from a previous media session, let's wait a bit...\n", ice_handle->handle_id);
2763
                        gint64 waited = 0;
2764
                        while(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING)) {
2765
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Still cleaning up from a previous media session, let's wait a bit...\n", ice_handle->handle_id);
2766
                                g_usleep(100000);
2767
                                waited += 100000;
2768
                                if(waited >= 3*G_USEC_PER_SEC) {
2769
                                        JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Waited 3 seconds, that's enough!\n", ice_handle->handle_id);
2770
                                        break;
2771
                                }
2772
                        }
2773
                }
2774
                if(ice_handle->agent == NULL) {
2775
                        /* Process SDP in order to setup ICE locally (this is going to result in an answer from the browser) */
2776
                        if(janus_ice_setup_local(ice_handle, 0, audio, video, data, bundle, rtcpmux, trickle) < 0) {
2777
                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error setting ICE locally\n", ice_handle->handle_id);
2778
                                janus_sdp_free(parsed_sdp);
2779
                                return NULL;
2780
                        }
2781
                } else {
2782
                        updating = TRUE;
2783
                        JANUS_LOG(LOG_INFO, "[%"SCNu64"] Updating existing session\n", ice_handle->handle_id);
2784
                }
2785
        }
2786
        if(!updating) {
2787
                /* Wait for candidates-done callback */
2788
                while(ice_handle->cdone < ice_handle->streams_num) {
2789
                        if(ice_handle == NULL || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2790
                                        || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)) {
2791
                                JANUS_LOG(LOG_WARN, "[%"SCNu64"] Handle detached or PC closed, giving up...!\n", ice_handle ? ice_handle->handle_id : 0);
2792
                                janus_sdp_free(parsed_sdp);
2793
                                return NULL;
2794
                        }
2795
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Waiting for candidates-done callback...\n", ice_handle->handle_id);
2796
                        g_usleep(100000);
2797
                        if(ice_handle->cdone < 0) {
2798
                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error gathering candidates!\n", ice_handle->handle_id);
2799
                                janus_sdp_free(parsed_sdp);
2800
                                return NULL;
2801
                        }
2802
                }
2803
        }
2804
        /* Anonymize SDP */
2805
        if(janus_sdp_anonymize(parsed_sdp) < 0) {
2806
                /* Invalid SDP */
2807
                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Invalid SDP\n", ice_handle->handle_id);
2808
                janus_sdp_free(parsed_sdp);
2809
                return NULL;
2810
        }
2811
        /* Add our details */
2812
        char *sdp_merged = janus_sdp_merge(ice_handle, parsed_sdp);
2813
        if(sdp_merged == NULL) {
2814
                /* Couldn't merge SDP */
2815
                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error merging SDP\n", ice_handle->handle_id);
2816
                janus_sdp_free(parsed_sdp);
2817
                return NULL;
2818
        }
2819
        janus_sdp_free(parsed_sdp);
2820
        /* FIXME Any disabled m-line? */
2821
        if(strstr(sdp_merged, "m=audio 0")) {
2822
                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio disabled via SDP\n", ice_handle->handle_id);
2823
                if(!janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)
2824
                                || (!video && !data)) {
2825
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Marking audio stream as disabled\n", ice_handle->handle_id);
2826
                        janus_ice_stream *stream = g_hash_table_lookup(ice_handle->streams, GUINT_TO_POINTER(ice_handle->audio_id));
2827
                        if(stream)
2828
                                stream->disabled = TRUE;
2829
                }
2830
        }
2831
        if(strstr(sdp_merged, "m=video 0")) {
2832
                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Video disabled via SDP\n", ice_handle->handle_id);
2833
                if(!janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)
2834
                                || (!audio && !data)) {
2835
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Marking video stream as disabled\n", ice_handle->handle_id);
2836
                        janus_ice_stream *stream = NULL;
2837
                        if(!janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
2838
                                stream = g_hash_table_lookup(ice_handle->streams, GUINT_TO_POINTER(ice_handle->video_id));
2839
                        } else {
2840
                                gint id = ice_handle->audio_id > 0 ? ice_handle->audio_id : ice_handle->video_id;
2841
                                stream = g_hash_table_lookup(ice_handle->streams, GUINT_TO_POINTER(id));
2842
                        }
2843
                        if(stream)
2844
                                stream->disabled = TRUE;
2845
                }
2846
        }
2847
        if(strstr(sdp_merged, "m=application 0 DTLS/SCTP")) {
2848
                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Data Channel disabled via SDP\n", ice_handle->handle_id);
2849
                if(!janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)
2850
                                || (!audio && !video)) {
2851
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Marking data channel stream as disabled\n", ice_handle->handle_id);
2852
                        janus_ice_stream *stream = NULL;
2853
                        if(!janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
2854
                                stream = g_hash_table_lookup(ice_handle->streams, GUINT_TO_POINTER(ice_handle->data_id));
2855
                        } else {
2856
                                gint id = ice_handle->audio_id > 0 ? ice_handle->audio_id : (ice_handle->video_id > 0 ? ice_handle->video_id : ice_handle->data_id);
2857
                                stream = g_hash_table_lookup(ice_handle->streams, GUINT_TO_POINTER(id));
2858
                        }
2859
                        if(stream)
2860
                                stream->disabled = TRUE;
2861
                }
2862
        }
2863

    
2864
        if(!updating) {
2865
                if(offer) {
2866
                        /* We set the flag to wait for an answer before handling trickle candidates */
2867
                        janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
2868
                } else {
2869
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Done! Ready to setup remote candidates and send connectivity checks...\n", ice_handle->handle_id);
2870
                        if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
2871
                                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);
2872
                                if(audio) {
2873
                                        /* Get rid of video and data, if present */
2874
                                        if(ice_handle->streams && ice_handle->video_stream) {
2875
                                                ice_handle->audio_stream->video_ssrc = ice_handle->video_stream->video_ssrc;
2876
                                                ice_handle->audio_stream->video_ssrc_peer = ice_handle->video_stream->video_ssrc_peer;
2877
                                                ice_handle->audio_stream->video_ssrc_peer_rtx = ice_handle->video_stream->video_ssrc_peer_rtx;
2878
                                                nice_agent_attach_recv(ice_handle->agent, ice_handle->video_stream->stream_id, 1, g_main_loop_get_context (ice_handle->iceloop), NULL, NULL);
2879
                                                if(!ice_handle->force_rtcp_mux && !janus_ice_is_rtcpmux_forced())
2880
                                                        nice_agent_attach_recv(ice_handle->agent, ice_handle->video_stream->stream_id, 2, g_main_loop_get_context (ice_handle->iceloop), NULL, NULL);
2881
                                                nice_agent_remove_stream(ice_handle->agent, ice_handle->video_stream->stream_id);
2882
                                                janus_ice_stream_free(ice_handle->streams, ice_handle->video_stream);
2883
                                        }
2884
                                        ice_handle->video_stream = NULL;
2885
                                        ice_handle->video_id = 0;
2886
                                        if(ice_handle->streams && ice_handle->data_stream) {
2887
                                                nice_agent_attach_recv(ice_handle->agent, ice_handle->data_stream->stream_id, 1, g_main_loop_get_context (ice_handle->iceloop), NULL, NULL);
2888
                                                nice_agent_remove_stream(ice_handle->agent, ice_handle->data_stream->stream_id);
2889
                                                janus_ice_stream_free(ice_handle->streams, ice_handle->data_stream);
2890
                                        }
2891
                                        ice_handle->data_stream = NULL;
2892
                                        ice_handle->data_id = 0;
2893
                                        if(!video) {
2894
                                                ice_handle->audio_stream->video_ssrc = 0;
2895
                                                ice_handle->audio_stream->video_ssrc_peer = 0;
2896
                                                g_free(ice_handle->audio_stream->video_rtcp_ctx);
2897
                                                ice_handle->audio_stream->video_rtcp_ctx = NULL;
2898
                                        }
2899
                                } else if(video) {
2900
                                        /* Get rid of data, if present */
2901
                                        if(ice_handle->streams && ice_handle->data_stream) {
2902
                                                nice_agent_attach_recv(ice_handle->agent, ice_handle->data_stream->stream_id, 1, g_main_loop_get_context (ice_handle->iceloop), NULL, NULL);
2903
                                                nice_agent_remove_stream(ice_handle->agent, ice_handle->data_stream->stream_id);
2904
                                                janus_ice_stream_free(ice_handle->streams, ice_handle->data_stream);
2905
                                        }
2906
                                        ice_handle->data_stream = NULL;
2907
                                        ice_handle->data_id = 0;
2908
                                }
2909
                        }
2910
                        if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX) && !ice_handle->force_rtcp_mux && !janus_ice_is_rtcpmux_forced()) {
2911
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- rtcp-mux is supported by the browser, getting rid of RTCP components, if any...\n", ice_handle->handle_id);
2912
                                if(ice_handle->audio_stream && ice_handle->audio_stream->rtcp_component && ice_handle->audio_stream->components != NULL) {
2913
                                        nice_agent_attach_recv(ice_handle->agent, ice_handle->audio_id, 2, g_main_loop_get_context (ice_handle->iceloop), NULL, NULL);
2914
                                        /* Free the component */
2915
                                        janus_ice_component_free(ice_handle->audio_stream->components, ice_handle->audio_stream->rtcp_component);
2916
                                        ice_handle->audio_stream->rtcp_component = NULL;
2917
                                        /* Create a dummy candidate and enforce it as the one to use for this now unneeded component */
2918
                                        NiceCandidate *c = nice_candidate_new(NICE_CANDIDATE_TYPE_HOST);
2919
                                        c->component_id = 2;
2920
                                        c->stream_id = ice_handle->audio_stream->stream_id;
2921
#ifndef HAVE_LIBNICE_TCP
2922
                                        c->transport = NICE_CANDIDATE_TRANSPORT_UDP;
2923
#endif
2924
                                        strncpy(c->foundation, "1", NICE_CANDIDATE_MAX_FOUNDATION);
2925
                                        c->priority = 1;
2926
                                        nice_address_set_from_string(&c->addr, "127.0.0.1");
2927
                                        nice_address_set_port(&c->addr, janus_ice_get_rtcpmux_blackhole_port());
2928
                                        c->username = g_strdup(ice_handle->audio_stream->ruser);
2929
                                        c->password = g_strdup(ice_handle->audio_stream->rpass);
2930
                                        if(!nice_agent_set_selected_remote_candidate(ice_handle->agent, ice_handle->audio_stream->stream_id, 2, c)) {
2931
                                                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);
2932
                                                nice_candidate_free(c);
2933
                                        }
2934
                                }
2935
                                if(ice_handle->video_stream && ice_handle->video_stream->rtcp_component && ice_handle->video_stream->components != NULL) {
2936
                                        nice_agent_attach_recv(ice_handle->agent, ice_handle->video_id, 2, g_main_loop_get_context (ice_handle->iceloop), NULL, NULL);
2937
                                        /* Free the component */
2938
                                        janus_ice_component_free(ice_handle->video_stream->components, ice_handle->video_stream->rtcp_component);
2939
                                        ice_handle->video_stream->rtcp_component = NULL;
2940
                                        /* Create a dummy candidate and enforce it as the one to use for this now unneeded component */
2941
                                        NiceCandidate *c = nice_candidate_new(NICE_CANDIDATE_TYPE_HOST);
2942
                                        c->component_id = 2;
2943
                                        c->stream_id = ice_handle->video_stream->stream_id;
2944
#ifndef HAVE_LIBNICE_TCP
2945
                                        c->transport = NICE_CANDIDATE_TRANSPORT_UDP;
2946
#endif
2947
                                        strncpy(c->foundation, "1", NICE_CANDIDATE_MAX_FOUNDATION);
2948
                                        c->priority = 1;
2949
                                        nice_address_set_from_string(&c->addr, "127.0.0.1");
2950
                                        nice_address_set_port(&c->addr, janus_ice_get_rtcpmux_blackhole_port());
2951
                                        c->username = g_strdup(ice_handle->video_stream->ruser);
2952
                                        c->password = g_strdup(ice_handle->video_stream->rpass);
2953
                                        if(!nice_agent_set_selected_remote_candidate(ice_handle->agent, ice_handle->video_stream->stream_id, 2, c)) {
2954
                                                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);
2955
                                                nice_candidate_free(c);
2956
                                        }
2957
                                }
2958
                        }
2959
                        janus_mutex_lock(&ice_handle->mutex);
2960
                        /* We got our answer */
2961
                        janus_flags_clear(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
2962
                        /* Any pending trickles? */
2963
                        if(ice_handle->pending_trickles) {
2964
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Processing %d pending trickle candidates\n", ice_handle->handle_id, g_list_length(ice_handle->pending_trickles));
2965
                                GList *temp = NULL;
2966
                                while(ice_handle->pending_trickles) {
2967
                                        temp = g_list_first(ice_handle->pending_trickles);
2968
                                        ice_handle->pending_trickles = g_list_remove_link(ice_handle->pending_trickles, temp);
2969
                                        janus_ice_trickle *trickle = (janus_ice_trickle *)temp->data;
2970
                                        g_list_free(temp);
2971
                                        if(trickle == NULL)
2972
                                                continue;
2973
                                        if((janus_get_monotonic_time() - trickle->received) > 45*G_USEC_PER_SEC) {
2974
                                                /* FIXME Candidate is too old, discard it */
2975
                                                janus_ice_trickle_destroy(trickle);
2976
                                                /* FIXME We should report that */
2977
                                                continue;
2978
                                        }
2979
                                        json_t *candidate = trickle->candidate;
2980
                                        if(candidate == NULL) {
2981
                                                janus_ice_trickle_destroy(trickle);
2982
                                                continue;
2983
                                        }
2984
                                        if(json_is_object(candidate)) {
2985
                                                /* We got a single candidate */
2986
                                                int error = 0;
2987
                                                const char *error_string = NULL;
2988
                                                if((error = janus_ice_trickle_parse(ice_handle, candidate, &error_string)) != 0) {
2989
                                                        /* FIXME We should report the error parsing the trickle candidate */
2990
                                                }
2991
                                        } else if(json_is_array(candidate)) {
2992
                                                /* We got multiple candidates in an array */
2993
                                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Got multiple candidates (%zu)\n", ice_handle->handle_id, json_array_size(candidate));
2994
                                                if(json_array_size(candidate) > 0) {
2995
                                                        /* Handle remote candidates */
2996
                                                        size_t i = 0;
2997
                                                        for(i=0; i<json_array_size(candidate); i++) {
2998
                                                                json_t *c = json_array_get(candidate, i);
2999
                                                                /* FIXME We don't care if any trickle fails to parse */
3000
                                                                janus_ice_trickle_parse(ice_handle, c, NULL);
3001
                                                        }
3002
                                                }
3003
                                        }
3004
                                        /* Done, free candidate */
3005
                                        janus_ice_trickle_destroy(trickle);
3006
                                }
3007
                        }
3008
                        /* This was an answer, check if it's time to start ICE */
3009
                        if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE) &&
3010
                                        !janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALL_TRICKLES)) {
3011
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- ICE Trickling is supported by the browser, waiting for remote candidates...\n", ice_handle->handle_id);
3012
                                janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_START);
3013
                        } else {
3014
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Done! Sending connectivity checks...\n", ice_handle->handle_id);
3015
                                if(ice_handle->audio_id > 0) {
3016
                                        janus_ice_setup_remote_candidates(ice_handle, ice_handle->audio_id, 1);
3017
                                        if(!janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX))        /* http://tools.ietf.org/html/rfc5761#section-5.1.3 */
3018
                                                janus_ice_setup_remote_candidates(ice_handle, ice_handle->audio_id, 2);
3019
                                }
3020
                                if(ice_handle->video_id > 0) {
3021
                                        janus_ice_setup_remote_candidates(ice_handle, ice_handle->video_id, 1);
3022
                                        if(!janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX))        /* http://tools.ietf.org/html/rfc5761#section-5.1.3 */
3023
                                                janus_ice_setup_remote_candidates(ice_handle, ice_handle->video_id, 2);
3024
                                }
3025
                                if(ice_handle->data_id > 0) {
3026
                                        janus_ice_setup_remote_candidates(ice_handle, ice_handle->data_id, 1);
3027
                                }
3028
                        }
3029
                        janus_mutex_unlock(&ice_handle->mutex);
3030
                }
3031
        }
3032

    
3033
        /* Prepare JSON event */
3034
        json_t *jsep = json_object();
3035
        json_object_set_new(jsep, "type", json_string(sdp_type));
3036
        json_object_set_new(jsep, "sdp", json_string(sdp_merged));
3037
        ice_handle->local_sdp = sdp_merged;
3038
        return jsep;
3039
}
3040

    
3041
void janus_plugin_relay_rtp(janus_plugin_session *plugin_session, int video, char *buf, int len) {
3042
        if((plugin_session < (janus_plugin_session *)0x1000) || plugin_session->stopped || buf == NULL || len < 1)
3043
                return;
3044
        janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
3045
        if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
3046
                        || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
3047
                return;
3048
        janus_ice_relay_rtp(handle, video, buf, len);
3049
}
3050

    
3051
void janus_plugin_relay_rtcp(janus_plugin_session *plugin_session, int video, char *buf, int len) {
3052
        if((plugin_session < (janus_plugin_session *)0x1000) || plugin_session->stopped || buf == NULL || len < 1)
3053
                return;
3054
        janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
3055
        if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
3056
                        || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
3057
                return;
3058
        janus_ice_relay_rtcp(handle, video, buf, len);
3059
}
3060

    
3061
void janus_plugin_relay_data(janus_plugin_session *plugin_session, char *buf, int len) {
3062
        if((plugin_session < (janus_plugin_session *)0x1000) || plugin_session->stopped || buf == NULL || len < 1)
3063
                return;
3064
        janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
3065
        if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
3066
                        || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
3067
                return;
3068
#ifdef HAVE_SCTP
3069
        janus_ice_relay_data(handle, buf, len);
3070
#else
3071
        JANUS_LOG(LOG_WARN, "Asked to relay data, but Data Channels support has not been compiled...\n");
3072
#endif
3073
}
3074

    
3075
void janus_plugin_close_pc(janus_plugin_session *plugin_session) {
3076
        /* A plugin asked to get rid of a PeerConnection */
3077
        if((plugin_session < (janus_plugin_session *)0x1000) || !janus_plugin_session_is_alive(plugin_session) || plugin_session->stopped)
3078
                return;
3079
        janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
3080
        if(!ice_handle)
3081
                return;
3082
        if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
3083
                        || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
3084
                return;
3085
        janus_session *session = (janus_session *)ice_handle->session;
3086
        if(!session)
3087
                return;
3088

    
3089
        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Plugin asked to hangup PeerConnection: sending alert\n", ice_handle->handle_id);
3090
        /* Send an alert on all the DTLS connections */
3091
        janus_ice_webrtc_hangup(ice_handle, "Close PC");
3092
}
3093

    
3094
void janus_plugin_end_session(janus_plugin_session *plugin_session) {
3095
        /* A plugin asked to get rid of a handle */
3096
        if((plugin_session < (janus_plugin_session *)0x1000) || !janus_plugin_session_is_alive(plugin_session) || plugin_session->stopped)
3097
                return;
3098
        janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
3099
        if(!ice_handle)
3100
                return;
3101
        janus_session *session = (janus_session *)ice_handle->session;
3102
        if(!session)
3103
                return;
3104
        /* Destroy the handle */
3105
        janus_ice_handle_destroy(session, ice_handle->handle_id);
3106
        janus_mutex_lock(&session->mutex);
3107
        g_hash_table_remove(session->ice_handles, &ice_handle->handle_id);
3108
        janus_mutex_unlock(&session->mutex);
3109
}
3110

    
3111
void janus_plugin_notify_event(janus_plugin *plugin, janus_plugin_session *plugin_session, json_t *event) {
3112
        /* A plugin asked to notify an event to the handlers */
3113
        if(!plugin || !event || !json_is_object(event))
3114
                return;
3115
        guint64 session_id = 0, handle_id = 0;
3116
        if(plugin_session != NULL) {
3117
                if((plugin_session < (janus_plugin_session *)0x1000) || !janus_plugin_session_is_alive(plugin_session) || plugin_session->stopped) {
3118
                        json_decref(event);
3119
                        return;
3120
                }
3121
                janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
3122
                if(!ice_handle) {
3123
                        json_decref(event);
3124
                        return;
3125
                }
3126
                handle_id = ice_handle->handle_id;
3127
                janus_session *session = (janus_session *)ice_handle->session;
3128
                if(!session) {
3129
                        json_decref(event);
3130
                        return;
3131
                }
3132
                session_id = session->session_id;
3133
        }
3134
        /* Notify event handlers */
3135
        if(janus_events_is_enabled()) {
3136
                janus_events_notify_handlers(JANUS_EVENT_TYPE_PLUGIN,
3137
                        session_id, handle_id, plugin->get_package(), event);
3138
        } else {
3139
                json_decref(event);
3140
        }
3141
}
3142

    
3143

    
3144
/* Main */
3145
gint main(int argc, char *argv[])
3146
{
3147
        /* Core dumps may be disallowed by parent of this process; change that */
3148
        struct rlimit core_limits;
3149
        core_limits.rlim_cur = core_limits.rlim_max = RLIM_INFINITY;
3150
        setrlimit(RLIMIT_CORE, &core_limits);
3151

    
3152
        g_print("Janus commit: %s\n", janus_build_git_sha);
3153
        g_print("Compiled on:  %s\n\n", janus_build_git_time);
3154

    
3155
        struct gengetopt_args_info args_info;
3156
        /* Let's call our cmdline parser */
3157
        if(cmdline_parser(argc, argv, &args_info) != 0)
3158
                exit(1);
3159

    
3160
        /* Any configuration to open? */
3161
        if(args_info.config_given) {
3162
                config_file = g_strdup(args_info.config_arg);
3163
        }
3164
        if(args_info.configs_folder_given) {
3165
                configs_folder = g_strdup(args_info.configs_folder_arg);
3166
        } else {
3167
                configs_folder = g_strdup (CONFDIR);
3168
        }
3169
        if(config_file == NULL) {
3170
                char file[255];
3171
                g_snprintf(file, 255, "%s/janus.cfg", configs_folder);
3172
                config_file = g_strdup(file);
3173
        }
3174
        if((config = janus_config_parse(config_file)) == NULL) {
3175
                if(args_info.config_given) {
3176
                        /* We only give up if the configuration file was explicitly provided */
3177
                        g_print("Error reading configuration from %s\n", config_file);
3178
                        exit(1);
3179
                }
3180
                g_print("Error reading/parsing the configuration file, going on with the defaults and the command line arguments\n");
3181
                config = janus_config_create("janus.cfg");
3182
                if(config == NULL) {
3183
                        /* If we can't even create an empty configuration, something's definitely wrong */
3184
                        exit(1);
3185
                }
3186
        }
3187

    
3188
        /* Check if we need to log to console and/or file */
3189
        gboolean use_stdout = TRUE;
3190
        if(args_info.disable_stdout_given) {
3191
                use_stdout = FALSE;
3192
                janus_config_add_item(config, "general", "log_to_stdout", "no");
3193
        } else {
3194
                /* Check if the configuration file is saying anything about this */
3195
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "log_to_stdout");
3196
                if(item && item->value && !janus_is_true(item->value))
3197
                        use_stdout = FALSE;
3198
        }
3199
        const char *logfile = NULL;
3200
        if(args_info.log_file_given) {
3201
                logfile = args_info.log_file_arg;
3202
                janus_config_add_item(config, "general", "log_to_file", "no");
3203
        } else {
3204
                /* Check if the configuration file is saying anything about this */
3205
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "log_to_file");
3206
                if(item && item->value)
3207
                        logfile = item->value;
3208
        }
3209

    
3210
        /* Check if we're going to daemonize Janus */
3211
        if(args_info.daemon_given) {
3212
                daemonize = TRUE;
3213
                janus_config_add_item(config, "general", "daemonize", "yes");
3214
        } else {
3215
                /* Check if the configuration file is saying anything about this */
3216
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "daemonize");
3217
                if(item && item->value && janus_is_true(item->value))
3218
                        daemonize = TRUE;
3219
        }
3220
        /* If we're going to daemonize, make sure logging to stdout is disabled and a log file has been specified */
3221
        if(daemonize && use_stdout) {
3222
                use_stdout = FALSE;
3223
        }
3224
        if(daemonize && logfile == NULL) {
3225
                g_print("Running Janus as a daemon but no log file provided, giving up...\n");
3226
                exit(1);
3227
        }
3228
        /* Daemonize now, if we need to */
3229
        if(daemonize) {
3230
                g_print("Running Janus as a daemon\n");
3231

    
3232
                /* Create a pipe for parent<->child communication during the startup phase */
3233
                if(pipe(pipefd) == -1) {
3234
                        g_print("pipe error!\n");
3235
                        exit(1);
3236
                }
3237

    
3238
                /* Fork off the parent process */
3239
                pid_t pid = fork();
3240
                if(pid < 0) {
3241
                        g_print("Fork error!\n");
3242
                        exit(1);
3243
                }
3244
                if(pid > 0) {
3245
                        /* Ok, we're the parent: let's wait for the child to tell us everything started fine */
3246
                        close(pipefd[1]);
3247
                        int code = -1;
3248
                        struct pollfd pollfds;
3249

    
3250
                        while(code < 0) {
3251
                                pollfds.fd = pipefd[0];
3252
                                pollfds.events = POLLIN;
3253
                                int res = poll(&pollfds, 1, -1);
3254
                                if(res < 0)
3255
                                        break;
3256
                                if(res == 0)
3257
                                        continue;
3258
                                if(pollfds.revents & POLLERR || pollfds.revents & POLLHUP)
3259
                                        break;
3260
                                if(pollfds.revents & POLLIN) {
3261
                                        res = read(pipefd[0], &code, sizeof(int));
3262
                                        break;
3263
                                }
3264
                        }
3265
                        if(code < 0)
3266
                                code = 1;
3267

    
3268
                        /* Leave the parent and return the exit code we received from the child */
3269
                        if(code)
3270
                                g_print("Error launching Janus (error code %d), check the logs for more details\n", code);
3271
                        exit(code);
3272
                }
3273
                /* Change the file mode mask */
3274
                umask(0);
3275

    
3276
                /* Create a new SID for the child process */
3277
                pid_t sid = setsid();
3278
                if(sid < 0) {
3279
                        g_print("Error setting SID!\n");
3280
                        exit(1);
3281
                }
3282
                /* Change the current working directory */
3283
                if((chdir("/")) < 0) {
3284
                        g_print("Error changing the current working directory!\n");
3285
                        exit(1);
3286
                }
3287
                /* We close stdin/stdout/stderr when initializing the logger */
3288
        }
3289

    
3290
        /* Initialize logger */
3291
        if(janus_log_init(daemonize, use_stdout, logfile) < 0)
3292
                exit(1);
3293

    
3294
        JANUS_PRINT("---------------------------------------------------\n");
3295
        JANUS_PRINT("  Starting Meetecho Janus (WebRTC Gateway) v%s\n", JANUS_VERSION_STRING);
3296
        JANUS_PRINT("---------------------------------------------------\n\n");
3297

    
3298
        /* Handle SIGINT (CTRL-C), SIGTERM (from service managers) */
3299
        signal(SIGINT, janus_handle_signal);
3300
        signal(SIGTERM, janus_handle_signal);
3301
        atexit(janus_termination_handler);
3302

    
3303
        /* Setup Glib */
3304
#if !GLIB_CHECK_VERSION(2, 36, 0)
3305
        g_type_init();
3306
#endif
3307

    
3308
        /* Logging level: default is info and no timestamps */
3309
        janus_log_level = LOG_INFO;
3310
        janus_log_timestamps = FALSE;
3311
        janus_log_colors = TRUE;
3312
        if(args_info.debug_level_given) {
3313
                if(args_info.debug_level_arg < LOG_NONE)
3314
                        args_info.debug_level_arg = 0;
3315
                else if(args_info.debug_level_arg > LOG_MAX)
3316
                        args_info.debug_level_arg = LOG_MAX;
3317
                janus_log_level = args_info.debug_level_arg;
3318
        }
3319

    
3320
        /* Any PID we need to create? */
3321
        const char *pidfile = NULL;
3322
        if(args_info.pid_file_given) {
3323
                pidfile = args_info.pid_file_arg;
3324
                janus_config_add_item(config, "general", "pid_file", pidfile);
3325
        } else {
3326
                /* Check if the configuration file is saying anything about this */
3327
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "pid_file");
3328
                if(item && item->value)
3329
                        pidfile = item->value;
3330
        }
3331
        if(janus_pidfile_create(pidfile) < 0)
3332
                exit(1);
3333

    
3334
        /* Proceed with the rest of the configuration */
3335
        janus_config_print(config);
3336
        if(args_info.debug_level_given) {
3337
                char debug[5];
3338
                g_snprintf(debug, 5, "%d", args_info.debug_level_arg);
3339
                janus_config_add_item(config, "general", "debug_level", debug);
3340
        } else {
3341
                /* No command line directive on logging, try the configuration file */
3342
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "debug_level");
3343
                if(item && item->value) {
3344
                        int temp_level = atoi(item->value);
3345
                        if(temp_level == 0 && strcmp(item->value, "0")) {
3346
                                JANUS_PRINT("Invalid debug level %s (configuration), using default (info=4)\n", item->value);
3347
                        } else {
3348
                                janus_log_level = temp_level;
3349
                                if(janus_log_level < LOG_NONE)
3350
                                        janus_log_level = 0;
3351
                                else if(janus_log_level > LOG_MAX)
3352
                                        janus_log_level = LOG_MAX;
3353
                        }
3354
                }
3355
        }
3356
        /* Any command line argument that should overwrite the configuration? */
3357
        JANUS_PRINT("Checking command line arguments...\n");
3358
        if(args_info.debug_timestamps_given) {
3359
                janus_config_add_item(config, "general", "debug_timestamps", "yes");
3360
        }
3361
        if(args_info.disable_colors_given) {
3362
                janus_config_add_item(config, "general", "debug_colors", "no");
3363
        }
3364
        if(args_info.server_name_given) {
3365
                janus_config_add_item(config, "general", "server_name", args_info.server_name_arg);
3366
        }
3367
        if(args_info.session_timeout_given) {
3368
                char st[20];
3369
                g_snprintf(st, 20, "%d", args_info.session_timeout_arg);
3370
                janus_config_add_item(config, "general", "session_timeout", st);
3371
        }
3372
         if(args_info.interface_given) {
3373
                janus_config_add_item(config, "general", "interface", args_info.interface_arg);
3374
        }
3375
        if(args_info.configs_folder_given) {
3376
                janus_config_add_item(config, "general", "configs_folder", args_info.configs_folder_arg);
3377
        }
3378
        if(args_info.plugins_folder_given) {
3379
                janus_config_add_item(config, "general", "plugins_folder", args_info.plugins_folder_arg);
3380
        }
3381
        if(args_info.apisecret_given) {
3382
                janus_config_add_item(config, "general", "api_secret", args_info.apisecret_arg);
3383
        }
3384
        if(args_info.token_auth_given) {
3385
                janus_config_add_item(config, "general", "token_auth", "yes");
3386
        }
3387
        if(args_info.cert_pem_given) {
3388
                janus_config_add_item(config, "certificates", "cert_pem", args_info.cert_pem_arg);
3389
        }
3390
        if(args_info.cert_key_given) {
3391
                janus_config_add_item(config, "certificates", "cert_key", args_info.cert_key_arg);
3392
        }
3393
        if(args_info.stun_server_given) {
3394
                /* Split in server and port (if port missing, use 3478 as default) */
3395
                char *stunport = strrchr(args_info.stun_server_arg, ':');
3396
                if(stunport != NULL) {
3397
                        *stunport = '\0';
3398
                        stunport++;
3399
                        janus_config_add_item(config, "nat", "stun_server", args_info.stun_server_arg);
3400
                        janus_config_add_item(config, "nat", "stun_port", stunport);
3401
                } else {
3402
                        janus_config_add_item(config, "nat", "stun_server", args_info.stun_server_arg);
3403
                        janus_config_add_item(config, "nat", "stun_port", "3478");
3404
                }
3405
        }
3406
        if(args_info.nat_1_1_given) {
3407
                janus_config_add_item(config, "nat", "nat_1_1_mapping", args_info.nat_1_1_arg);
3408
        }
3409
        if(args_info.ice_enforce_list_given) {
3410
                janus_config_add_item(config, "nat", "ice_enforce_list", args_info.ice_enforce_list_arg);
3411
        }
3412
        if(args_info.ice_ignore_list_given) {
3413
                janus_config_add_item(config, "nat", "ice_ignore_list", args_info.ice_ignore_list_arg);
3414
        }
3415
        if(args_info.libnice_debug_given) {
3416
                janus_config_add_item(config, "nat", "nice_debug", "true");
3417
        }
3418
        if(args_info.ice_lite_given) {
3419
                janus_config_add_item(config, "nat", "ice_lite", "true");
3420
        }
3421
        if(args_info.ice_tcp_given) {
3422
                janus_config_add_item(config, "nat", "ice_tcp", "true");
3423
        }
3424
        if(args_info.ipv6_candidates_given) {
3425
                janus_config_add_item(config, "media", "ipv6", "true");
3426
        }
3427
        if(args_info.force_bundle_given) {
3428
                janus_config_add_item(config, "media", "force-bundle", "true");
3429
        }
3430
        if(args_info.force_rtcp_mux_given) {
3431
                janus_config_add_item(config, "media", "force-rtcp-mux", "true");
3432
        }
3433
        if(args_info.max_nack_queue_given) {
3434
                char mnq[20];
3435
                g_snprintf(mnq, 20, "%d", args_info.max_nack_queue_arg);
3436
                janus_config_add_item(config, "media", "max_nack_queue", mnq);
3437
        }
3438
        if(args_info.no_media_timer_given) {
3439
                char nmt[20];
3440
                g_snprintf(nmt, 20, "%d", args_info.no_media_timer_arg);
3441
                janus_config_add_item(config, "media", "no_media_timer", nmt);
3442
        }
3443
        if(args_info.rtp_port_range_given) {
3444
                janus_config_add_item(config, "media", "rtp_port_range", args_info.rtp_port_range_arg);
3445
        }
3446
        if(args_info.event_handlers_given) {
3447
                janus_config_add_item(config, "events", "broadcast", "yes");
3448
        }
3449
        janus_config_print(config);
3450

    
3451
        /* Logging/debugging */
3452
        JANUS_PRINT("Debug/log level is %d\n", janus_log_level);
3453
        janus_config_item *item = janus_config_get_item_drilldown(config, "general", "debug_timestamps");
3454
        if(item && item->value)
3455
                janus_log_timestamps = janus_is_true(item->value);
3456
        JANUS_PRINT("Debug/log timestamps are %s\n", janus_log_timestamps ? "enabled" : "disabled");
3457
        item = janus_config_get_item_drilldown(config, "general", "debug_colors");
3458
        if(item && item->value)
3459
                janus_log_colors = janus_is_true(item->value);
3460
        JANUS_PRINT("Debug/log colors are %s\n", janus_log_colors ? "enabled" : "disabled");
3461

    
3462
        /* Any IP/interface to enforce/ignore? */
3463
        item = janus_config_get_item_drilldown(config, "nat", "ice_enforce_list");
3464
        if(item && item->value) {
3465
                gchar **list = g_strsplit(item->value, ",", -1);
3466
                gchar *index = list[0];
3467
                if(index != NULL) {
3468
                        int i=0;
3469
                        while(index != NULL) {
3470
                                if(strlen(index) > 0) {
3471
                                        JANUS_LOG(LOG_INFO, "Adding '%s' to the ICE enforce list...\n", index);
3472
                                        janus_ice_enforce_interface(g_strdup(index));
3473
                                }
3474
                                i++;
3475
                                index = list[i];
3476
                        }
3477
                }
3478
                g_clear_pointer(&list, g_strfreev);
3479
        }
3480
        item = janus_config_get_item_drilldown(config, "nat", "ice_ignore_list");
3481
        if(item && item->value) {
3482
                gchar **list = g_strsplit(item->value, ",", -1);
3483
                gchar *index = list[0];
3484
                if(index != NULL) {
3485
                        int i=0;
3486
                        while(index != NULL) {
3487
                                if(strlen(index) > 0) {
3488
                                        JANUS_LOG(LOG_INFO, "Adding '%s' to the ICE ignore list...\n", index);
3489
                                        janus_ice_ignore_interface(g_strdup(index));
3490
                                }
3491
                                i++;
3492
                                index = list[i];
3493
                        }
3494
                }
3495
                g_clear_pointer(&list, g_strfreev);
3496
        }
3497
        /* What is the local IP? */
3498
        JANUS_LOG(LOG_VERB, "Selecting local IP address...\n");
3499
        item = janus_config_get_item_drilldown(config, "general", "interface");
3500
        if(item && item->value) {
3501
                JANUS_LOG(LOG_VERB, "  -- Will try to use %s\n", item->value);
3502
                /* Verify that the address is valid */
3503
                struct ifaddrs *ifas = NULL;
3504
                janus_network_address iface;
3505
                janus_network_address_string_buffer ibuf;
3506
                if(getifaddrs(&ifas) || ifas == NULL) {
3507
                        JANUS_LOG(LOG_ERR, "Unable to acquire list of network devices/interfaces; some configurations may not work as expected...\n");
3508
                } else {
3509
                        if(janus_network_lookup_interface(ifas, item->value, &iface) != 0) {
3510
                                JANUS_LOG(LOG_WARN, "Error setting local IP address to %s, falling back to detecting IP address...\n", item->value);
3511
                        } else {
3512
                                if(janus_network_address_to_string_buffer(&iface, &ibuf) != 0 || janus_network_address_string_buffer_is_null(&ibuf)) {
3513
                                        JANUS_LOG(LOG_WARN, "Error getting local IP address from %s, falling back to detecting IP address...\n", item->value);
3514
                                } else {
3515
                                        local_ip = g_strdup(janus_network_address_string_from_buffer(&ibuf));
3516
                                }
3517
                        }
3518
                }
3519
        }
3520
        if(local_ip == NULL) {
3521
                local_ip = janus_network_detect_local_ip_as_string(janus_network_query_options_any_ip);
3522
                if(local_ip == NULL) {
3523
                        JANUS_LOG(LOG_WARN, "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");
3524
                        local_ip = g_strdup("127.0.0.1");
3525
                }
3526
        }
3527
        JANUS_LOG(LOG_INFO, "Using %s as local IP...\n", local_ip);
3528

    
3529
        /* Was a custom instance name provided? */
3530
        item = janus_config_get_item_drilldown(config, "general", "server_name");
3531
        if(item && item->value) {
3532
                server_name = g_strdup(item->value);
3533
        }
3534

    
3535
        /* Check if a custom session timeout value was specified */
3536
        item = janus_config_get_item_drilldown(config, "general", "session_timeout");
3537
        if(item && item->value) {
3538
                int st = atoi(item->value);
3539
                if(st < 0) {
3540
                        JANUS_LOG(LOG_WARN, "Ignoring session_timeout value as it's not a positive integer\n");
3541
                } else {
3542
                        if(st == 0) {
3543
                                JANUS_LOG(LOG_WARN, "Session timeouts have been disabled (note, may result in orphaned sessions)\n");
3544
                        }
3545
                        session_timeout = st;
3546
                }
3547
        }
3548

    
3549
        /* Is there any API secret to consider? */
3550
        api_secret = NULL;
3551
        item = janus_config_get_item_drilldown(config, "general", "api_secret");
3552
        if(item && item->value) {
3553
                api_secret = g_strdup(item->value);
3554
        }
3555
        /* Is there any API secret to consider? */
3556
        admin_api_secret = NULL;
3557
        item = janus_config_get_item_drilldown(config, "general", "admin_secret");
3558
        if(item && item->value) {
3559
                admin_api_secret = g_strdup(item->value);
3560
        }
3561
        /* Also check if the token based authentication mechanism needs to be enabled */
3562
        item = janus_config_get_item_drilldown(config, "general", "token_auth");
3563
        janus_auth_init(item && item->value && janus_is_true(item->value));
3564

    
3565
        /* Initialize the recorder code */
3566
        item = janus_config_get_item_drilldown(config, "general", "recordings_tmp_ext");
3567
        if(item && item->value) {
3568
                janus_recorder_init(TRUE, item->value);
3569
        } else {
3570
                janus_recorder_init(FALSE, NULL);
3571
        }
3572

    
3573
        /* Setup ICE stuff (e.g., checking if the provided STUN server is correct) */
3574
        char *stun_server = NULL, *turn_server = NULL;
3575
        uint16_t stun_port = 0, turn_port = 0;
3576
        char *turn_type = NULL, *turn_user = NULL, *turn_pwd = NULL;
3577
        char *turn_rest_api = NULL, *turn_rest_api_key = NULL;
3578
#ifdef HAVE_LIBCURL
3579
        char *turn_rest_api_method = NULL;
3580
#endif
3581
        const char *nat_1_1_mapping = NULL;
3582
        uint16_t rtp_min_port = 0, rtp_max_port = 0;
3583
        gboolean ice_lite = FALSE, ice_tcp = FALSE, ipv6 = FALSE;
3584
        item = janus_config_get_item_drilldown(config, "media", "ipv6");
3585
        ipv6 = (item && item->value) ? janus_is_true(item->value) : FALSE;
3586
        item = janus_config_get_item_drilldown(config, "media", "rtp_port_range");
3587
        if(item && item->value) {
3588
                /* Split in min and max port */
3589
                char *maxport = strrchr(item->value, '-');
3590
                if(maxport != NULL) {
3591
                        *maxport = '\0';
3592
                        maxport++;
3593
                        rtp_min_port = atoi(item->value);
3594
                        rtp_max_port = atoi(maxport);
3595
                        maxport--;
3596
                        *maxport = '-';
3597
                }
3598
                if(rtp_min_port > rtp_max_port) {
3599
                        int temp_port = rtp_min_port;
3600
                        rtp_min_port = rtp_max_port;
3601
                        rtp_max_port = temp_port;
3602
                }
3603
                if(rtp_max_port == 0)
3604
                        rtp_max_port = 65535;
3605
                JANUS_LOG(LOG_INFO, "RTP port range: %u -- %u\n", rtp_min_port, rtp_max_port);
3606
        }
3607
        /* Check if we need to enable the ICE Lite mode */
3608
        item = janus_config_get_item_drilldown(config, "nat", "ice_lite");
3609
        ice_lite = (item && item->value) ? janus_is_true(item->value) : FALSE;
3610
        /* Check if we need to enable ICE-TCP support (warning: still broken, for debugging only) */
3611
        item = janus_config_get_item_drilldown(config, "nat", "ice_tcp");
3612
        ice_tcp = (item && item->value) ? janus_is_true(item->value) : FALSE;
3613
        /* Any STUN server to use in Janus? */
3614
        item = janus_config_get_item_drilldown(config, "nat", "stun_server");
3615
        if(item && item->value)
3616
                stun_server = (char *)item->value;
3617
        item = janus_config_get_item_drilldown(config, "nat", "stun_port");
3618
        if(item && item->value)
3619
                stun_port = atoi(item->value);
3620
        /* Any 1:1 NAT mapping to take into account? */
3621
        item = janus_config_get_item_drilldown(config, "nat", "nat_1_1_mapping");
3622
        if(item && item->value) {
3623
                JANUS_LOG(LOG_VERB, "Using nat_1_1_mapping for public ip - %s\n", item->value);
3624
                if(!janus_network_string_is_valid_address(janus_network_query_options_any_ip, item->value)) {
3625
                        JANUS_LOG(LOG_WARN, "Invalid nat_1_1_mapping address %s, disabling...\n", item->value);
3626
                } else {
3627
                        nat_1_1_mapping = item->value;
3628
                        janus_set_public_ip(item->value);
3629
                        janus_ice_enable_nat_1_1();
3630
                }
3631
        }
3632
        /* Any TURN server to use in Janus? */
3633
        item = janus_config_get_item_drilldown(config, "nat", "turn_server");
3634
        if(item && item->value)
3635
                turn_server = (char *)item->value;
3636
        item = janus_config_get_item_drilldown(config, "nat", "turn_port");
3637
        if(item && item->value)
3638
                turn_port = atoi(item->value);
3639
        item = janus_config_get_item_drilldown(config, "nat", "turn_type");
3640
        if(item && item->value)
3641
                turn_type = (char *)item->value;
3642
        item = janus_config_get_item_drilldown(config, "nat", "turn_user");
3643
        if(item && item->value)
3644
                turn_user = (char *)item->value;
3645
        item = janus_config_get_item_drilldown(config, "nat", "turn_pwd");
3646
        if(item && item->value)
3647
                turn_pwd = (char *)item->value;
3648
        /* Check if there's any TURN REST API backend to use */
3649
        item = janus_config_get_item_drilldown(config, "nat", "turn_rest_api");
3650
        if(item && item->value)
3651
                turn_rest_api = (char *)item->value;
3652
        item = janus_config_get_item_drilldown(config, "nat", "turn_rest_api_key");
3653
        if(item && item->value)
3654
                turn_rest_api_key = (char *)item->value;
3655
#ifdef HAVE_LIBCURL
3656
        item = janus_config_get_item_drilldown(config, "nat", "turn_rest_api_method");
3657
        if(item && item->value)
3658
                turn_rest_api_method = (char *)item->value;
3659
#endif
3660
        /* Initialize the ICE stack now */
3661
        janus_ice_init(ice_lite, ice_tcp, ipv6, rtp_min_port, rtp_max_port);
3662
        if(janus_ice_set_stun_server(stun_server, stun_port) < 0) {
3663
                JANUS_LOG(LOG_FATAL, "Invalid STUN address %s:%u\n", stun_server, stun_port);
3664
                exit(1);
3665
        }
3666
        if(janus_ice_set_turn_server(turn_server, turn_port, turn_type, turn_user, turn_pwd) < 0) {
3667
                JANUS_LOG(LOG_FATAL, "Invalid TURN address %s:%u\n", turn_server, turn_port);
3668
                exit(1);
3669
        }
3670
#ifndef HAVE_LIBCURL
3671
        if(turn_rest_api != NULL || turn_rest_api_key != NULL) {
3672
                JANUS_LOG(LOG_WARN, "A TURN REST API backend specified in the settings, but libcurl support has not been built\n");
3673
        }
3674
#else
3675
        if(janus_ice_set_turn_rest_api(turn_rest_api, turn_rest_api_key, turn_rest_api_method) < 0) {
3676
                JANUS_LOG(LOG_FATAL, "Invalid TURN REST API configuration: %s (%s, %s)\n", turn_rest_api, turn_rest_api_key, turn_rest_api_method);
3677
                exit(1);
3678
        }
3679
#endif
3680
        item = janus_config_get_item_drilldown(config, "nat", "nice_debug");
3681
        if(item && item->value && janus_is_true(item->value)) {
3682
                /* Enable libnice debugging */
3683
                janus_ice_debugging_enable();
3684
        }
3685
        if(stun_server == NULL && turn_server == NULL) {
3686
                /* No STUN and TURN server provided for Janus: make sure it isn't on a private address */
3687
                gboolean private_address = FALSE;
3688
                const char *test_ip = nat_1_1_mapping ? nat_1_1_mapping : local_ip;
3689
                janus_network_address addr;
3690
                if(janus_network_string_to_address(janus_network_query_options_any_ip, test_ip, &addr) != 0) {
3691
                        JANUS_LOG(LOG_ERR, "Invalid address %s..?\n", test_ip);
3692
                } else {
3693
                        if(addr.family == AF_INET) {
3694
                                unsigned short int ip[4];
3695
                                sscanf(test_ip, "%hu.%hu.%hu.%hu", &ip[0], &ip[1], &ip[2], &ip[3]);
3696
                                if(ip[0] == 10) {
3697
                                        /* Class A private address */
3698
                                        private_address = TRUE;
3699
                                } else if(ip[0] == 172 && (ip[1] >= 16 && ip[1] <= 31)) {
3700
                                        /* Class B private address */
3701
                                        private_address = TRUE;
3702
                                } else if(ip[0] == 192 && ip[1] == 168) {
3703
                                        /* Class C private address */
3704
                                        private_address = TRUE;
3705
                                }
3706
                        } else {
3707
                                /* TODO Similar check for IPv6... */
3708
                        }
3709
                }
3710
                if(private_address) {
3711
                        JANUS_LOG(LOG_WARN, "Janus is deployed on a private address (%s) but you didn't specify any STUN server!"
3712
                                            " Expect trouble if this is supposed to work over the internet and not just in a LAN...\n", test_ip);
3713
                }
3714
        }
3715
        /* Are we going to force BUNDLE and/or rtcp-mux? */
3716
        gboolean force_bundle = FALSE, force_rtcpmux = FALSE;
3717
        item = janus_config_get_item_drilldown(config, "media", "force-bundle");
3718
        force_bundle = (item && item->value) ? janus_is_true(item->value) : FALSE;
3719
        janus_ice_force_bundle(force_bundle);
3720
        item = janus_config_get_item_drilldown(config, "media", "force-rtcp-mux");
3721
        force_rtcpmux = (item && item->value) ? janus_is_true(item->value) : FALSE;
3722
        janus_ice_force_rtcpmux(force_rtcpmux);
3723
        /* NACK related stuff */
3724
        item = janus_config_get_item_drilldown(config, "media", "max_nack_queue");
3725
        if(item && item->value) {
3726
                int mnq = atoi(item->value);
3727
                if(mnq < 0) {
3728
                        JANUS_LOG(LOG_WARN, "Ignoring max_nack_queue value as it's not a positive integer\n");
3729
                } else if(mnq > 0 && mnq < 200) {
3730
                        JANUS_LOG(LOG_WARN, "Ignoring max_nack_queue value as it's less than 200\n");
3731
                } else {
3732
                        janus_set_max_nack_queue(mnq);
3733
                }
3734
        }
3735
        /* no-media timer */
3736
        item = janus_config_get_item_drilldown(config, "media", "no_media_timer");
3737
        if(item && item->value) {
3738
                int nmt = atoi(item->value);
3739
                if(nmt < 0) {
3740
                        JANUS_LOG(LOG_WARN, "Ignoring no_media_timer value as it's not a positive integer\n");
3741
                } else {
3742
                        janus_set_no_media_timer(nmt);
3743
                }
3744
        }
3745

    
3746
        /* Setup OpenSSL stuff */
3747
        const char* server_pem;
3748
        item = janus_config_get_item_drilldown(config, "certificates", "cert_pem");
3749
        if(!item || !item->value) {
3750
                server_pem = NULL;
3751
        } else {
3752
                server_pem = item->value;
3753
        }
3754

    
3755
        const char* server_key;
3756
        item = janus_config_get_item_drilldown(config, "certificates", "cert_key");
3757
        if(!item || !item->value) {
3758
                server_key = NULL;
3759
        } else {
3760
                server_key = item->value;
3761
        }
3762
        JANUS_LOG(LOG_VERB, "Using certificates:\n\t%s\n\t%s\n", server_pem, server_key);
3763

    
3764
        SSL_library_init();
3765
        SSL_load_error_strings();
3766
        OpenSSL_add_all_algorithms();
3767
        /* ... and DTLS-SRTP in particular */
3768
        if(janus_dtls_srtp_init(server_pem, server_key) < 0) {
3769
                exit(1);
3770
        }
3771
        /* Check if there's any custom value for the starting MTU to use in the BIO filter */
3772
        item = janus_config_get_item_drilldown(config, "media", "dtls_mtu");
3773
        if(item && item->value)
3774
                janus_dtls_bio_filter_set_mtu(atoi(item->value));
3775

    
3776
#ifdef HAVE_SCTP
3777
        /* Initialize SCTP for DataChannels */
3778
        if(janus_sctp_init() < 0) {
3779
                exit(1);
3780
        }
3781
#else
3782
        JANUS_LOG(LOG_WARN, "Data Channels support not compiled\n");
3783
#endif
3784

    
3785
        /* Sessions */
3786
        sessions = g_hash_table_new_full(g_int64_hash, g_int64_equal, (GDestroyNotify)g_free, NULL);
3787
        old_sessions = g_hash_table_new_full(g_int64_hash, g_int64_equal, (GDestroyNotify)g_free, NULL);
3788
        janus_mutex_init(&sessions_mutex);
3789
        /* Start the sessions watchdog */
3790
        sessions_watchdog_context = g_main_context_new();
3791
        GMainLoop *watchdog_loop = g_main_loop_new(sessions_watchdog_context, FALSE);
3792
        GError *error = NULL;
3793
        GThread *watchdog = g_thread_try_new("sessions watchdog", &janus_sessions_watchdog, watchdog_loop, &error);
3794
        if(error != NULL) {
3795
                JANUS_LOG(LOG_FATAL, "Got error %d (%s) trying to start sessions watchdog...\n", error->code, error->message ? error->message : "??");
3796
                exit(1);
3797
        }
3798

    
3799
        /* Load event handlers */
3800
        const char *path = EVENTDIR;
3801
        item = janus_config_get_item_drilldown(config, "general", "events_folder");
3802
        if(item && item->value)
3803
                path = (char *)item->value;
3804
        JANUS_LOG(LOG_INFO, "Event handler plugins folder: %s\n", path);
3805
        DIR *dir = opendir(path);
3806
        if(!dir) {
3807
                /* Not really fatal, we don't care and go on anyway: event handlers are not fundamental */
3808
                JANUS_LOG(LOG_FATAL, "\tCouldn't access event handler plugins folder...\n");
3809
        } else {
3810
                /* Any event handlers to ignore? */
3811
                gchar **disabled_eventhandlers = NULL;
3812
                item = janus_config_get_item_drilldown(config, "events", "broadcast");
3813
                /* Event handlers are disabled by default: they need to be enabled in the configuration */
3814
                gboolean enable_events = FALSE;
3815
                if(item && item->value)
3816
                        enable_events = janus_is_true(item->value);
3817
                if(!enable_events) {
3818
                        JANUS_LOG(LOG_WARN, "Event handlers support disabled\n");
3819
                } else {
3820
                        item = janus_config_get_item_drilldown(config, "events", "disable");
3821
                        if(item && item->value)
3822
                                disabled_eventhandlers = g_strsplit(item->value, ",", -1);
3823
                        /* Open the shared objects */
3824
                        struct dirent *eventent = NULL;
3825
                        char eventpath[1024];
3826
                        while((eventent = readdir(dir))) {
3827
                                int len = strlen(eventent->d_name);
3828
                                if (len < 4) {
3829
                                        continue;
3830
                                }
3831
                                if (strcasecmp(eventent->d_name+len-strlen(SHLIB_EXT), SHLIB_EXT)) {
3832
                                        continue;
3833
                                }
3834
                                /* Check if this event handler has been disabled in the configuration file */
3835
                                if(disabled_eventhandlers != NULL) {
3836
                                        gchar *index = disabled_eventhandlers[0];
3837
                                        if(index != NULL) {
3838
                                                int i=0;
3839
                                                gboolean skip = FALSE;
3840
                                                while(index != NULL) {
3841
                                                        while(isspace(*index))
3842
                                                                index++;
3843
                                                        if(strlen(index) && !strcmp(index, eventent->d_name)) {
3844
                                                                JANUS_LOG(LOG_WARN, "Event handler plugin '%s' has been disabled, skipping...\n", eventent->d_name);
3845
                                                                skip = TRUE;
3846
                                                                break;
3847
                                                        }
3848
                                                        i++;
3849
                                                        index = disabled_eventhandlers[i];
3850
                                                }
3851
                                                if(skip)
3852
                                                        continue;
3853
                                        }
3854
                                }
3855
                                JANUS_LOG(LOG_INFO, "Loading event handler plugin '%s'...\n", eventent->d_name);
3856
                                memset(eventpath, 0, 1024);
3857
                                g_snprintf(eventpath, 1024, "%s/%s", path, eventent->d_name);
3858
                                void *event = dlopen(eventpath, RTLD_LAZY);
3859
                                if (!event) {
3860
                                        JANUS_LOG(LOG_ERR, "\tCouldn't load event handler plugin '%s': %s\n", eventent->d_name, dlerror());
3861
                                } else {
3862
                                        create_e *create = (create_e*) dlsym(event, "create");
3863
                                        const char *dlsym_error = dlerror();
3864
                                        if (dlsym_error) {
3865
                                                JANUS_LOG(LOG_ERR, "\tCouldn't load symbol 'create': %s\n", dlsym_error);
3866
                                                continue;
3867
                                        }
3868
                                        janus_eventhandler *janus_eventhandler = create();
3869
                                        if(!janus_eventhandler) {
3870
                                                JANUS_LOG(LOG_ERR, "\tCouldn't use function 'create'...\n");
3871
                                                continue;
3872
                                        }
3873
                                        /* Are all the mandatory methods and callbacks implemented? */
3874
                                        if(!janus_eventhandler->init || !janus_eventhandler->destroy ||
3875
                                                        !janus_eventhandler->get_api_compatibility ||
3876
                                                        !janus_eventhandler->get_version ||
3877
                                                        !janus_eventhandler->get_version_string ||
3878
                                                        !janus_eventhandler->get_description ||
3879
                                                        !janus_eventhandler->get_package ||
3880
                                                        !janus_eventhandler->get_name ||
3881
                                                        !janus_eventhandler->incoming_event) {
3882
                                                JANUS_LOG(LOG_ERR, "\tMissing some mandatory methods/callbacks, skipping this event handler plugin...\n");
3883
                                                continue;
3884
                                        }
3885
                                        if(janus_eventhandler->get_api_compatibility() < JANUS_EVENTHANDLER_API_VERSION) {
3886
                                                JANUS_LOG(LOG_ERR, "The '%s' event handler plugin was compiled against an older version of the API (%d < %d), skipping it: update it to enable it again\n",
3887
                                                        janus_eventhandler->get_package(), janus_eventhandler->get_api_compatibility(), JANUS_EVENTHANDLER_API_VERSION);
3888
                                                continue;
3889
                                        }
3890
                                        janus_eventhandler->init(configs_folder);
3891
                                        JANUS_LOG(LOG_VERB, "\tVersion: %d (%s)\n", janus_eventhandler->get_version(), janus_eventhandler->get_version_string());
3892
                                        JANUS_LOG(LOG_VERB, "\t   [%s] %s\n", janus_eventhandler->get_package(), janus_eventhandler->get_name());
3893
                                        JANUS_LOG(LOG_VERB, "\t   %s\n", janus_eventhandler->get_description());
3894
                                        JANUS_LOG(LOG_VERB, "\t   Plugin API version: %d\n", janus_eventhandler->get_api_compatibility());
3895
                                        JANUS_LOG(LOG_VERB, "\t   Subscriptions:");
3896
                                        if(janus_eventhandler->events_mask == 0) {
3897
                                                JANUS_LOG(LOG_VERB, " none");
3898
                                        } else {
3899
                                                if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_SESSION))
3900
                                                        JANUS_LOG(LOG_VERB, " sessions");
3901
                                                if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_HANDLE))
3902
                                                        JANUS_LOG(LOG_VERB, " handles");
3903
                                                if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_JSEP))
3904
                                                        JANUS_LOG(LOG_VERB, " jsep");
3905
                                                if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_WEBRTC))
3906
                                                        JANUS_LOG(LOG_VERB, " webrtc");
3907
                                                if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_MEDIA))
3908
                                                        JANUS_LOG(LOG_VERB, " media");
3909
                                                if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_PLUGIN))
3910
                                                        JANUS_LOG(LOG_VERB, " plugins");
3911
                                                if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_TRANSPORT))
3912
                                                        JANUS_LOG(LOG_VERB, " transports");
3913
                                        }
3914
                                        JANUS_LOG(LOG_VERB, "\n");
3915
                                        if(eventhandlers == NULL)
3916
                                                eventhandlers = g_hash_table_new(g_str_hash, g_str_equal);
3917
                                        g_hash_table_insert(eventhandlers, (gpointer)janus_eventhandler->get_package(), janus_eventhandler);
3918
                                        if(eventhandlers_so == NULL)
3919
                                                eventhandlers_so = g_hash_table_new(g_str_hash, g_str_equal);
3920
                                        g_hash_table_insert(eventhandlers_so, (gpointer)janus_eventhandler->get_package(), event);
3921
                                }
3922
                        }
3923
                }
3924
                closedir(dir);
3925
                if(disabled_eventhandlers != NULL)
3926
                        g_strfreev(disabled_eventhandlers);
3927
                disabled_eventhandlers = NULL;
3928
                /* Initialize the event broadcaster */
3929
                if(janus_events_init(enable_events, eventhandlers) < 0) {
3930
                        JANUS_LOG(LOG_FATAL, "Error initializing the Event handlers mechanism...\n");
3931
                        exit(1);
3932
                }
3933
        }
3934

    
3935
        /* Load plugins */
3936
        path = PLUGINDIR;
3937
        item = janus_config_get_item_drilldown(config, "general", "plugins_folder");
3938
        if(item && item->value)
3939
                path = (char *)item->value;
3940
        JANUS_LOG(LOG_INFO, "Plugins folder: %s\n", path);
3941
        dir = opendir(path);
3942
        if(!dir) {
3943
                JANUS_LOG(LOG_FATAL, "\tCouldn't access plugins folder...\n");