Statistics
| Branch: | Revision:

janus-gateway / janus.c @ 84c1291c

History | View | Annotate | Download (174 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 "cmdline.h"
30
#include "config.h"
31
#include "apierror.h"
32
#include "log.h"
33
#include "debug.h"
34
#include "rtcp.h"
35
#include "sdp.h"
36
#include "auth.h"
37
#include "events.h"
38

    
39

    
40
#define JANUS_NAME                                "Janus WebRTC Gateway"
41
#define JANUS_AUTHOR                        "Meetecho s.r.l."
42
#define JANUS_VERSION                        21
43
#define JANUS_VERSION_STRING        "0.2.1"
44
#define JANUS_SERVER_NAME                "MyJanusInstance"
45

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

    
52

    
53
static janus_config *config = NULL;
54
static char *config_file = NULL;
55
static char *configs_folder = NULL;
56

    
57
static GHashTable *transports = NULL;
58
static GHashTable *transports_so = NULL;
59

    
60
static GHashTable *eventhandlers = NULL;
61
static GHashTable *eventhandlers_so = NULL;
62

    
63
static GHashTable *plugins = NULL;
64
static GHashTable *plugins_so = NULL;
65

    
66

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

    
71

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

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

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

    
125
/* Admin/Monitor helpers */
126
json_t *janus_admin_stream_summary(janus_ice_stream *stream);
127
json_t *janus_admin_component_summary(janus_ice_component *component);
128

    
129

    
130
/* IP addresses */
131
static gchar local_ip[INET6_ADDRSTRLEN];
132
gchar *janus_get_local_ip(void) {
133
        return local_ip;
134
}
135
static gchar *public_ip = NULL;
136
gchar *janus_get_public_ip(void) {
137
        /* Fallback to the local IP, if we have no public one */
138
        return public_ip ? public_ip : local_ip;
139
}
140
void janus_set_public_ip(const char *ip) {
141
        /* once set do not override */
142
        if(ip == NULL || public_ip != NULL)
143
                return;
144
        public_ip = g_strdup(ip);
145
}
146
static volatile gint stop = 0;
147
static gint stop_signal = 0;
148
gint janus_is_stopping(void) {
149
        return g_atomic_int_get(&stop);
150
}
151

    
152

    
153
/* Public instance name */
154
static gchar *server_name = NULL;
155

    
156

    
157
/* Information */
158
json_t *janus_info(const char *transaction);
159
json_t *janus_info(const char *transaction) {
160
        /* Prepare a summary on the gateway */
161
        json_t *info = json_object();
162
        json_object_set_new(info, "janus", json_string("server_info"));
163
        if(transaction != NULL)
164
                json_object_set_new(info, "transaction", json_string(transaction));
165
        json_object_set_new(info, "name", json_string(JANUS_NAME));
166
        json_object_set_new(info, "version", json_integer(JANUS_VERSION));
167
        json_object_set_new(info, "version_string", json_string(JANUS_VERSION_STRING));
168
        json_object_set_new(info, "author", json_string(JANUS_AUTHOR));
169
        json_object_set_new(info, "log-to-stdout", janus_log_is_stdout_enabled() ? json_true() : json_false());
170
        json_object_set_new(info, "log-to-file", janus_log_is_logfile_enabled() ? json_true() : json_false());
171
        if(janus_log_is_logfile_enabled())
172
                json_object_set_new(info, "log-path", json_string(janus_log_get_logfile_path()));
173
#ifdef HAVE_SCTP
174
        json_object_set_new(info, "data_channels", json_true());
175
#else
176
        json_object_set_new(info, "data_channels", json_false());
177
#endif
178
        json_object_set_new(info, "server-name", json_string(server_name ? server_name : JANUS_SERVER_NAME));
179
        json_object_set_new(info, "local-ip", json_string(local_ip));
180
        if(public_ip != NULL)
181
                json_object_set_new(info, "public-ip", json_string(public_ip));
182
        json_object_set_new(info, "ipv6", janus_ice_is_ipv6_enabled() ? json_true() : json_false());
183
        json_object_set_new(info, "ice-tcp", janus_ice_is_ice_tcp_enabled() ? json_true() : json_false());
184
        if(janus_ice_get_stun_server() != NULL) {
185
                char server[255];
186
                g_snprintf(server, 255, "%s:%"SCNu16, janus_ice_get_stun_server(), janus_ice_get_stun_port());
187
                json_object_set_new(info, "stun-server", json_string(server));
188
        }
189
        if(janus_ice_get_turn_server() != NULL) {
190
                char server[255];
191
                g_snprintf(server, 255, "%s:%"SCNu16, janus_ice_get_turn_server(), janus_ice_get_turn_port());
192
                json_object_set_new(info, "turn-server", json_string(server));
193
        }
194
        json_object_set_new(info, "api_secret", api_secret ? json_true() : json_false());
195
        json_object_set_new(info, "auth_token", janus_auth_is_enabled() ? json_true() : json_false());
196
        json_object_set_new(info, "event_handlers", janus_events_is_enabled() ? json_true() : json_false());
197
        /* Available transports */
198
        json_t *t_data = json_object();
199
        if(transports && g_hash_table_size(transports) > 0) {
200
                GHashTableIter iter;
201
                gpointer value;
202
                g_hash_table_iter_init(&iter, transports);
203
                while (g_hash_table_iter_next(&iter, NULL, &value)) {
204
                        janus_transport *t = value;
205
                        if(t == NULL) {
206
                                continue;
207
                        }
208
                        json_t *transport = json_object();
209
                        json_object_set_new(transport, "name", json_string(t->get_name()));
210
                        json_object_set_new(transport, "author", json_string(t->get_author()));
211
                        json_object_set_new(transport, "description", json_string(t->get_description()));
212
                        json_object_set_new(transport, "version_string", json_string(t->get_version_string()));
213
                        json_object_set_new(transport, "version", json_integer(t->get_version()));
214
                        json_object_set_new(t_data, t->get_package(), transport);
215
                }
216
        }
217
        json_object_set_new(info, "transports", t_data);
218
        /* Available event handlers */
219
        json_t *e_data = json_object();
220
        if(eventhandlers && g_hash_table_size(eventhandlers) > 0) {
221
                GHashTableIter iter;
222
                gpointer value;
223
                g_hash_table_iter_init(&iter, eventhandlers);
224
                while (g_hash_table_iter_next(&iter, NULL, &value)) {
225
                        janus_eventhandler *e = value;
226
                        if(e == NULL) {
227
                                continue;
228
                        }
229
                        json_t *eventhandler = json_object();
230
                        json_object_set_new(eventhandler, "name", json_string(e->get_name()));
231
                        json_object_set_new(eventhandler, "author", json_string(e->get_author()));
232
                        json_object_set_new(eventhandler, "description", json_string(e->get_description()));
233
                        json_object_set_new(eventhandler, "version_string", json_string(e->get_version_string()));
234
                        json_object_set_new(eventhandler, "version", json_integer(e->get_version()));
235
                        json_object_set_new(e_data, e->get_package(), eventhandler);
236
                }
237
        }
238
        json_object_set_new(info, "events", e_data);
239
        /* Available plugins */
240
        json_t *p_data = json_object();
241
        if(plugins && g_hash_table_size(plugins) > 0) {
242
                GHashTableIter iter;
243
                gpointer value;
244
                g_hash_table_iter_init(&iter, plugins);
245
                while (g_hash_table_iter_next(&iter, NULL, &value)) {
246
                        janus_plugin *p = value;
247
                        if(p == NULL) {
248
                                continue;
249
                        }
250
                        json_t *plugin = json_object();
251
                        json_object_set_new(plugin, "name", json_string(p->get_name()));
252
                        json_object_set_new(plugin, "author", json_string(p->get_author()));
253
                        json_object_set_new(plugin, "description", json_string(p->get_description()));
254
                        json_object_set_new(plugin, "version_string", json_string(p->get_version_string()));
255
                        json_object_set_new(plugin, "version", json_integer(p->get_version()));
256
                        json_object_set_new(p_data, p->get_package(), plugin);
257
                }
258
        }
259
        json_object_set_new(info, "plugins", p_data);
260

    
261
        return info;
262
}
263

    
264

    
265
/* Logging */
266
int janus_log_level = LOG_INFO;
267
gboolean janus_log_timestamps = FALSE;
268
gboolean janus_log_colors = FALSE;
269
int lock_debug = 0;
270

    
271

    
272
/*! \brief Signal handler (just used to intercept CTRL+C and SIGTERM) */
273
static void janus_handle_signal(int signum) {
274
        stop_signal = signum;
275
        switch(g_atomic_int_get(&stop)) {
276
                case 0:
277
                        JANUS_PRINT("Stopping gateway, please wait...\n");
278
                        break;
279
                case 1:
280
                        JANUS_PRINT("In a hurry? I'm trying to free resources cleanly, here!\n");
281
                        break;
282
                default:
283
                        JANUS_PRINT("Ok, leaving immediately...\n");
284
                        break;
285
        }
286
        g_atomic_int_inc(&stop);
287
        if(g_atomic_int_get(&stop) > 2)
288
                exit(1);
289
}
290

    
291
/*! \brief Termination handler (atexit) */
292
static void janus_termination_handler(void) {
293
        /* Free the instance name, if provided */
294
        g_free(server_name);
295
        /* Remove the PID file if we created it */
296
        janus_pidfile_remove();
297
        /* Close the logger */
298
        janus_log_destroy();
299
        /* If we're daemonizing, we send an error code to the parent */
300
        if(daemonize) {
301
                int code = 1;
302
                ssize_t res = 0;
303
                do {
304
                        res = write(pipefd[1], &code, sizeof(int));
305
                } while(res == -1 && errno == EINTR);
306
        }
307
}
308

    
309

    
310
/** @name Transport plugin callback interface
311
 * These are the callbacks implemented by the gateway core, as part of
312
 * the janus_transport_callbacks interface. Everything the transport
313
 * plugins send the gateway is handled here.
314
 */
315
///@{
316
void janus_transport_incoming_request(janus_transport *plugin, void *transport, void *request_id, gboolean admin, json_t *message, json_error_t *error);
317
void janus_transport_gone(janus_transport *plugin, void *transport);
318
gboolean janus_transport_is_api_secret_needed(janus_transport *plugin);
319
gboolean janus_transport_is_api_secret_valid(janus_transport *plugin, const char *apisecret);
320
gboolean janus_transport_is_auth_token_needed(janus_transport *plugin);
321
gboolean janus_transport_is_auth_token_valid(janus_transport *plugin, const char *token);
322

    
323
static janus_transport_callbacks janus_handler_transport =
324
        {
325
                .incoming_request = janus_transport_incoming_request,
326
                .transport_gone = janus_transport_gone,
327
                .is_api_secret_needed = janus_transport_is_api_secret_needed,
328
                .is_api_secret_valid = janus_transport_is_api_secret_valid,
329
                .is_auth_token_needed = janus_transport_is_auth_token_needed,
330
                .is_auth_token_valid = janus_transport_is_auth_token_valid,
331
        };
332
GThreadPool *tasks = NULL;
333
void janus_transport_task(gpointer data, gpointer user_data);
334
///@}
335

    
336

    
337
/** @name Plugin callback interface
338
 * These are the callbacks implemented by the gateway core, as part of
339
 * the janus_callbacks interface. Everything the plugins send the
340
 * gateway is handled here.
341
 */
342
///@{
343
int janus_plugin_push_event(janus_plugin_session *plugin_session, janus_plugin *plugin, const char *transaction, json_t *message, json_t *jsep);
344
json_t *janus_plugin_handle_sdp(janus_plugin_session *plugin_session, janus_plugin *plugin, const char *sdp_type, const char *sdp);
345
void janus_plugin_relay_rtp(janus_plugin_session *plugin_session, int video, char *buf, int len);
346
void janus_plugin_relay_rtcp(janus_plugin_session *plugin_session, int video, char *buf, int len);
347
void janus_plugin_relay_data(janus_plugin_session *plugin_session, char *buf, int len);
348
void janus_plugin_close_pc(janus_plugin_session *plugin_session);
349
void janus_plugin_end_session(janus_plugin_session *plugin_session);
350
void janus_plugin_notify_event(janus_plugin_session *plugin_session, json_t *event);
351
static janus_callbacks janus_handler_plugin =
352
        {
353
                .push_event = janus_plugin_push_event,
354
                .relay_rtp = janus_plugin_relay_rtp,
355
                .relay_rtcp = janus_plugin_relay_rtcp,
356
                .relay_data = janus_plugin_relay_data,
357
                .close_pc = janus_plugin_close_pc,
358
                .end_session = janus_plugin_end_session,
359
                .events_is_enabled = janus_events_is_enabled,
360
                .notify_event = janus_plugin_notify_event,
361
        }; 
362
///@}
363

    
364

    
365
/* Gateway Sessions */
366
static janus_mutex sessions_mutex;
367
static GHashTable *sessions = NULL, *old_sessions = NULL;
368
static GMainContext *sessions_watchdog_context = NULL;
369

    
370

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

    
373
static gboolean janus_cleanup_session(gpointer user_data) {
374
        janus_session *session = (janus_session *) user_data;
375

    
376
        JANUS_LOG(LOG_DBG, "Cleaning up session %"SCNu64"...\n", session->session_id);
377
        janus_session_destroy(session->session_id);
378

    
379
        return G_SOURCE_REMOVE;
380
}
381

    
382
static gboolean janus_check_sessions(gpointer user_data) {
383
        GMainContext *watchdog_context = (GMainContext *) user_data;
384
        janus_mutex_lock(&sessions_mutex);
385
        if(sessions && g_hash_table_size(sessions) > 0) {
386
                GHashTableIter iter;
387
                gpointer value;
388
                g_hash_table_iter_init(&iter, sessions);
389
                while (g_hash_table_iter_next(&iter, NULL, &value)) {
390
                        janus_session *session = (janus_session *) value;
391
                        if (!session || session->destroy) {
392
                                continue;
393
                        }
394
                        gint64 now = janus_get_monotonic_time();
395
                        if (now - session->last_activity >= SESSION_TIMEOUT * G_USEC_PER_SEC && !session->timeout) {
396
                                JANUS_LOG(LOG_INFO, "Timeout expired for session %"SCNu64"...\n", session->session_id);
397

    
398
                                /* Notify the transport */
399
                                if(session->source) {
400
                                        json_t *event = json_object();
401
                                        json_object_set_new(event, "janus", json_string("timeout"));
402
                                        json_object_set_new(event, "session_id", json_integer(session->session_id));
403
                                        /* Send this to the transport client */
404
                                        session->source->transport->send_message(session->source->instance, NULL, FALSE, event);
405
                                        /* Notify the transport plugin about the session timeout */
406
                                        session->source->transport->session_over(session->source->instance, session->session_id, TRUE);
407
                                }
408
                                /* Notify event handlers as well */
409
                                if(janus_events_is_enabled())
410
                                        janus_events_notify_handlers(JANUS_EVENT_TYPE_SESSION, session->session_id, "timeout");
411

    
412
                                /* Mark the session as over, we'll deal with it later */
413
                                session->timeout = 1;
414
                                /* FIXME Is this safe? apparently it causes hash table errors on the console */
415
                                g_hash_table_iter_remove(&iter);
416
                                g_hash_table_insert(old_sessions, janus_uint64_dup(session->session_id), session);
417

    
418
                                /* Schedule the session for deletion */
419
                                GSource *timeout_source = g_timeout_source_new_seconds(3);
420
                                g_source_set_callback(timeout_source, janus_cleanup_session, session, NULL);
421
                                g_source_attach(timeout_source, watchdog_context);
422
                                g_source_unref(timeout_source);
423
                        }
424
                }
425
        }
426
        janus_mutex_unlock(&sessions_mutex);
427

    
428
        return G_SOURCE_CONTINUE;
429
}
430

    
431
static gpointer janus_sessions_watchdog(gpointer user_data) {
432
        GMainLoop *loop = (GMainLoop *) user_data;
433
        GMainContext *watchdog_context = g_main_loop_get_context(loop);
434
        GSource *timeout_source;
435

    
436
        timeout_source = g_timeout_source_new_seconds(2);
437
        g_source_set_callback(timeout_source, janus_check_sessions, watchdog_context, NULL);
438
        g_source_attach(timeout_source, watchdog_context);
439
        g_source_unref(timeout_source);
440

    
441
        JANUS_LOG(LOG_INFO, "Sessions watchdog started\n");
442

    
443
        g_main_loop_run(loop);
444

    
445
        return NULL;
446
}
447

    
448
janus_session *janus_session_create(guint64 session_id) {
449
        if(session_id == 0) {
450
                while(session_id == 0) {
451
                        session_id = janus_random_uint64();
452
                        if(janus_session_find(session_id) != NULL) {
453
                                /* Session ID already taken, try another one */
454
                                session_id = 0;
455
                        }
456
                }
457
        }
458
        JANUS_LOG(LOG_INFO, "Creating new session: %"SCNu64"\n", session_id);
459
        janus_session *session = (janus_session *)g_malloc0(sizeof(janus_session));
460
        if(session == NULL) {
461
                JANUS_LOG(LOG_FATAL, "Memory error!\n");
462
                return NULL;
463
        }
464
        session->session_id = session_id;
465
        session->source = NULL;
466
        session->destroy = 0;
467
        session->timeout = 0;
468
        session->last_activity = janus_get_monotonic_time();
469
        janus_mutex_init(&session->mutex);
470
        janus_mutex_lock(&sessions_mutex);
471
        g_hash_table_insert(sessions, janus_uint64_dup(session->session_id), session);
472
        janus_mutex_unlock(&sessions_mutex);
473
        /* Notify event handlers */
474
        if(janus_events_is_enabled())
475
                janus_events_notify_handlers(JANUS_EVENT_TYPE_SESSION, session_id, "created");
476
        return session;
477
}
478

    
479
janus_session *janus_session_find(guint64 session_id) {
480
        janus_mutex_lock(&sessions_mutex);
481
        janus_session *session = g_hash_table_lookup(sessions, &session_id);
482
        janus_mutex_unlock(&sessions_mutex);
483
        return session;
484
}
485

    
486
janus_session *janus_session_find_destroyed(guint64 session_id) {
487
        janus_mutex_lock(&sessions_mutex);
488
        janus_session *session = g_hash_table_lookup(old_sessions, &session_id);
489
        janus_mutex_unlock(&sessions_mutex);
490
        return session;
491
}
492

    
493
void janus_session_notify_event(guint64 session_id, json_t *event) {
494
        janus_mutex_lock(&sessions_mutex);
495
        janus_session *session = sessions ? g_hash_table_lookup(sessions, &session_id) : NULL;
496
        if(session != NULL && !session->destroy && session->source != NULL && session->source->transport != NULL) {
497
                janus_mutex_unlock(&sessions_mutex);
498
                /* Send this to the transport client */
499
                JANUS_LOG(LOG_HUGE, "Sending event to %s (%p)\n", session->source->transport->get_package(), session->source->instance);
500
                session->source->transport->send_message(session->source->instance, NULL, FALSE, event);
501
        } else {
502
                janus_mutex_unlock(&sessions_mutex);
503
                /* No transport, free the event */
504
                json_decref(event);
505
        }
506
}
507

    
508

    
509
/* Destroys a session but does not remove it from the sessions hash table. */
510
gint janus_session_destroy(guint64 session_id) {
511
        janus_session *session = janus_session_find_destroyed(session_id);
512
        if(session == NULL) {
513
                JANUS_LOG(LOG_ERR, "Couldn't find session to destroy: %"SCNu64"\n", session_id);
514
                return -1;
515
        }
516
        JANUS_LOG(LOG_VERB, "Destroying session %"SCNu64"\n", session_id);
517
        session->destroy = 1;
518
        if (session->ice_handles != NULL && g_hash_table_size(session->ice_handles) > 0) {
519
                GHashTableIter iter;
520
                gpointer value;
521
                /* Remove all handles */
522
                g_hash_table_iter_init(&iter, session->ice_handles);
523
                while (g_hash_table_iter_next(&iter, NULL, &value)) {
524
                        janus_ice_handle *handle = value;
525
                        if(!handle || g_atomic_int_get(&stop)) {
526
                                continue;
527
                        }
528
                        janus_ice_handle_destroy(session, handle->handle_id);
529
                        g_hash_table_iter_remove(&iter);
530
                }
531
        }
532

    
533
        /* FIXME Actually destroy session */
534
        janus_session_free(session);
535

    
536
        return 0;
537
}
538

    
539
void janus_session_free(janus_session *session) {
540
        if(session == NULL)
541
                return;
542
        janus_mutex_lock(&session->mutex);
543
        if(session->ice_handles != NULL) {
544
                g_hash_table_destroy(session->ice_handles);
545
                session->ice_handles = NULL;
546
        }
547
        if(session->source != NULL) {
548
                janus_request_destroy(session->source);
549
                session->source = NULL;
550
        }
551
        janus_mutex_unlock(&session->mutex);
552
        g_free(session);
553
        session = NULL;
554
}
555

    
556

    
557
/* Requests management */
558
janus_request *janus_request_new(janus_transport *transport, void *instance, void *request_id, gboolean admin, json_t *message) {
559
        janus_request *request = (janus_request *)g_malloc0(sizeof(janus_request));
560
        request->transport = transport;
561
        request->instance = instance;
562
        request->request_id = request_id;
563
        request->admin = admin;
564
        request->message = message;
565
        return request;
566
}
567

    
568
void janus_request_destroy(janus_request *request) {
569
        if(request == NULL)
570
                return;
571
        request->transport = NULL;
572
        request->instance = NULL;
573
        request->request_id = NULL;
574
        if(request->message)
575
                json_decref(request->message);
576
        request->message = NULL;
577
        g_free(request);
578
}
579

    
580
int janus_process_incoming_request(janus_request *request) {
581
        int ret = -1;
582
        if(request == NULL) {
583
                JANUS_LOG(LOG_ERR, "Missing request or payload to process, giving up...\n");
584
                return ret;
585
        }
586
        int error_code = 0;
587
        char error_cause[100];
588
        json_t *root = request->message;
589
        /* Ok, let's start with the ids */
590
        guint64 session_id = 0, handle_id = 0;
591
        json_t *s = json_object_get(root, "session_id");
592
        if(s && json_is_integer(s))
593
                session_id = json_integer_value(s);
594
        json_t *h = json_object_get(root, "handle_id");
595
        if(h && json_is_integer(h))
596
                handle_id = json_integer_value(h);
597

    
598
        /* Get transaction and message request */
599
        JANUS_VALIDATE_JSON_OBJECT(root, incoming_request_parameters,
600
                error_code, error_cause, FALSE,
601
                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
602
        if(error_code != 0) {
603
                ret = janus_process_error_string(request, session_id, NULL, error_code, error_cause);
604
                goto jsondone;
605
        }
606
        json_t *transaction = json_object_get(root, "transaction");
607
        const gchar *transaction_text = json_string_value(transaction);
608
        json_t *message = json_object_get(root, "janus");
609
        const gchar *message_text = json_string_value(message);
610

    
611
        if(session_id == 0 && handle_id == 0) {
612
                /* Can only be a 'Create new session', a 'Get info' or a 'Ping/Pong' request */
613
                if(!strcasecmp(message_text, "info")) {
614
                        ret = janus_process_success(request, janus_info(transaction_text));
615
                        goto jsondone;
616
                }
617
                if(!strcasecmp(message_text, "ping")) {
618
                        /* Prepare JSON reply */
619
                        json_t *reply = json_object();
620
                        json_object_set_new(reply, "janus", json_string("pong"));
621
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
622
                        ret = janus_process_success(request, reply);
623
                        goto jsondone;
624
                }
625
                if(strcasecmp(message_text, "create")) {
626
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
627
                        goto jsondone;
628
                }
629
                /* Any secret/token to check? */
630
                gboolean secret_authorized = FALSE, token_authorized = FALSE;
631
                if(api_secret == NULL && !janus_auth_is_enabled()) {
632
                        /* Nothing to check */
633
                        secret_authorized = TRUE;
634
                        token_authorized = TRUE;
635
                } else {
636
                        if(api_secret != NULL) {
637
                                /* There's an API secret, check that the client provided it */
638
                                json_t *secret = json_object_get(root, "apisecret");
639
                                if(secret && json_is_string(secret) && janus_strcmp_const_time(json_string_value(secret), api_secret)) {
640
                                        secret_authorized = TRUE;
641
                                }
642
                        }
643
                        if(janus_auth_is_enabled()) {
644
                                /* The token based authentication mechanism is enabled, check that the client provided it */
645
                                json_t *token = json_object_get(root, "token");
646
                                if(token && json_is_string(token) && janus_auth_check_token(json_string_value(token))) {
647
                                        token_authorized = TRUE;
648
                                }
649
                        }
650
                        /* We consider a request authorized if either the proper API secret or a valid token has been provided */
651
                        if(!secret_authorized && !token_authorized) {
652
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED, NULL);
653
                                goto jsondone;
654
                        }
655
                }
656
                session_id = 0;
657
                json_t *id = json_object_get(root, "id");
658
                if(id != NULL) {
659
                        /* The application provided the session ID to use */
660
                        session_id = json_integer_value(id);
661
                        if(session_id > 0 && janus_session_find(session_id) != NULL) {
662
                                /* Session ID already taken */
663
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_CONFLICT, "Session ID already in use");
664
                                goto jsondone;
665
                        }
666
                }
667
                /* Handle it */
668
                janus_session *session = janus_session_create(session_id);
669
                if(session == NULL) {
670
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Memory error");
671
                        goto jsondone;
672
                }
673
                session_id = session->session_id;
674
                /* Take note of the request source that originated this session (HTTP, WebSockets, RabbitMQ?) */
675
                session->source = janus_request_new(request->transport, request->instance, NULL, FALSE, NULL);
676
                /* Notify the source that a new session has been created */
677
                request->transport->session_created(request->instance, session->session_id);
678
                /* Prepare JSON reply */
679
                json_t *reply = json_object();
680
                json_object_set_new(reply, "janus", json_string("success"));
681
                json_object_set_new(reply, "transaction", json_string(transaction_text));
682
                json_t *data = json_object();
683
                json_object_set_new(data, "id", json_integer(session_id));
684
                json_object_set_new(reply, "data", data);
685
                /* Send the success reply */
686
                ret = janus_process_success(request, reply);
687
                goto jsondone;
688
        }
689
        if(session_id < 1) {
690
                JANUS_LOG(LOG_ERR, "Invalid session\n");
691
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, NULL);
692
                goto jsondone;
693
        }
694
        if(h && handle_id < 1) {
695
                JANUS_LOG(LOG_ERR, "Invalid handle\n");
696
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, NULL);
697
                goto jsondone;
698
        }
699

    
700
        /* Go on with the processing */
701
        gboolean secret_authorized = FALSE, token_authorized = FALSE;
702
        if(api_secret == NULL && !janus_auth_is_enabled()) {
703
                /* Nothing to check */
704
                secret_authorized = TRUE;
705
                token_authorized = TRUE;
706
        } else {
707
                if(api_secret != NULL) {
708
                        /* There's an API secret, check that the client provided it */
709
                        json_t *secret = json_object_get(root, "apisecret");
710
                        if(secret && json_is_string(secret) && janus_strcmp_const_time(json_string_value(secret), api_secret)) {
711
                                secret_authorized = TRUE;
712
                        }
713
                }
714
                if(janus_auth_is_enabled()) {
715
                        /* The token based authentication mechanism is enabled, check that the client provided it */
716
                        json_t *token = json_object_get(root, "token");
717
                        if(token && json_is_string(token) && janus_auth_check_token(json_string_value(token))) {
718
                                token_authorized = TRUE;
719
                        }
720
                }
721
                /* We consider a request authorized if either the proper API secret or a valid token has been provided */
722
                if(!secret_authorized && !token_authorized) {
723
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED, NULL);
724
                        goto jsondone;
725
                }
726
        }
727

    
728
        /* If we got here, make sure we have a session (and/or a handle) */
729
        janus_session *session = janus_session_find(session_id);
730
        if(!session) {
731
                JANUS_LOG(LOG_ERR, "Couldn't find any session %"SCNu64"...\n", session_id);
732
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, "No such session %"SCNu64"", session_id);
733
                goto jsondone;
734
        }
735
        /* Update the last activity timer */
736
        session->last_activity = janus_get_monotonic_time();
737
        janus_ice_handle *handle = NULL;
738
        if(handle_id > 0) {
739
                handle = janus_ice_handle_find(session, handle_id);
740
                if(!handle) {
741
                        JANUS_LOG(LOG_ERR, "Couldn't find any handle %"SCNu64" in session %"SCNu64"...\n", handle_id, session_id);
742
                        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);
743
                        goto jsondone;
744
                }
745
        }
746

    
747
        /* What is this? */
748
        if(!strcasecmp(message_text, "keepalive")) {
749
                /* Just a keep-alive message, reply with an ack */
750
                JANUS_LOG(LOG_VERB, "Got a keep-alive on session %"SCNu64"\n", session_id);
751
                json_t *reply = json_object();
752
                json_object_set_new(reply, "janus", json_string("ack"));
753
                json_object_set_new(reply, "session_id", json_integer(session_id));
754
                json_object_set_new(reply, "transaction", json_string(transaction_text));
755
                /* Send the success reply */
756
                ret = janus_process_success(request, reply);
757
        } else if(!strcasecmp(message_text, "attach")) {
758
                if(handle != NULL) {
759
                        /* Attach is a session-level command */
760
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
761
                        goto jsondone;
762
                }
763
                JANUS_VALIDATE_JSON_OBJECT(root, attach_parameters,
764
                        error_code, error_cause, FALSE,
765
                        JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
766
                if(error_code != 0) {
767
                        ret = janus_process_error_string(request, session_id, NULL, error_code, error_cause);
768
                        goto jsondone;
769
                }
770
                json_t *plugin = json_object_get(root, "plugin");
771
                const gchar *plugin_text = json_string_value(plugin);
772
                janus_plugin *plugin_t = janus_plugin_find(plugin_text);
773
                if(plugin_t == NULL) {
774
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_NOT_FOUND, "No such plugin '%s'", plugin_text);
775
                        goto jsondone;
776
                }
777
                /* If the auth token mechanism is enabled, we should check if this token can access this plugin */
778
                if(janus_auth_is_enabled()) {
779
                        json_t *token = json_object_get(root, "token");
780
                        if(token != NULL) {
781
                                const char *token_value = json_string_value(token);
782
                                if(token_value && !janus_auth_check_plugin(token_value, plugin_t)) {
783
                                        JANUS_LOG(LOG_ERR, "Token '%s' can't access plugin '%s'\n", token_value, plugin_text);
784
                                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED_PLUGIN, "Provided token can't access plugin '%s'", plugin_text);
785
                                        goto jsondone;
786
                                }
787
                        }
788
                }
789
                /* Create handle */
790
                handle = janus_ice_handle_create(session);
791
                if(handle == NULL) {
792
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Memory error");
793
                        goto jsondone;
794
                }
795
                handle_id = handle->handle_id;
796
                /* Attach to the plugin */
797
                int error = 0;
798
                if((error = janus_ice_handle_attach_plugin(session, handle_id, plugin_t)) != 0) {
799
                        /* TODO Make error struct to pass verbose information */
800
                        janus_ice_handle_destroy(session, handle_id);
801
                        janus_mutex_lock(&session->mutex);
802
                        g_hash_table_remove(session->ice_handles, &handle_id);
803
                        janus_mutex_unlock(&session->mutex);
804
                        JANUS_LOG(LOG_ERR, "Couldn't attach to plugin '%s', error '%d'\n", plugin_text, error);
805
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_ATTACH, "Couldn't attach to plugin: error '%d'", error);
806
                        goto jsondone;
807
                }
808
                /* Prepare JSON reply */
809
                json_t *reply = json_object();
810
                json_object_set_new(reply, "janus", json_string("success"));
811
                json_object_set_new(reply, "session_id", json_integer(session_id));
812
                json_object_set_new(reply, "transaction", json_string(transaction_text));
813
                json_t *data = json_object();
814
                json_object_set_new(data, "id", json_integer(handle_id));
815
                json_object_set_new(reply, "data", data);
816
                /* Send the success reply */
817
                ret = janus_process_success(request, reply);
818
        } else if(!strcasecmp(message_text, "destroy")) {
819
                if(handle != NULL) {
820
                        /* Query is a session-level command */
821
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
822
                        goto jsondone;
823
                }
824
                /* Schedule the session for deletion */
825
                session->destroy = 1;
826
                janus_mutex_lock(&sessions_mutex);
827
                g_hash_table_remove(sessions, &session->session_id);
828
                g_hash_table_insert(old_sessions, janus_uint64_dup(session->session_id), session);
829
                GSource *timeout_source = g_timeout_source_new_seconds(3);
830
                g_source_set_callback(timeout_source, janus_cleanup_session, session, NULL);
831
                g_source_attach(timeout_source, sessions_watchdog_context);
832
                g_source_unref(timeout_source);
833
                janus_mutex_unlock(&sessions_mutex);
834
                /* Notify the source that the session has been destroyed */
835
                if(session->source && session->source->transport)
836
                        session->source->transport->session_over(session->source->instance, session->session_id, FALSE);
837

    
838
                /* Prepare JSON reply */
839
                json_t *reply = json_object();
840
                json_object_set_new(reply, "janus", json_string("success"));
841
                json_object_set_new(reply, "session_id", json_integer(session_id));
842
                json_object_set_new(reply, "transaction", json_string(transaction_text));
843
                /* Send the success reply */
844
                ret = janus_process_success(request, reply);
845
                /* Notify event handlers as well */
846
                if(janus_events_is_enabled())
847
                        janus_events_notify_handlers(JANUS_EVENT_TYPE_SESSION, session_id, "destroyed");
848
        } else if(!strcasecmp(message_text, "detach")) {
849
                if(handle == NULL) {
850
                        /* Query is an handle-level command */
851
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
852
                        goto jsondone;
853
                }
854
                if(handle->app == NULL || handle->app_handle == NULL) {
855
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_DETACH, "No plugin to detach from");
856
                        goto jsondone;
857
                }
858
                int error = janus_ice_handle_destroy(session, handle_id);
859
                janus_mutex_lock(&session->mutex);
860
                g_hash_table_remove(session->ice_handles, &handle_id);
861
                janus_mutex_unlock(&session->mutex);
862

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

    
1278
                /* Make sure the app handle is still valid */
1279
                if(handle->app == NULL || handle->app_handle == NULL || !janus_plugin_session_is_alive(handle->app_handle)) {
1280
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "No plugin to handle this message");
1281
                        if(jsep_type)
1282
                                g_free(jsep_type);
1283
                        if(jsep_sdp_stripped)
1284
                                g_free(jsep_sdp_stripped);
1285
                        janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1286
                        goto jsondone;
1287
                }
1288

    
1289
                /* Send the message to the plugin (which must eventually free transaction_text and unref the two objects, body and jsep) */
1290
                json_incref(body);
1291
                janus_plugin_result *result = plugin_t->handle_message(handle->app_handle,
1292
                        g_strdup((char *)transaction_text), body,
1293
                        jsep_sdp_stripped ? json_pack("{ssss}", "type", jsep_type, "sdp", jsep_sdp_stripped) : NULL);
1294
                if(result == NULL) {
1295
                        /* Something went horribly wrong! */
1296
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "Plugin didn't give a result");
1297
                        goto jsondone;
1298
                }
1299
                if(result->type == JANUS_PLUGIN_OK) {
1300
                        /* The plugin gave a result already (synchronous request/response) */
1301
                        if(result->content == NULL || !json_is_object(result->content)) {
1302
                                /* Missing content, or not a JSON object */
1303
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE,
1304
                                        result->content == NULL ?
1305
                                                "Plugin didn't provide any content for this synchronous response" :
1306
                                                "Plugin returned an invalid JSON response");
1307
                                janus_plugin_result_destroy(result);
1308
                                goto jsondone;
1309
                        }
1310
                        /* Reference the content, as destroying the result instance will decref it */
1311
                        json_incref(result->content);
1312
                        /* Prepare JSON response */
1313
                        json_t *reply = json_object();
1314
                        json_object_set_new(reply, "janus", json_string("success"));
1315
                        json_object_set_new(reply, "session_id", json_integer(session->session_id));
1316
                        json_object_set_new(reply, "sender", json_integer(handle->handle_id));
1317
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1318
                        json_t *plugin_data = json_object();
1319
                        json_object_set_new(plugin_data, "plugin", json_string(plugin_t->get_package()));
1320
                        json_object_set_new(plugin_data, "data", result->content);
1321
                        json_object_set_new(reply, "plugindata", plugin_data);
1322
                        /* Send the success reply */
1323
                        ret = janus_process_success(request, reply);
1324
                } else if(result->type == JANUS_PLUGIN_OK_WAIT) {
1325
                        /* The plugin received the request but didn't process it yet, send an ack (asynchronous notifications may follow) */
1326
                        json_t *reply = json_object();
1327
                        json_object_set_new(reply, "janus", json_string("ack"));
1328
                        json_object_set_new(reply, "session_id", json_integer(session_id));
1329
                        if(result->text)
1330
                                json_object_set_new(reply, "hint", json_string(result->text));
1331
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1332
                        /* Send the success reply */
1333
                        ret = janus_process_success(request, reply);
1334
                } else {
1335
                        /* Something went horribly wrong! */
1336
                        ret = janus_process_error_string(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE,
1337
                                (char *)(result->text ? result->text : "Plugin returned a severe (unknown) error"));
1338
                        janus_plugin_result_destroy(result);
1339
                        goto jsondone;
1340
                }
1341
                janus_plugin_result_destroy(result);
1342
        } else if(!strcasecmp(message_text, "trickle")) {
1343
                if(handle == NULL) {
1344
                        /* Trickle is an handle-level command */
1345
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
1346
                        goto jsondone;
1347
                }
1348
                if(handle->app == NULL || handle->app_handle == NULL || !janus_plugin_session_is_alive(handle->app_handle)) {
1349
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "No plugin to handle this trickle candidate");
1350
                        goto jsondone;
1351
                }
1352
                json_t *candidate = json_object_get(root, "candidate");
1353
                json_t *candidates = json_object_get(root, "candidates");
1354
                if(candidate == NULL && candidates == NULL) {
1355
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "Missing mandatory element (candidate|candidates)");
1356
                        goto jsondone;
1357
                }
1358
                if(candidate != NULL && candidates != NULL) {
1359
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_JSON, "Can't have both candidate and candidates");
1360
                        goto jsondone;
1361
                }
1362
                janus_mutex_lock(&handle->mutex);
1363
                if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE)) {
1364
                        /* It looks like this peer supports Trickle, after all */
1365
                        JANUS_LOG(LOG_VERB, "Handle %"SCNu64" supports trickle even if it didn't negotiate it...\n", handle->handle_id);
1366
                        janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE);
1367
                }
1368
                /* Is there any stream ready? this trickle may get here before the SDP it relates to */
1369
                if(handle->audio_stream == NULL && handle->video_stream == NULL && handle->data_stream == NULL) {
1370
                        JANUS_LOG(LOG_WARN, "[%"SCNu64"] No stream, queueing this trickle as it got here before the SDP...\n", handle->handle_id);
1371
                        /* Enqueue this trickle candidate(s), we'll process this later */
1372
                        janus_ice_trickle *early_trickle = janus_ice_trickle_new(handle, transaction_text, candidate ? candidate : candidates);
1373
                        handle->pending_trickles = g_list_append(handle->pending_trickles, early_trickle);
1374
                        /* Send the ack right away, an event will tell the application if the candidate(s) failed */
1375
                        goto trickledone;
1376
                }
1377
                /* Is the ICE stack ready already? */
1378
                if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER) ||
1379
                                !janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER) ||
1380
                                !janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER)) {
1381
                        const char *cause = NULL;
1382
                        if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER))
1383
                                cause = "processing the offer";
1384
                        else if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER))
1385
                                cause = "waiting for the answer";
1386
                        else if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER))
1387
                                cause = "waiting for the offer";
1388
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Still %s, queueing this trickle to wait until we're done there...\n",
1389
                                handle->handle_id, cause);
1390
                        /* Enqueue this trickle candidate(s), we'll process this later */
1391
                        janus_ice_trickle *early_trickle = janus_ice_trickle_new(handle, transaction_text, candidate ? candidate : candidates);
1392
                        handle->pending_trickles = g_list_append(handle->pending_trickles, early_trickle);
1393
                        /* Send the ack right away, an event will tell the application if the candidate(s) failed */
1394
                        goto trickledone;
1395
                }
1396
                if(candidate != NULL) {
1397
                        /* We got a single candidate */
1398
                        int error = 0;
1399
                        const char *error_string = NULL;
1400
                        if((error = janus_ice_trickle_parse(handle, candidate, &error_string)) != 0) {
1401
                                ret = janus_process_error(request, session_id, transaction_text, error, "%s", error_string);
1402
                                janus_mutex_unlock(&handle->mutex);
1403
                                goto jsondone;
1404
                        }
1405
                } else {
1406
                        /* We got multiple candidates in an array */
1407
                        if(!json_is_array(candidates)) {
1408
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "candidates is not an array");
1409
                                janus_mutex_unlock(&handle->mutex);
1410
                                goto jsondone;
1411
                        }
1412
                        JANUS_LOG(LOG_VERB, "Got multiple candidates (%zu)\n", json_array_size(candidates));
1413
                        if(json_array_size(candidates) > 0) {
1414
                                /* Handle remote candidates */
1415
                                size_t i = 0;
1416
                                for(i=0; i<json_array_size(candidates); i++) {
1417
                                        json_t *c = json_array_get(candidates, i);
1418
                                        /* FIXME We don't care if any trickle fails to parse */
1419
                                        janus_ice_trickle_parse(handle, c, NULL);
1420
                                }
1421
                        }
1422
                }
1423

    
1424
trickledone:
1425
                janus_mutex_unlock(&handle->mutex);
1426
                /* We reply right away, not to block the web server... */
1427
                json_t *reply = json_object();
1428
                json_object_set_new(reply, "janus", json_string("ack"));
1429
                json_object_set_new(reply, "session_id", json_integer(session_id));
1430
                json_object_set_new(reply, "transaction", json_string(transaction_text));
1431
                /* Send the success reply */
1432
                ret = janus_process_success(request, reply);
1433
        } else {
1434
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN_REQUEST, "Unknown request '%s'", message_text);
1435
        }
1436

    
1437
jsondone:
1438
        /* Done processing */
1439
        return ret;
1440
}
1441

    
1442
/* Admin/monitor WebServer requests handler */
1443
int janus_process_incoming_admin_request(janus_request *request) {
1444
        int ret = -1;
1445
        int error_code = 0;
1446
        char error_cause[100];
1447
        if(request == NULL) {
1448
                JANUS_LOG(LOG_ERR, "Missing request or payload to process, giving up...\n");
1449
                return ret;
1450
        }
1451
        json_t *root = request->message;
1452
        /* Ok, let's start with the ids */
1453
        guint64 session_id = 0, handle_id = 0;
1454
        json_t *s = json_object_get(root, "session_id");
1455
        if(s && json_is_integer(s))
1456
                session_id = json_integer_value(s);
1457
        json_t *h = json_object_get(root, "handle_id");
1458
        if(h && json_is_integer(h))
1459
                handle_id = json_integer_value(h);
1460

    
1461
        /* Get transaction and message request */
1462
        JANUS_VALIDATE_JSON_OBJECT(root, admin_parameters,
1463
                error_code, error_cause, FALSE,
1464
                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1465
        if(error_code != 0) {
1466
                ret = janus_process_error_string(request, session_id, NULL, error_code, error_cause);
1467
                goto jsondone;
1468
        }
1469
        json_t *transaction = json_object_get(root, "transaction");
1470
        const gchar *transaction_text = json_string_value(transaction);
1471
        json_t *message = json_object_get(root, "janus");
1472
        if(!message) {
1473
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "Missing mandatory element (janus)");
1474
                goto jsondone;
1475
        }
1476
        if(!json_is_string(message)) {
1477
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (janus should be a string)");
1478
                goto jsondone;
1479
        }
1480
        const gchar *message_text = json_string_value(message);
1481

    
1482
        if(session_id == 0 && handle_id == 0) {
1483
                /* Can only be a 'Get all sessions' or some general setting manipulation request */
1484
                if(!strcasecmp(message_text, "info")) {
1485
                        /* The generic info request */
1486
                        ret = janus_process_success(request, janus_info(transaction_text));
1487
                        goto jsondone;
1488
                }
1489
                if(admin_api_secret != NULL) {
1490
                        /* There's an admin/monitor secret, check that the client provided it */
1491
                        json_t *secret = json_object_get(root, "admin_secret");
1492
                        if(!secret || !json_is_string(secret) || !janus_strcmp_const_time(json_string_value(secret), admin_api_secret)) {
1493
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED, NULL);
1494
                                goto jsondone;
1495
                        }
1496
                }
1497
                if(!strcasecmp(message_text, "get_status")) {
1498
                        /* Return some info on the settings (mostly debug-related, at the moment) */
1499
                        json_t *reply = json_object();
1500
                        json_object_set_new(reply, "janus", json_string("success"));
1501
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1502
                        json_t *status = json_object();
1503
                        json_object_set_new(status, "token_auth", janus_auth_is_enabled() ? json_true() : json_false());
1504
                        json_object_set_new(status, "log_level", json_integer(janus_log_level));
1505
                        json_object_set_new(status, "log_timestamps", janus_log_timestamps ? json_true() : json_false());
1506
                        json_object_set_new(status, "log_colors", janus_log_colors ? json_true() : json_false());
1507
                        json_object_set_new(status, "locking_debug", lock_debug ? json_true() : json_false());
1508
                        json_object_set_new(status, "libnice_debug", janus_ice_is_ice_debugging_enabled() ? json_true() : json_false());
1509
                        json_object_set_new(status, "max_nack_queue", json_integer(janus_get_max_nack_queue()));
1510
                        json_object_set_new(reply, "status", status);
1511
                        /* Send the success reply */
1512
                        ret = janus_process_success(request, reply);
1513
                        goto jsondone;
1514
                } else if(!strcasecmp(message_text, "set_log_level")) {
1515
                        /* Change the debug logging level */
1516
                        JANUS_VALIDATE_JSON_OBJECT(root, level_parameters,
1517
                                error_code, error_cause, FALSE,
1518
                                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1519
                        if(error_code != 0) {
1520
                                ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1521
                                goto jsondone;
1522
                        }
1523
                        json_t *level = json_object_get(root, "level");
1524
                        int level_num = json_integer_value(level);
1525
                        if(level_num < LOG_NONE || level_num > LOG_MAX) {
1526
                                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);
1527
                                goto jsondone;
1528
                        }
1529
                        janus_log_level = level_num;
1530
                        /* Prepare JSON reply */
1531
                        json_t *reply = json_object();
1532
                        json_object_set_new(reply, "janus", json_string("success"));
1533
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1534
                        json_object_set_new(reply, "level", json_integer(janus_log_level));
1535
                        /* Send the success reply */
1536
                        ret = janus_process_success(request, reply);
1537
                        goto jsondone;
1538
                } else if(!strcasecmp(message_text, "set_locking_debug")) {
1539
                        /* Enable/disable the locking debug (would show a message on the console for every lock attempt) */
1540
                        JANUS_VALIDATE_JSON_OBJECT(root, debug_parameters,
1541
                                error_code, error_cause, FALSE,
1542
                                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1543
                        if(error_code != 0) {
1544
                                ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1545
                                goto jsondone;
1546
                        }
1547
                        json_t *debug = json_object_get(root, "debug");
1548
                        lock_debug = json_is_true(debug);
1549
                        /* Prepare JSON reply */
1550
                        json_t *reply = json_object();
1551
                        json_object_set_new(reply, "janus", json_string("success"));
1552
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1553
                        json_object_set_new(reply, "locking_debug", lock_debug ? json_true() : json_false());
1554
                        /* Send the success reply */
1555
                        ret = janus_process_success(request, reply);
1556
                        goto jsondone;
1557
                } else if(!strcasecmp(message_text, "set_log_timestamps")) {
1558
                        /* Enable/disable the log timestamps */
1559
                        JANUS_VALIDATE_JSON_OBJECT(root, timestamps_parameters,
1560
                                error_code, error_cause, FALSE,
1561
                                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1562
                        if(error_code != 0) {
1563
                                ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1564
                                goto jsondone;
1565
                        }
1566
                        json_t *timestamps = json_object_get(root, "timestamps");
1567
                        janus_log_timestamps = json_is_true(timestamps);
1568
                        /* Prepare JSON reply */
1569
                        json_t *reply = json_object();
1570
                        json_object_set_new(reply, "janus", json_string("success"));
1571
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1572
                        json_object_set_new(reply, "log_timestamps", janus_log_timestamps ? json_true() : json_false());
1573
                        /* Send the success reply */
1574
                        ret = janus_process_success(request, reply);
1575
                        goto jsondone;
1576
                } else if(!strcasecmp(message_text, "set_log_colors")) {
1577
                        /* Enable/disable the log colors */
1578
                        JANUS_VALIDATE_JSON_OBJECT(root, colors_parameters,
1579
                                error_code, error_cause, FALSE,
1580
                                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1581
                        if(error_code != 0) {
1582
                                ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1583
                                goto jsondone;
1584
                        }
1585
                        json_t *colors = json_object_get(root, "colors");
1586
                        janus_log_colors = json_is_true(colors);
1587
                        /* Prepare JSON reply */
1588
                        json_t *reply = json_object();
1589
                        json_object_set_new(reply, "janus", json_string("success"));
1590
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1591
                        json_object_set_new(reply, "log_colors", janus_log_colors ? json_true() : json_false());
1592
                        /* Send the success reply */
1593
                        ret = janus_process_success(request, reply);
1594
                        goto jsondone;
1595
                } else if(!strcasecmp(message_text, "set_libnice_debug")) {
1596
                        /* Enable/disable the libnice debugging (http://nice.freedesktop.org/libnice/libnice-Debug-messages.html) */
1597
                        JANUS_VALIDATE_JSON_OBJECT(root, debug_parameters,
1598
                                error_code, error_cause, FALSE,
1599
                                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1600
                        if(error_code != 0) {
1601
                                ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1602
                                goto jsondone;
1603
                        }
1604
                        json_t *debug = json_object_get(root, "debug");
1605
                        if(json_is_true(debug)) {
1606
                                janus_ice_debugging_enable();
1607
                        } else {
1608
                                janus_ice_debugging_disable();
1609
                        }
1610
                        /* Prepare JSON reply */
1611
                        json_t *reply = json_object();
1612
                        json_object_set_new(reply, "janus", json_string("success"));
1613
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1614
                        json_object_set_new(reply, "libnice_debug", janus_ice_is_ice_debugging_enabled() ? json_true() : json_false());
1615
                        /* Send the success reply */
1616
                        ret = janus_process_success(request, reply);
1617
                        goto jsondone;
1618
                } else if(!strcasecmp(message_text, "set_max_nack_queue")) {
1619
                        /* Change the current value for the max NACK queue */
1620
                        JANUS_VALIDATE_JSON_OBJECT(root, mnq_parameters,
1621
                                error_code, error_cause, FALSE,
1622
                                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1623
                        if(error_code != 0) {
1624
                                ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1625
                                goto jsondone;
1626
                        }
1627
                        json_t *mnq = json_object_get(root, "max_nack_queue");
1628
                        int mnq_num = json_integer_value(mnq);
1629
                        if(mnq_num < 0 || (mnq_num > 0 && mnq_num < 200)) {
1630
                                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)");
1631
                                goto jsondone;
1632
                        }
1633
                        janus_set_max_nack_queue(mnq_num);
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, "max_nack_queue", json_integer(janus_get_max_nack_queue()));
1639
                        /* Send the success reply */
1640
                        ret = janus_process_success(request, reply);
1641
                        goto jsondone;
1642
                } else if(!strcasecmp(message_text, "list_sessions")) {
1643
                        /* List sessions */
1644
                        session_id = 0;
1645
                        json_t *list = json_array();
1646
                        if(sessions != NULL && g_hash_table_size(sessions) > 0) {
1647
                                janus_mutex_lock(&sessions_mutex);
1648
                                GHashTableIter iter;
1649
                                gpointer value;
1650
                                g_hash_table_iter_init(&iter, sessions);
1651
                                while (g_hash_table_iter_next(&iter, NULL, &value)) {
1652
                                        janus_session *session = value;
1653
                                        if(session == NULL) {
1654
                                                continue;
1655
                                        }
1656
                                        json_array_append_new(list, json_integer(session->session_id));
1657
                                }
1658
                                janus_mutex_unlock(&sessions_mutex);
1659
                        }
1660
                        /* Prepare JSON reply */
1661
                        json_t *reply = json_object();
1662
                        json_object_set_new(reply, "janus", json_string("success"));
1663
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1664
                        json_object_set_new(reply, "sessions", list);
1665
                        /* Send the success reply */
1666
                        ret = janus_process_success(request, reply);
1667
                        goto jsondone;
1668
                } else if(!strcasecmp(message_text, "add_token")) {
1669
                        /* Add a token valid for authentication */
1670
                        if(!janus_auth_is_enabled()) {
1671
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Token based authentication disabled");
1672
                                goto jsondone;
1673
                        }
1674
                        JANUS_VALIDATE_JSON_OBJECT(root, add_token_parameters,
1675
                                error_code, error_cause, FALSE,
1676
                                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1677
                        if(error_code != 0) {
1678
                                ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1679
                                goto jsondone;
1680
                        }
1681
                        json_t *token = json_object_get(root, "token");
1682
                        const char *token_value = json_string_value(token);
1683
                        /* Any plugin this token should be limited to? */
1684
                        json_t *allowed = json_object_get(root, "plugins");
1685
                        /* First of all, add the new token */
1686
                        if(!janus_auth_add_token(token_value)) {
1687
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Error adding token");
1688
                                goto jsondone;
1689
                        }
1690
                        /* Then take care of the plugins access limitations, if any */
1691
                        if(allowed && json_array_size(allowed) > 0) {
1692
                                /* Specify which plugins this token has access to */
1693
                                size_t i = 0;
1694
                                for(i=0; i<json_array_size(allowed); i++) {
1695
                                        json_t *p = json_array_get(allowed, i);
1696
                                        if(!p || !json_is_string(p)) {
1697
                                                /* FIXME Should we fail here? */
1698
                                                JANUS_LOG(LOG_WARN, "Invalid plugin passed to the new token request, skipping...\n");
1699
                                                continue;
1700
                                        }
1701
                                        const gchar *plugin_text = json_string_value(p);
1702
                                        janus_plugin *plugin_t = janus_plugin_find(plugin_text);
1703
                                        if(plugin_t == NULL) {
1704
                                                /* FIXME Should we fail here? */
1705
                                                JANUS_LOG(LOG_WARN, "No such plugin '%s' passed to the new token request, skipping...\n", plugin_text);
1706
                                                continue;
1707
                                        }
1708
                                        if(!janus_auth_allow_plugin(token_value, plugin_t)) {
1709
                                                JANUS_LOG(LOG_WARN, "Error allowing access to '%s' to the new token, bad things may happen...\n", plugin_text);
1710
                                        }
1711
                                }
1712
                        } else {
1713
                                /* No plugin limitation specified, allow all plugins */
1714
                                if(plugins && g_hash_table_size(plugins) > 0) {
1715
                                        GHashTableIter iter;
1716
                                        gpointer value;
1717
                                        g_hash_table_iter_init(&iter, plugins);
1718
                                        while (g_hash_table_iter_next(&iter, NULL, &value)) {
1719
                                                janus_plugin *plugin_t = value;
1720
                                                if(plugin_t == NULL)
1721
                                                        continue;
1722
                                                if(!janus_auth_allow_plugin(token_value, plugin_t)) {
1723
                                                        JANUS_LOG(LOG_WARN, "Error allowing access to '%s' to the new token, bad things may happen...\n", plugin_t->get_package());
1724
                                                }
1725
                                        }
1726
                                }
1727
                        }
1728
                        /* Get the list of plugins this new token can access */
1729
                        json_t *plugins_list = json_array();
1730
                        GList *plugins = janus_auth_list_plugins(token_value);
1731
                        if(plugins != NULL) {
1732
                                GList *tmp = plugins;
1733
                                while(tmp) {
1734
                                        janus_plugin *p = (janus_plugin *)tmp->data;
1735
                                        if(p != NULL)
1736
                                                json_array_append_new(plugins_list, json_string(p->get_package()));
1737
                                        tmp = tmp->next;
1738
                                }
1739
                                g_list_free(plugins);
1740
                                plugins = NULL;
1741
                        }
1742
                        /* Prepare JSON reply */
1743
                        json_t *reply = json_object();
1744
                        json_object_set_new(reply, "janus", json_string("success"));
1745
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1746
                        json_t *data = json_object();
1747
                        json_object_set_new(data, "plugins", plugins_list);
1748
                        json_object_set_new(reply, "data", data);
1749
                        /* Send the success reply */
1750
                        ret = janus_process_success(request, reply);
1751
                        goto jsondone;
1752
                } else if(!strcasecmp(message_text, "list_tokens")) {
1753
                        /* List all the valid tokens */
1754
                        if(!janus_auth_is_enabled()) {
1755
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Token based authentication disabled");
1756
                                goto jsondone;
1757
                        }
1758
                        json_t *tokens_list = json_array();
1759
                        GList *list = janus_auth_list_tokens();
1760
                        if(list != NULL) {
1761
                                GList *tmp = list;
1762
                                while(tmp) {
1763
                                        char *token = (char *)tmp->data;
1764
                                        if(token != NULL) {
1765
                                                GList *plugins = janus_auth_list_plugins(token);
1766
                                                if(plugins != NULL) {
1767
                                                        json_t *t = json_object();
1768
                                                        json_object_set_new(t, "token", json_string(token));
1769
                                                        json_t *plugins_list = json_array();
1770
                                                        GList *tmp2 = plugins;
1771
                                                        while(tmp2) {
1772
                                                                janus_plugin *p = (janus_plugin *)tmp2->data;
1773
                                                                if(p != NULL)
1774
                                                                        json_array_append_new(plugins_list, json_string(p->get_package()));
1775
                                                                tmp2 = tmp2->next;
1776
                                                        }
1777
                                                        g_list_free(plugins);
1778
                                                        plugins = NULL;
1779
                                                        json_object_set_new(t, "allowed_plugins", plugins_list);
1780
                                                        json_array_append_new(tokens_list, t);
1781
                                                }
1782
                                                tmp->data = NULL;
1783
                                                g_free(token);
1784
                                        }
1785
                                        tmp = tmp->next;
1786
                                }
1787
                                g_list_free(list);
1788
                        }
1789
                        /* Prepare JSON reply */
1790
                        json_t *reply = json_object();
1791
                        json_object_set_new(reply, "janus", json_string("success"));
1792
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1793
                        json_t *data = json_object();
1794
                        json_object_set_new(data, "tokens", tokens_list);
1795
                        json_object_set_new(reply, "data", data);
1796
                        /* Send the success reply */
1797
                        ret = janus_process_success(request, reply);
1798
                        goto jsondone;
1799
                } else if(!strcasecmp(message_text, "allow_token")) {
1800
                        /* Allow a valid token valid to access a plugin */
1801
                        if(!janus_auth_is_enabled()) {
1802
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Token based authentication disabled");
1803
                                goto jsondone;
1804
                        }
1805
                        JANUS_VALIDATE_JSON_OBJECT(root, allow_token_parameters,
1806
                                error_code, error_cause, FALSE,
1807
                                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1808
                        if(error_code != 0) {
1809
                                ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1810
                                goto jsondone;
1811
                        }
1812
                        json_t *token = json_object_get(root, "token");
1813
                        const char *token_value = json_string_value(token);
1814
                        /* Check if the token is valid, first */
1815
                        if(!janus_auth_check_token(token_value)) {
1816
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_TOKEN_NOT_FOUND, "Token %s not found", token_value);
1817
                                goto jsondone;
1818
                        }
1819
                        /* Any plugin this token should be limited to? */
1820
                        json_t *allowed = json_object_get(root, "plugins");
1821
                        /* Check the list first */
1822
                        size_t i = 0;
1823
                        gboolean ok = TRUE;
1824
                        for(i=0; i<json_array_size(allowed); i++) {
1825
                                json_t *p = json_array_get(allowed, i);
1826
                                if(!p || !json_is_string(p)) {
1827
                                        /* FIXME Should we fail here? */
1828
                                        JANUS_LOG(LOG_ERR, "Invalid plugin passed to the new token request...\n");
1829
                                        ok = FALSE;
1830
                                        break;
1831
                                }
1832
                                const gchar *plugin_text = json_string_value(p);
1833
                                janus_plugin *plugin_t = janus_plugin_find(plugin_text);
1834
                                if(plugin_t == NULL) {
1835
                                        /* FIXME Should we fail here? */
1836
                                        JANUS_LOG(LOG_ERR, "No such plugin '%s' passed to the new token request...\n", plugin_text);
1837
                                        ok = FALSE;
1838
                                        break;
1839
                                }
1840
                        }
1841
                        if(!ok) {
1842
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (some of the provided plugins are invalid)");
1843
                                goto jsondone;
1844
                        }
1845
                        /* Take care of the plugins access limitations */
1846
                        i = 0;
1847
                        for(i=0; i<json_array_size(allowed); i++) {
1848
                                json_t *p = json_array_get(allowed, i);
1849
                                const gchar *plugin_text = json_string_value(p);
1850
                                janus_plugin *plugin_t = janus_plugin_find(plugin_text);
1851
                                if(!janus_auth_allow_plugin(token_value, plugin_t)) {
1852
                                        /* FIXME Should we notify individual failures? */
1853
                                        JANUS_LOG(LOG_WARN, "Error allowing access to '%s' to the new token, bad things may happen...\n", plugin_text);
1854
                                }
1855
                        }
1856
                        /* Get the list of plugins this new token can now access */
1857
                        json_t *plugins_list = json_array();
1858
                        GList *plugins = janus_auth_list_plugins(token_value);
1859
                        if(plugins != NULL) {
1860
                                GList *tmp = plugins;
1861
                                while(tmp) {
1862
                                        janus_plugin *p = (janus_plugin *)tmp->data;
1863
                                        if(p != NULL)
1864
                                                json_array_append_new(plugins_list, json_string(p->get_package()));
1865
                                        tmp = tmp->next;
1866
                                }
1867
                                g_list_free(plugins);
1868
                                plugins = NULL;
1869
                        }
1870
                        /* Prepare JSON reply */
1871
                        json_t *reply = json_object();
1872
                        json_object_set_new(reply, "janus", json_string("success"));
1873
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1874
                        json_t *data = json_object();
1875
                        json_object_set_new(data, "plugins", plugins_list);
1876
                        json_object_set_new(reply, "data", data);
1877
                        /* Send the success reply */
1878
                        ret = janus_process_success(request, reply);
1879
                        goto jsondone;
1880
                } else if(!strcasecmp(message_text, "disallow_token")) {
1881
                        /* Disallow a valid token valid from accessing a plugin */
1882
                        if(!janus_auth_is_enabled()) {
1883
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Token based authentication disabled");
1884
                                goto jsondone;
1885
                        }
1886
                        JANUS_VALIDATE_JSON_OBJECT(root, allow_token_parameters,
1887
                                error_code, error_cause, FALSE,
1888
                                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1889
                        if(error_code != 0) {
1890
                                ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1891
                                goto jsondone;
1892
                        }
1893
                        json_t *token = json_object_get(root, "token");
1894
                        const char *token_value = json_string_value(token);
1895
                        /* Check if the token is valid, first */
1896
                        if(!janus_auth_check_token(token_value)) {
1897
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_TOKEN_NOT_FOUND, "Token %s not found", token_value);
1898
                                goto jsondone;
1899
                        }
1900
                        /* Any plugin this token should be prevented access to? */
1901
                        json_t *allowed = json_object_get(root, "plugins");
1902
                        /* Check the list first */
1903
                        size_t i = 0;
1904
                        gboolean ok = TRUE;
1905
                        for(i=0; i<json_array_size(allowed); i++) {
1906
                                json_t *p = json_array_get(allowed, i);
1907
                                if(!p || !json_is_string(p)) {
1908
                                        /* FIXME Should we fail here? */
1909
                                        JANUS_LOG(LOG_ERR, "Invalid plugin passed to the new token request...\n");
1910
                                        ok = FALSE;
1911
                                        break;
1912
                                }
1913
                                const gchar *plugin_text = json_string_value(p);
1914
                                janus_plugin *plugin_t = janus_plugin_find(plugin_text);
1915
                                if(plugin_t == NULL) {
1916
                                        /* FIXME Should we fail here? */
1917
                                        JANUS_LOG(LOG_ERR, "No such plugin '%s' passed to the new token request...\n", plugin_text);
1918
                                        ok = FALSE;
1919
                                        break;
1920
                                }
1921
                        }
1922
                        if(!ok) {
1923
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (some of the provided plugins are invalid)");
1924
                                goto jsondone;
1925
                        }
1926
                        /* Take care of the plugins access limitations */
1927
                        i = 0;
1928
                        for(i=0; i<json_array_size(allowed); i++) {
1929
                                json_t *p = json_array_get(allowed, i);
1930
                                const gchar *plugin_text = json_string_value(p);
1931
                                janus_plugin *plugin_t = janus_plugin_find(plugin_text);
1932
                                if(!janus_auth_disallow_plugin(token_value, plugin_t)) {
1933
                                        /* FIXME Should we notify individual failures? */
1934
                                        JANUS_LOG(LOG_WARN, "Error allowing access to '%s' to the new token, bad things may happen...\n", plugin_text);
1935
                                }
1936
                        }
1937
                        /* Get the list of plugins this new token can now access */
1938
                        json_t *plugins_list = json_array();
1939
                        GList *plugins = janus_auth_list_plugins(token_value);
1940
                        if(plugins != NULL) {
1941
                                GList *tmp = plugins;
1942
                                while(tmp) {
1943
                                        janus_plugin *p = (janus_plugin *)tmp->data;
1944
                                        if(p != NULL)
1945
                                                json_array_append_new(plugins_list, json_string(p->get_package()));
1946
                                        tmp = tmp->next;
1947
                                }
1948
                                g_list_free(plugins);
1949
                                plugins = NULL;
1950
                        }
1951
                        /* Prepare JSON reply */
1952
                        json_t *reply = json_object();
1953
                        json_object_set_new(reply, "janus", json_string("success"));
1954
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1955
                        json_t *data = json_object();
1956
                        json_object_set_new(data, "plugins", plugins_list);
1957
                        json_object_set_new(reply, "data", data);
1958
                        /* Send the success reply */
1959
                        ret = janus_process_success(request, reply);
1960
                        goto jsondone;
1961
                } else if(!strcasecmp(message_text, "remove_token")) {
1962
                        /* Invalidate a token for authentication purposes */
1963
                        if(!janus_auth_is_enabled()) {
1964
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Token based authentication disabled");
1965
                                goto jsondone;
1966
                        }
1967
                        JANUS_VALIDATE_JSON_OBJECT(root, token_parameters,
1968
                                error_code, error_cause, FALSE,
1969
                                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1970
                        if(error_code != 0) {
1971
                                ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1972
                                goto jsondone;
1973
                        }
1974
                        json_t *token = json_object_get(root, "token");
1975
                        const char *token_value = json_string_value(token);
1976
                        if(!janus_auth_remove_token(token_value)) {
1977
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Error removing token");
1978
                                goto jsondone;
1979
                        }
1980
                        /* Prepare JSON reply */
1981
                        json_t *reply = json_object();
1982
                        json_object_set_new(reply, "janus", json_string("success"));
1983
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1984
                        /* Send the success reply */
1985
                        ret = janus_process_success(request, reply);
1986
                        goto jsondone;
1987
                } else {
1988
                        /* No message we know of */
1989
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
1990
                        goto jsondone;
1991
                }
1992
        }
1993
        if(session_id < 1) {
1994
                JANUS_LOG(LOG_ERR, "Invalid session\n");
1995
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, NULL);
1996
                goto jsondone;
1997
        }
1998
        if(h && handle_id < 1) {
1999
                JANUS_LOG(LOG_ERR, "Invalid handle\n");
2000
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, NULL);
2001
                goto jsondone;
2002
        }
2003

    
2004
        /* Go on with the processing */
2005
        if(admin_api_secret != NULL) {
2006
                /* There's an API secret, check that the client provided it */
2007
                json_t *secret = json_object_get(root, "admin_secret");
2008
                if(!secret || !json_is_string(secret) || !janus_strcmp_const_time(json_string_value(secret), admin_api_secret)) {
2009
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED, NULL);
2010
                        goto jsondone;
2011
                }
2012
        }
2013

    
2014
        /* If we got here, make sure we have a session (and/or a handle) */
2015
        janus_session *session = janus_session_find(session_id);
2016
        if(!session) {
2017
                JANUS_LOG(LOG_ERR, "Couldn't find any session %"SCNu64"...\n", session_id);
2018
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, "No such session %"SCNu64"", session_id);
2019
                goto jsondone;
2020
        }
2021
        janus_ice_handle *handle = NULL;
2022
        if(handle_id > 0) {
2023
                handle = janus_ice_handle_find(session, handle_id);
2024
                if(!handle) {
2025
                        JANUS_LOG(LOG_ERR, "Couldn't find any handle %"SCNu64" in session %"SCNu64"...\n", handle_id, session_id);
2026
                        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);
2027
                        goto jsondone;
2028
                }
2029
        }
2030

    
2031
        /* What is this? */
2032
        if(handle == NULL) {
2033
                /* Session-related */
2034
                if(strcasecmp(message_text, "list_handles")) {
2035
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
2036
                        goto jsondone;
2037
                }
2038
                /* List handles */
2039
                json_t *list = json_array();
2040
                if(session->ice_handles != NULL && g_hash_table_size(session->ice_handles) > 0) {
2041
                        GHashTableIter iter;
2042
                        gpointer value;
2043
                        janus_mutex_lock(&session->mutex);
2044
                        g_hash_table_iter_init(&iter, session->ice_handles);
2045
                        while (g_hash_table_iter_next(&iter, NULL, &value)) {
2046
                                janus_ice_handle *handle = value;
2047
                                if(handle == NULL) {
2048
                                        continue;
2049
                                }
2050
                                json_array_append_new(list, json_integer(handle->handle_id));
2051
                        }
2052
                        janus_mutex_unlock(&session->mutex);
2053
                }
2054
                /* Prepare JSON reply */
2055
                json_t *reply = json_object();
2056
                json_object_set_new(reply, "janus", json_string("success"));
2057
                json_object_set_new(reply, "transaction", json_string(transaction_text));
2058
                json_object_set_new(reply, "session_id", json_integer(session_id));
2059
                json_object_set_new(reply, "handles", list);
2060
                /* Send the success reply */
2061
                ret = janus_process_success(request, reply);
2062
                goto jsondone;
2063
        } else {
2064
                /* Handle-related */
2065
                if(strcasecmp(message_text, "handle_info")) {
2066
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
2067
                        goto jsondone;
2068
                }
2069
                /* Prepare info */
2070
                janus_mutex_lock(&handle->mutex);
2071
                json_t *info = json_object();
2072
                json_object_set_new(info, "session_id", json_integer(session_id));
2073
                json_object_set_new(info, "session_last_activity", json_integer(session->last_activity));
2074
                if(session->source && session->source->transport)
2075
                        json_object_set_new(info, "session_transport", json_string(session->source->transport->get_package()));
2076
                json_object_set_new(info, "handle_id", json_integer(handle_id));
2077
                json_object_set_new(info, "created", json_integer(handle->created));
2078
                json_object_set_new(info, "send_thread_created", g_atomic_int_get(&handle->send_thread_created) ? json_true() : json_false());
2079
                json_object_set_new(info, "current_time", json_integer(janus_get_monotonic_time()));
2080
                if(handle->app && handle->app_handle && janus_plugin_session_is_alive(handle->app_handle)) {
2081
                        janus_plugin *plugin = (janus_plugin *)handle->app;
2082
                        json_object_set_new(info, "plugin", json_string(plugin->get_package()));
2083
                        if(plugin->query_session) {
2084
                                /* FIXME This check will NOT work with legacy plugins that were compiled BEFORE the method was specified in plugin.h */
2085
                                json_t *query = plugin->query_session(handle->app_handle);
2086
                                if(query != NULL) {
2087
                                        /* Make sure this is a JSON object */
2088
                                        if(!json_is_object(query)) {
2089
                                                JANUS_LOG(LOG_WARN, "Ignoring invalid query response from the plugin (not an object)\n");
2090
                                                json_decref(query);
2091
                                        } else {
2092
                                                json_object_set_new(info, "plugin_specific", query);
2093
                                        }
2094
                                        query = NULL;
2095
                                }
2096
                        }
2097
                }
2098
                json_t *flags = json_object();
2099
                json_object_set_new(flags, "got-offer", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER) ? json_true() : json_false());
2100
                json_object_set_new(flags, "got-answer", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER) ? json_true() : json_false());
2101
                json_object_set_new(flags, "processing-offer", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER) ? json_true() : json_false());
2102
                json_object_set_new(flags, "starting", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_START) ? json_true() : json_false());
2103
                json_object_set_new(flags, "ready", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_READY) ? json_true() : json_false());
2104
                json_object_set_new(flags, "stopped", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP) ? json_true() : json_false());
2105
                json_object_set_new(flags, "alert", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT) ? json_true() : json_false());
2106
                json_object_set_new(flags, "bundle", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE) ? json_true() : json_false());
2107
                json_object_set_new(flags, "rtcp-mux", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX) ? json_true() : json_false());
2108
                json_object_set_new(flags, "trickle", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE) ? json_true() : json_false());
2109
                json_object_set_new(flags, "all-trickles", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALL_TRICKLES) ? json_true() : json_false());
2110
                json_object_set_new(flags, "trickle-synced", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE_SYNCED) ? json_true() : json_false());
2111
                json_object_set_new(flags, "data-channels", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS) ? json_true() : json_false());
2112
                json_object_set_new(flags, "has-audio", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO) ? json_true() : json_false());
2113
                json_object_set_new(flags, "has-video", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO) ? json_true() : json_false());
2114
                json_object_set_new(flags, "plan-b", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PLAN_B) ? json_true() : json_false());
2115
                json_object_set_new(flags, "cleaning", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING) ? json_true() : json_false());
2116
                json_object_set_new(info, "flags", flags);
2117
                if(handle->agent) {
2118
                        json_object_set_new(info, "agent-created", json_integer(handle->agent_created));
2119
                        json_object_set_new(info, "ice-mode", json_string(janus_ice_is_ice_lite_enabled() ? "lite" : "full"));
2120
                        json_object_set_new(info, "ice-role", json_string(handle->controlling ? "controlling" : "controlled"));
2121
                }
2122
                json_t *sdps = json_object();
2123
                if(handle->rtp_profile)
2124
                        json_object_set_new(sdps, "profile", json_string(handle->rtp_profile));
2125
                if(handle->local_sdp)
2126
                        json_object_set_new(sdps, "local", json_string(handle->local_sdp));
2127
                if(handle->remote_sdp)
2128
                        json_object_set_new(sdps, "remote", json_string(handle->remote_sdp));
2129
                json_object_set_new(info, "sdps", sdps);
2130
                if(handle->pending_trickles)
2131
                        json_object_set_new(info, "pending-trickles", json_integer(g_list_length(handle->pending_trickles)));
2132
                if(handle->queued_packets)
2133
                        json_object_set_new(info, "queued-packets", json_integer(g_async_queue_length(handle->queued_packets)));
2134
                json_t *streams = json_array();
2135
                if(handle->audio_stream) {
2136
                        json_t *s = janus_admin_stream_summary(handle->audio_stream);
2137
                        if(s)
2138
                                json_array_append_new(streams, s);
2139
                }
2140
                if(handle->video_stream) {
2141
                        json_t *s = janus_admin_stream_summary(handle->video_stream);
2142
                        if(s)
2143
                                json_array_append_new(streams, s);
2144
                }
2145
                if(handle->data_stream) {
2146
                        json_t *s = janus_admin_stream_summary(handle->data_stream);
2147
                        if(s)
2148
                                json_array_append_new(streams, s);
2149
                }
2150
                json_object_set_new(info, "streams", streams);
2151
                janus_mutex_unlock(&handle->mutex);
2152
                /* Prepare JSON reply */
2153
                json_t *reply = json_object();
2154
                json_object_set_new(reply, "janus", json_string("success"));
2155
                json_object_set_new(reply, "transaction", json_string(transaction_text));
2156
                json_object_set_new(reply, "session_id", json_integer(session_id));
2157
                json_object_set_new(reply, "handle_id", json_integer(handle_id));
2158
                json_object_set_new(reply, "info", info);
2159
                /* Send the success reply */
2160
                ret = janus_process_success(request, reply);
2161
                goto jsondone;
2162
        }
2163

    
2164
jsondone:
2165
        /* Done processing */
2166
        return ret;
2167
}
2168

    
2169
int janus_process_success(janus_request *request, json_t *payload)
2170
{
2171
        if(!request || !payload)
2172
                return -1;
2173
        /* Pass to the right transport plugin */
2174
        JANUS_LOG(LOG_HUGE, "Sending %s API response to %s (%p)\n", request->admin ? "admin" : "Janus", request->transport->get_package(), request->instance);
2175
        return request->transport->send_message(request->instance, request->request_id, request->admin, payload);
2176
}
2177

    
2178
static int janus_process_error_string(janus_request *request, uint64_t session_id, const char *transaction, gint error, gchar *error_string)
2179
{
2180
        if(!request)
2181
                return -1;
2182
        /* Done preparing error */
2183
        JANUS_LOG(LOG_VERB, "[%s] Returning %s API error %d (%s)\n", transaction, request->admin ? "admin" : "Janus", error, error_string);
2184
        /* Prepare JSON error */
2185
        json_t *reply = json_object();
2186
        json_object_set_new(reply, "janus", json_string("error"));
2187
        if(session_id > 0)
2188
                json_object_set_new(reply, "session_id", json_integer(session_id));
2189
        if(transaction != NULL)
2190
                json_object_set_new(reply, "transaction", json_string(transaction));
2191
        json_t *error_data = json_object();
2192
        json_object_set_new(error_data, "code", json_integer(error));
2193
        json_object_set_new(error_data, "reason", json_string(error_string));
2194
        json_object_set_new(reply, "error", error_data);
2195
        /* Pass to the right transport plugin */
2196
        return request->transport->send_message(request->instance, request->request_id, request->admin, reply);
2197
}
2198

    
2199
int janus_process_error(janus_request *request, uint64_t session_id, const char *transaction, gint error, const char *format, ...)
2200
{
2201
        if(!request)
2202
                return -1;
2203
        gchar *error_string = NULL;
2204
        gchar error_buf[512];
2205
        if(format == NULL) {
2206
                /* No error string provided, use the default one */
2207
                error_string = (gchar *)janus_get_api_error(error);
2208
        } else {
2209
                /* This callback has variable arguments (error string) */
2210
                va_list ap;
2211
                va_start(ap, format);
2212
                g_vsnprintf(error_buf, sizeof(error_buf), format, ap);
2213
                va_end(ap);
2214
                error_string = error_buf;
2215
        }
2216
        return janus_process_error_string(request, session_id, transaction, error, error_string);
2217
}
2218

    
2219
/* Admin/monitor helpers */
2220
json_t *janus_admin_stream_summary(janus_ice_stream *stream) {
2221
        if(stream == NULL)
2222
                return NULL;
2223
        json_t *s = json_object();
2224
        json_object_set_new(s, "id", json_integer(stream->stream_id));
2225
        json_object_set_new(s, "ready", json_integer(stream->cdone));
2226
        json_object_set_new(s, "disabled", stream->disabled ? json_true() : json_false());
2227
        json_t *ss = json_object();
2228
        if(stream->audio_ssrc)
2229
                json_object_set_new(ss, "audio", json_integer(stream->audio_ssrc));
2230
        if(stream->video_ssrc)
2231
                json_object_set_new(ss, "video", json_integer(stream->video_ssrc));
2232
        if(stream->audio_ssrc_peer)
2233
                json_object_set_new(ss, "audio-peer", json_integer(stream->audio_ssrc_peer));
2234
        if(stream->video_ssrc_peer)
2235
                json_object_set_new(ss, "video-peer", json_integer(stream->video_ssrc_peer));
2236
        if(stream->video_ssrc_peer_rtx)
2237
                json_object_set_new(ss, "video-peer-rtx", json_integer(stream->video_ssrc_peer_rtx));
2238
        json_object_set_new(s, "ssrc", ss);
2239
        json_t *components = json_array();
2240
        if(stream->rtp_component) {
2241
                json_t *c = janus_admin_component_summary(stream->rtp_component);
2242
                if(c)
2243
                        json_array_append_new(components, c);
2244
        }
2245
        if(stream->rtcp_component) {
2246
                json_t *c = janus_admin_component_summary(stream->rtcp_component);
2247
                if(c)
2248
                        json_array_append_new(components, c);
2249
        }
2250
        json_t *rtcp_stats = NULL;
2251
        if(stream->audio_rtcp_ctx != NULL) {
2252
                rtcp_stats = json_object();
2253
                json_t *audio_rtcp_stats = json_object();
2254
                json_object_set_new(audio_rtcp_stats, "base", json_integer(stream->audio_rtcp_ctx->tb));
2255
                json_object_set_new(audio_rtcp_stats, "lsr", json_integer(janus_rtcp_context_get_lsr(stream->audio_rtcp_ctx)));
2256
                json_object_set_new(audio_rtcp_stats, "lost", json_integer(janus_rtcp_context_get_lost_all(stream->audio_rtcp_ctx, FALSE)));
2257
                json_object_set_new(audio_rtcp_stats, "lost-by-remote", json_integer(janus_rtcp_context_get_lost_all(stream->audio_rtcp_ctx, TRUE)));
2258
                json_object_set_new(audio_rtcp_stats, "jitter-local", json_integer(janus_rtcp_context_get_jitter(stream->audio_rtcp_ctx, FALSE)));
2259
                json_object_set_new(audio_rtcp_stats, "jitter-remote", json_integer(janus_rtcp_context_get_jitter(stream->audio_rtcp_ctx, TRUE)));
2260
                json_object_set_new(rtcp_stats, "audio", audio_rtcp_stats);
2261
        }
2262
        if(stream->video_rtcp_ctx != NULL) {
2263
                if(rtcp_stats == NULL)
2264
                        rtcp_stats = json_object();
2265
                json_t *video_rtcp_stats = json_object();
2266
                json_object_set_new(video_rtcp_stats, "base", json_integer(stream->video_rtcp_ctx->tb));
2267
                json_object_set_new(video_rtcp_stats, "lsr", json_integer(janus_rtcp_context_get_lsr(stream->video_rtcp_ctx)));
2268
                json_object_set_new(video_rtcp_stats, "lost", json_integer(janus_rtcp_context_get_lost_all(stream->video_rtcp_ctx, FALSE)));
2269
                json_object_set_new(video_rtcp_stats, "lost-by-remote", json_integer(janus_rtcp_context_get_lost_all(stream->video_rtcp_ctx, TRUE)));
2270
                json_object_set_new(video_rtcp_stats, "jitter-local", json_integer(janus_rtcp_context_get_jitter(stream->video_rtcp_ctx, FALSE)));
2271
                json_object_set_new(video_rtcp_stats, "jitter-remote", json_integer(janus_rtcp_context_get_jitter(stream->video_rtcp_ctx, TRUE)));
2272
                json_object_set_new(rtcp_stats, "video", video_rtcp_stats);
2273
        }
2274
        if(rtcp_stats != NULL)
2275
                json_object_set_new(s, "rtcp_stats", rtcp_stats);
2276
        json_object_set_new(s, "components", components);
2277
        return s;
2278
}
2279

    
2280
json_t *janus_admin_component_summary(janus_ice_component *component) {
2281
        if(component == NULL)
2282
                return NULL;
2283
        janus_ice_handle *handle = component->stream ? component->stream->handle : NULL;
2284
        json_t *c = json_object();
2285
        json_object_set_new(c, "id", json_integer(component->component_id));
2286
        json_object_set_new(c, "state", json_string(janus_get_ice_state_name(component->state)));
2287
        if(component->component_connected > 0)
2288
                json_object_set_new(c, "connected", json_integer(component->component_connected));
2289
        if(component->local_candidates) {
2290
                json_t *cs = json_array();
2291
                GSList *candidates = component->local_candidates, *i = NULL;
2292
                for (i = candidates; i; i = i->next) {
2293
                        gchar *lc = (gchar *) i->data;
2294
                        if(lc)
2295
                                json_array_append_new(cs, json_string(lc));
2296
                }
2297
                json_object_set_new(c, "local-candidates", cs);
2298
        }
2299
        if(component->remote_candidates) {
2300
                json_t *cs = json_array();
2301
                GSList *candidates = component->remote_candidates, *i = NULL;
2302
                for (i = candidates; i; i = i->next) {
2303
                        gchar *rc = (gchar *) i->data;
2304
                        if(rc)
2305
                                json_array_append_new(cs, json_string(rc));
2306
                }
2307
                json_object_set_new(c, "remote-candidates", cs);
2308
        }
2309
        if(component->selected_pair) {
2310
                json_object_set_new(c, "selected-pair", json_string(component->selected_pair));
2311
        }
2312
        json_t *d = json_object();
2313
        json_t *in_stats = json_object();
2314
        json_t *out_stats = json_object();
2315
        if(component->dtls) {
2316
                janus_dtls_srtp *dtls = component->dtls;
2317
                json_object_set_new(d, "fingerprint", json_string(janus_dtls_get_local_fingerprint()));
2318
                json_object_set_new(d, "remote-fingerprint", json_string(component->stream->remote_fingerprint));
2319
                json_object_set_new(d, "remote-fingerprint-hash", json_string(component->stream->remote_hashing));
2320
                json_object_set_new(d, "dtls-role", json_string(janus_get_dtls_srtp_role(component->stream->dtls_role)));
2321
                json_object_set_new(d, "dtls-state", json_string(janus_get_dtls_srtp_state(dtls->dtls_state)));
2322
                json_object_set_new(d, "valid", dtls->srtp_valid ? json_true() : json_false());
2323
                json_object_set_new(d, "ready", dtls->ready ? json_true() : json_false());
2324
                if(dtls->dtls_connected > 0)
2325
                        json_object_set_new(d, "connected", json_integer(dtls->dtls_connected));
2326
                if(handle && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO)) {
2327
                        json_object_set_new(in_stats, "audio_packets", json_integer(component->in_stats.audio_packets));
2328
                        json_object_set_new(in_stats, "audio_bytes", json_integer(component->in_stats.audio_bytes));
2329
                        json_object_set_new(in_stats, "audio_nacks", json_integer(component->in_stats.audio_nacks));
2330
                        /* Compute the last second stuff too */
2331
                        gint64 now = janus_get_monotonic_time();
2332
                        guint64 bytes = 0;
2333
                        if(component->in_stats.audio_bytes_lastsec) {
2334
                                GList *lastsec = component->in_stats.audio_bytes_lastsec;
2335
                                while(lastsec) {
2336
                                        janus_ice_stats_item *s = (janus_ice_stats_item *)lastsec->data;
2337
                                        if(s && now-s->when < G_USEC_PER_SEC)
2338
                                                bytes += s->bytes;
2339
                                        lastsec = lastsec->next;
2340
                                }
2341
                        }
2342
                        json_object_set_new(in_stats, "audio_bytes_lastsec", json_integer(bytes));
2343
                }
2344
                if(handle && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO)) {
2345
                        json_object_set_new(in_stats, "video_packets", json_integer(component->in_stats.video_packets));
2346
                        json_object_set_new(in_stats, "video_bytes", json_integer(component->in_stats.video_bytes));
2347
                        json_object_set_new(in_stats, "video_nacks", json_integer(component->in_stats.video_nacks));
2348
                        /* Compute the last second stuff too */
2349
                        gint64 now = janus_get_monotonic_time();
2350
                        guint64 bytes = 0;
2351
                        if(component->in_stats.video_bytes_lastsec) {
2352
                                GList *lastsec = component->in_stats.video_bytes_lastsec;
2353
                                while(lastsec) {
2354
                                        janus_ice_stats_item *s = (janus_ice_stats_item *)lastsec->data;
2355
                                        if(s && now-s->when < G_USEC_PER_SEC)
2356
                                                bytes += s->bytes;
2357
                                        lastsec = lastsec->next;
2358
                                }
2359
                        }
2360
                        json_object_set_new(in_stats, "video_bytes_lastsec", json_integer(bytes));
2361
                }
2362
                json_object_set_new(in_stats, "data_packets", json_integer(component->in_stats.data_packets));
2363
                json_object_set_new(in_stats, "data_bytes", json_integer(component->in_stats.data_bytes));
2364
                if(handle && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO)) {
2365
                        json_object_set_new(out_stats, "audio_packets", json_integer(component->out_stats.audio_packets));
2366
                        json_object_set_new(out_stats, "audio_bytes", json_integer(component->out_stats.audio_bytes));
2367
                        json_object_set_new(out_stats, "audio_nacks", json_integer(component->out_stats.audio_nacks));
2368
                }
2369
                if(handle && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO)) {
2370
                        json_object_set_new(out_stats, "video_packets", json_integer(component->out_stats.video_packets));
2371
                        json_object_set_new(out_stats, "video_bytes", json_integer(component->out_stats.video_bytes));
2372
                        json_object_set_new(out_stats, "video_nacks", json_integer(component->out_stats.video_nacks));
2373
                }
2374
                json_object_set_new(out_stats, "data_packets", json_integer(component->out_stats.data_packets));
2375
                json_object_set_new(out_stats, "data_bytes", json_integer(component->out_stats.data_bytes));
2376
#ifdef HAVE_SCTP
2377
                /* FIXME Actually check if this succeeded? */
2378
                json_object_set_new(d, "sctp-association", dtls->sctp ? json_true() : json_false());
2379
#endif
2380
        }
2381
        json_object_set_new(c, "dtls", d);
2382
        json_object_set_new(c, "in_stats", in_stats);
2383
        json_object_set_new(c, "out_stats", out_stats);
2384
        return c;
2385
}
2386

    
2387

    
2388
/* Transports */
2389
void janus_transport_close(gpointer key, gpointer value, gpointer user_data) {
2390
        janus_transport *transport = (janus_transport *)value;
2391
        if(!transport)
2392
                return;
2393
        transport->destroy();
2394
}
2395

    
2396
void janus_transportso_close(gpointer key, gpointer value, gpointer user_data) {
2397
        void *transport = (janus_transport *)value;
2398
        if(!transport)
2399
                return;
2400
        //~ dlclose(transport);
2401
}
2402

    
2403
/* Transport callback interface */
2404
void janus_transport_incoming_request(janus_transport *plugin, void *transport, void *request_id, gboolean admin, json_t *message, json_error_t *error) {
2405
        JANUS_LOG(LOG_VERB, "Got %s API request from %s (%p)\n", admin ? "an admin" : "a Janus", plugin->get_package(), transport);
2406
        /* Create a janus_request instance to handle the request */
2407
        janus_request *request = janus_request_new(plugin, transport, request_id, admin, message);
2408
        GError *tperror = NULL;
2409
        g_thread_pool_push(tasks, request, &tperror);
2410
        if(tperror != NULL) {
2411
                /* Something went wrong... */
2412
                JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to push task in thread pool...\n", tperror->code, tperror->message ? tperror->message : "??");
2413
                json_t *transaction = json_object_get(message, "transaction");
2414
                const char *transaction_text = json_is_string(transaction) ? json_string_value(transaction) : NULL;
2415
                janus_process_error(request, 0, transaction_text, JANUS_ERROR_UNKNOWN, "Thread pool error");
2416
                janus_request_destroy(request);
2417
        }
2418
}
2419

    
2420
void janus_transport_gone(janus_transport *plugin, void *transport) {
2421
        /* Get rid of sessions this transport was handling */
2422
        JANUS_LOG(LOG_VERB, "A %s transport instance has gone away (%p)\n", plugin->get_package(), transport);
2423
        janus_mutex_lock(&sessions_mutex);
2424
        if(sessions && g_hash_table_size(sessions) > 0) {
2425
                GHashTableIter iter;
2426
                gpointer value;
2427
                g_hash_table_iter_init(&iter, sessions);
2428
                while(g_hash_table_iter_next(&iter, NULL, &value)) {
2429
                        janus_session *session = (janus_session *) value;
2430
                        if(!session || session->destroy || session->timeout || session->last_activity == 0)
2431
                                continue;
2432
                        if(session->source && session->source->instance == transport) {
2433
                                JANUS_LOG(LOG_VERB, "  -- Marking Session %"SCNu64" as over\n", session->session_id);
2434
                                session->last_activity = 0;        /* This will trigger a timeout */
2435
                        }
2436
                }
2437
        }
2438
        janus_mutex_unlock(&sessions_mutex);
2439
}
2440

    
2441
gboolean janus_transport_is_api_secret_needed(janus_transport *plugin) {
2442
        return api_secret != NULL;
2443
}
2444

    
2445
gboolean janus_transport_is_api_secret_valid(janus_transport *plugin, const char *apisecret) {
2446
        if(api_secret == NULL)
2447
                return TRUE;
2448
        return apisecret && janus_strcmp_const_time(apisecret, api_secret);
2449
}
2450

    
2451
gboolean janus_transport_is_auth_token_needed(janus_transport *plugin) {
2452
        return janus_auth_is_enabled();
2453
}
2454

    
2455
gboolean janus_transport_is_auth_token_valid(janus_transport *plugin, const char *token) {
2456
        if(!janus_auth_is_enabled())
2457
                return TRUE;
2458
        return token && janus_auth_check_token(token);
2459
}
2460

    
2461
void janus_transport_task(gpointer data, gpointer user_data) {
2462
        JANUS_LOG(LOG_VERB, "Transport task pool, serving request\n");
2463
        janus_request *request = (janus_request *)data;
2464
        if(request == NULL) {
2465
                JANUS_LOG(LOG_ERR, "Missing request\n");
2466
                return;
2467
        }
2468
        if(!request->admin)
2469
                janus_process_incoming_request(request);
2470
        else
2471
                janus_process_incoming_admin_request(request);
2472
        /* Done */
2473
        janus_request_destroy(request);
2474
}
2475

    
2476

    
2477
/* Event handlers */
2478
void janus_eventhandler_close(gpointer key, gpointer value, gpointer user_data) {
2479
        janus_eventhandler *eventhandler = (janus_eventhandler *)value;
2480
        if(!eventhandler)
2481
                return;
2482
        eventhandler->destroy();
2483
}
2484

    
2485
void janus_eventhandlerso_close(gpointer key, gpointer value, gpointer user_data) {
2486
        void *eventhandler = (janus_eventhandler *)value;
2487
        if(!eventhandler)
2488
                return;
2489
        //~ dlclose(eventhandler);
2490
}
2491

    
2492

    
2493
/* Plugins */
2494
void janus_plugin_close(gpointer key, gpointer value, gpointer user_data) {
2495
        janus_plugin *plugin = (janus_plugin *)value;
2496
        if(!plugin)
2497
                return;
2498
        plugin->destroy();
2499
}
2500

    
2501
void janus_pluginso_close(gpointer key, gpointer value, gpointer user_data) {
2502
        void *plugin = (janus_plugin *)value;
2503
        if(!plugin)
2504
                return;
2505
        //~ dlclose(plugin);
2506
}
2507

    
2508
janus_plugin *janus_plugin_find(const gchar *package) {
2509
        if(package != NULL && plugins != NULL)        /* FIXME Do we need to fix the key pointer? */
2510
                return g_hash_table_lookup(plugins, package);
2511
        return NULL;
2512
}
2513

    
2514

    
2515
/* Plugin callback interface */
2516
int janus_plugin_push_event(janus_plugin_session *plugin_session, janus_plugin *plugin, const char *transaction, json_t *message, json_t *jsep) {
2517
        if(!plugin || !message)
2518
                return -1;
2519
        if(!plugin_session || plugin_session < (janus_plugin_session *)0x1000 ||
2520
                        !janus_plugin_session_is_alive(plugin_session) || plugin_session->stopped)
2521
                return -2;
2522
        janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
2523
        if(!ice_handle || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP))
2524
                return JANUS_ERROR_SESSION_NOT_FOUND;
2525
        janus_session *session = ice_handle->session;
2526
        if(!session || session->destroy)
2527
                return JANUS_ERROR_SESSION_NOT_FOUND;
2528
        /* Make sure this is a JSON object */
2529
        if(!json_is_object(message)) {
2530
                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (JSON error: not an object)\n", ice_handle->handle_id);
2531
                return JANUS_ERROR_INVALID_JSON_OBJECT;
2532
        }
2533
        /* Attach JSEP if possible? */
2534
        const char *sdp_type = json_string_value(json_object_get(jsep, "type"));
2535
        const char *sdp = json_string_value(json_object_get(jsep, "sdp"));
2536
        json_t *merged_jsep = NULL;
2537
        if(sdp_type != NULL && sdp != NULL) {
2538
                merged_jsep = janus_plugin_handle_sdp(plugin_session, plugin, sdp_type, sdp);
2539
                if(merged_jsep == NULL) {
2540
                        if(ice_handle == NULL || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2541
                                        || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)) {
2542
                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (handle not available anymore or negotiation stopped)\n", ice_handle->handle_id);
2543
                                return JANUS_ERROR_HANDLE_NOT_FOUND;
2544
                        } else {
2545
                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (JSON error: problem with the SDP)\n", ice_handle->handle_id);
2546
                                return JANUS_ERROR_JSEP_INVALID_SDP;
2547
                        }
2548
                }
2549
        }
2550
        /* Reference the payload, as the plugin may still need it and will do a decref itself */
2551
        json_incref(message);
2552
        /* Prepare JSON event */
2553
        json_t *event = json_object();
2554
        json_object_set_new(event, "janus", json_string("event"));
2555
        json_object_set_new(event, "session_id", json_integer(session->session_id));
2556
        json_object_set_new(event, "sender", json_integer(ice_handle->handle_id));
2557
        if(transaction != NULL)
2558
                json_object_set_new(event, "transaction", json_string(transaction));
2559
        json_t *plugin_data = json_object();
2560
        json_object_set_new(plugin_data, "plugin", json_string(plugin->get_package()));
2561
        json_object_set_new(plugin_data, "data", message);
2562
        json_object_set_new(event, "plugindata", plugin_data);
2563
        if(merged_jsep != NULL)
2564
                json_object_set_new(event, "jsep", merged_jsep);
2565
        /* Send the event */
2566
        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Sending event to transport...\n", ice_handle->handle_id);
2567
        janus_session_notify_event(session->session_id, event);
2568

    
2569
        if(jsep != NULL && janus_events_is_enabled()) {
2570
                /* Notify event handlers as well */
2571
                janus_events_notify_handlers(JANUS_EVENT_TYPE_JSEP,
2572
                        session->session_id, ice_handle->handle_id, "local", sdp_type, sdp);
2573
        }
2574

    
2575
        return JANUS_OK;
2576
}
2577

    
2578
json_t *janus_plugin_handle_sdp(janus_plugin_session *plugin_session, janus_plugin *plugin, const char *sdp_type, const char *sdp) {
2579
        if(!plugin_session || plugin_session < (janus_plugin_session *)0x1000 ||
2580
                        !janus_plugin_session_is_alive(plugin_session) || plugin_session->stopped ||
2581
                        plugin == NULL || sdp_type == NULL || sdp == NULL) {
2582
                JANUS_LOG(LOG_ERR, "Invalid arguments\n");
2583
                return NULL;
2584
        }
2585
        janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
2586
        //~ if(ice_handle == NULL || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_READY)) {
2587
        if(ice_handle == NULL) {
2588
                JANUS_LOG(LOG_ERR, "Invalid ICE handle\n");
2589
                return NULL;
2590
        }
2591
        int offer = 0;
2592
        if(!strcasecmp(sdp_type, "offer")) {
2593
                /* This is an offer from a plugin */
2594
                offer = 1;
2595
                janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER);
2596
                janus_flags_clear(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER);
2597
        } else if(!strcasecmp(sdp_type, "answer")) {
2598
                /* This is an answer from a plugin */
2599
                janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER);
2600
        } else {
2601
                /* TODO Handle other messages */
2602
                JANUS_LOG(LOG_ERR, "Unknown type '%s'\n", sdp_type);
2603
                return NULL;
2604
        }
2605
        /* Is this valid SDP? */
2606
        int audio = 0, video = 0, data = 0, bundle = 0, rtcpmux = 0, trickle = 0;
2607
        janus_sdp *parsed_sdp = janus_sdp_preparse(sdp, &audio, &video, &data, &bundle, &rtcpmux, &trickle);
2608
        if(parsed_sdp == NULL) {
2609
                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Couldn't parse SDP...\n", ice_handle->handle_id);
2610
                return NULL;
2611
        }
2612
        janus_sdp_free(parsed_sdp);
2613
        gboolean updating = FALSE;
2614
        if(offer) {
2615
                /* We still don't have a local ICE setup */
2616
                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio %s been negotiated\n", ice_handle->handle_id, audio ? "has" : "has NOT");
2617
                if(audio > 1) {
2618
                        JANUS_LOG(LOG_ERR, "[%"SCNu64"] More than one audio line? only going to negotiate one...\n", ice_handle->handle_id);
2619
                }
2620
                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Video %s been negotiated\n", ice_handle->handle_id, video ? "has" : "has NOT");
2621
                if(video > 1) {
2622
                        JANUS_LOG(LOG_ERR, "[%"SCNu64"] More than one video line? only going to negotiate one...\n", ice_handle->handle_id);
2623
                }
2624
                JANUS_LOG(LOG_VERB, "[%"SCNu64"] SCTP/DataChannels %s been negotiated\n", ice_handle->handle_id, data ? "have" : "have NOT");
2625
                if(data > 1) {
2626
                        JANUS_LOG(LOG_ERR, "[%"SCNu64"] More than one data line? only going to negotiate one...\n", ice_handle->handle_id);
2627
                }
2628
#ifndef HAVE_SCTP
2629
                if(data) {
2630
                        JANUS_LOG(LOG_WARN, "[%"SCNu64"]   -- DataChannels have been negotiated, but support for them has not been compiled...\n", ice_handle->handle_id);
2631
                }
2632
#endif
2633
                /* Are we still cleaning up from a previous media session? */
2634
                if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING)) {
2635
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Still cleaning up from a previous media session, let's wait a bit...\n", ice_handle->handle_id);
2636
                        gint64 waited = 0;
2637
                        while(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING)) {
2638
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Still cleaning up from a previous media session, let's wait a bit...\n", ice_handle->handle_id);
2639
                                g_usleep(100000);
2640
                                waited += 100000;
2641
                                if(waited >= 3*G_USEC_PER_SEC) {
2642
                                        JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Waited 3 seconds, that's enough!\n", ice_handle->handle_id);
2643
                                        break;
2644
                                }
2645
                        }
2646
                }
2647
                if(ice_handle->agent == NULL) {
2648
                        /* Process SDP in order to setup ICE locally (this is going to result in an answer from the browser) */
2649
                        if(janus_ice_setup_local(ice_handle, 0, audio, video, data, bundle, rtcpmux, trickle) < 0) {
2650
                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error setting ICE locally\n", ice_handle->handle_id);
2651
                                return NULL;
2652
                        }
2653
                } else {
2654
                        updating = TRUE;
2655
                        JANUS_LOG(LOG_INFO, "[%"SCNu64"] Updating existing session\n", ice_handle->handle_id);
2656
                }
2657
        }
2658
        if(!updating) {
2659
                /* Wait for candidates-done callback */
2660
                while(ice_handle->cdone < ice_handle->streams_num) {
2661
                        if(ice_handle == NULL || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2662
                                        || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)) {
2663
                                JANUS_LOG(LOG_WARN, "[%"SCNu64"] Handle detached or PC closed, giving up...!\n", ice_handle ? ice_handle->handle_id : 0);
2664
                                return NULL;
2665
                        }
2666
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Waiting for candidates-done callback...\n", ice_handle->handle_id);
2667
                        g_usleep(100000);
2668
                        if(ice_handle->cdone < 0) {
2669
                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error gathering candidates!\n", ice_handle->handle_id);
2670
                                return NULL;
2671
                        }
2672
                }
2673
        }
2674
        /* Anonymize SDP */
2675
        char *sdp_stripped = janus_sdp_anonymize(sdp);
2676
        if(sdp_stripped == NULL) {
2677
                /* Invalid SDP */
2678
                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Invalid SDP\n", ice_handle->handle_id);
2679
                return NULL;
2680
        }
2681
        /* Add our details */
2682
        char *sdp_merged = janus_sdp_merge(ice_handle, sdp_stripped);
2683
        if(sdp_merged == NULL) {
2684
                /* Couldn't merge SDP */
2685
                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error merging SDP\n", ice_handle->handle_id);
2686
                g_free(sdp_stripped);
2687
                return NULL;
2688
        }
2689
        /* FIXME Any disabled m-line? */
2690
        if(strstr(sdp_merged, "m=audio 0")) {
2691
                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio disabled via SDP\n", ice_handle->handle_id);
2692
                if(!janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)
2693
                                || (!video && !data)) {
2694
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Marking audio stream as disabled\n", ice_handle->handle_id);
2695
                        janus_ice_stream *stream = g_hash_table_lookup(ice_handle->streams, GUINT_TO_POINTER(ice_handle->audio_id));
2696
                        if(stream)
2697
                                stream->disabled = TRUE;
2698
                }
2699
        }
2700
        if(strstr(sdp_merged, "m=video 0")) {
2701
                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Video disabled via SDP\n", ice_handle->handle_id);
2702
                if(!janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)
2703
                                || (!audio && !data)) {
2704
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Marking video stream as disabled\n", ice_handle->handle_id);
2705
                        janus_ice_stream *stream = NULL;
2706
                        if(!janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
2707
                                stream = g_hash_table_lookup(ice_handle->streams, GUINT_TO_POINTER(ice_handle->video_id));
2708
                        } else {
2709
                                gint id = ice_handle->audio_id > 0 ? ice_handle->audio_id : ice_handle->video_id;
2710
                                stream = g_hash_table_lookup(ice_handle->streams, GUINT_TO_POINTER(id));
2711
                        }
2712
                        if(stream)
2713
                                stream->disabled = TRUE;
2714
                }
2715
        }
2716
        if(strstr(sdp_merged, "m=application 0 DTLS/SCTP")) {
2717
                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Data Channel disabled via SDP\n", ice_handle->handle_id);
2718
                if(!janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)
2719
                                || (!audio && !video)) {
2720
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Marking data channel stream as disabled\n", ice_handle->handle_id);
2721
                        janus_ice_stream *stream = NULL;
2722
                        if(!janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
2723
                                stream = g_hash_table_lookup(ice_handle->streams, GUINT_TO_POINTER(ice_handle->data_id));
2724
                        } else {
2725
                                gint id = ice_handle->audio_id > 0 ? ice_handle->audio_id : (ice_handle->video_id > 0 ? ice_handle->video_id : ice_handle->data_id);
2726
                                stream = g_hash_table_lookup(ice_handle->streams, GUINT_TO_POINTER(id));
2727
                        }
2728
                        if(stream)
2729
                                stream->disabled = TRUE;
2730
                }
2731
        }
2732

    
2733
        if(!updating) {
2734
                if(offer) {
2735
                        /* We set the flag to wait for an answer before handling trickle candidates */
2736
                        janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
2737
                } else {
2738
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Done! Ready to setup remote candidates and send connectivity checks...\n", ice_handle->handle_id);
2739
                        if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
2740
                                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);
2741
                                if(audio) {
2742
                                        /* Get rid of video and data, if present */
2743
                                        if(ice_handle->streams && ice_handle->video_stream) {
2744
                                                ice_handle->audio_stream->video_ssrc = ice_handle->video_stream->video_ssrc;
2745
                                                ice_handle->audio_stream->video_ssrc_peer = ice_handle->video_stream->video_ssrc_peer;
2746
                                                ice_handle->audio_stream->video_ssrc_peer_rtx = ice_handle->video_stream->video_ssrc_peer_rtx;
2747
                                                nice_agent_attach_recv(ice_handle->agent, ice_handle->video_stream->stream_id, 1, g_main_loop_get_context (ice_handle->iceloop), NULL, NULL);
2748
                                                if(!janus_ice_is_rtcpmux_forced())
2749
                                                        nice_agent_attach_recv(ice_handle->agent, ice_handle->video_stream->stream_id, 2, g_main_loop_get_context (ice_handle->iceloop), NULL, NULL);
2750
                                                nice_agent_remove_stream(ice_handle->agent, ice_handle->video_stream->stream_id);
2751
                                                janus_ice_stream_free(ice_handle->streams, ice_handle->video_stream);
2752
                                        }
2753
                                        ice_handle->video_stream = NULL;
2754
                                        ice_handle->video_id = 0;
2755
                                        if(ice_handle->streams && ice_handle->data_stream) {
2756
                                                nice_agent_attach_recv(ice_handle->agent, ice_handle->data_stream->stream_id, 1, g_main_loop_get_context (ice_handle->iceloop), NULL, NULL);
2757
                                                nice_agent_remove_stream(ice_handle->agent, ice_handle->data_stream->stream_id);
2758
                                                janus_ice_stream_free(ice_handle->streams, ice_handle->data_stream);
2759
                                        }
2760
                                        ice_handle->data_stream = NULL;
2761
                                        ice_handle->data_id = 0;
2762
                                        if(!video) {
2763
                                                ice_handle->audio_stream->video_ssrc = 0;
2764
                                                ice_handle->audio_stream->video_ssrc_peer = 0;
2765
                                                g_free(ice_handle->audio_stream->video_rtcp_ctx);
2766
                                                ice_handle->audio_stream->video_rtcp_ctx = NULL;
2767
                                        }
2768
                                } else if(video) {
2769
                                        /* Get rid of data, if present */
2770
                                        if(ice_handle->streams && ice_handle->data_stream) {
2771
                                                nice_agent_attach_recv(ice_handle->agent, ice_handle->data_stream->stream_id, 1, g_main_loop_get_context (ice_handle->iceloop), NULL, NULL);
2772
                                                nice_agent_remove_stream(ice_handle->agent, ice_handle->data_stream->stream_id);
2773
                                                janus_ice_stream_free(ice_handle->streams, ice_handle->data_stream);
2774
                                        }
2775
                                        ice_handle->data_stream = NULL;
2776
                                        ice_handle->data_id = 0;
2777
                                }
2778
                        }
2779
                        if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX) && !janus_ice_is_rtcpmux_forced()) {
2780
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- rtcp-mux is supported by the browser, getting rid of RTCP components, if any...\n", ice_handle->handle_id);
2781
                                if(ice_handle->audio_stream && ice_handle->audio_stream->rtcp_component && ice_handle->audio_stream->components != NULL) {
2782
                                        nice_agent_attach_recv(ice_handle->agent, ice_handle->audio_id, 2, g_main_loop_get_context (ice_handle->iceloop), NULL, NULL);
2783
                                        /* Free the component */
2784
                                        janus_ice_component_free(ice_handle->audio_stream->components, ice_handle->audio_stream->rtcp_component);
2785
                                        ice_handle->audio_stream->rtcp_component = NULL;
2786
                                        /* Create a dummy candidate and enforce it as the one to use for this now unneeded component */
2787
                                        NiceCandidate *c = nice_candidate_new(NICE_CANDIDATE_TYPE_HOST);
2788
                                        c->component_id = 2;
2789
                                        c->stream_id = ice_handle->audio_stream->stream_id;
2790
#ifndef HAVE_LIBNICE_TCP
2791
                                        c->transport = NICE_CANDIDATE_TRANSPORT_UDP;
2792
#endif
2793
                                        strncpy(c->foundation, "1", NICE_CANDIDATE_MAX_FOUNDATION);
2794
                                        c->priority = 1;
2795
                                        nice_address_set_from_string(&c->addr, "127.0.0.1");
2796
                                        nice_address_set_port(&c->addr, janus_ice_get_rtcpmux_blackhole_port());
2797
                                        c->username = g_strdup(ice_handle->audio_stream->ruser);
2798
                                        c->password = g_strdup(ice_handle->audio_stream->rpass);
2799
                                        if(!nice_agent_set_selected_remote_candidate(ice_handle->agent, ice_handle->audio_stream->stream_id, 2, c)) {
2800
                                                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);
2801
                                                nice_candidate_free(c);
2802
                                        }
2803
                                }
2804
                                if(ice_handle->video_stream && ice_handle->video_stream->rtcp_component && ice_handle->video_stream->components != NULL) {
2805
                                        nice_agent_attach_recv(ice_handle->agent, ice_handle->video_id, 2, g_main_loop_get_context (ice_handle->iceloop), NULL, NULL);
2806
                                        /* Free the component */
2807
                                        janus_ice_component_free(ice_handle->video_stream->components, ice_handle->video_stream->rtcp_component);
2808
                                        ice_handle->video_stream->rtcp_component = NULL;
2809
                                        /* Create a dummy candidate and enforce it as the one to use for this now unneeded component */
2810
                                        NiceCandidate *c = nice_candidate_new(NICE_CANDIDATE_TYPE_HOST);
2811
                                        c->component_id = 2;
2812
                                        c->stream_id = ice_handle->video_stream->stream_id;
2813
#ifndef HAVE_LIBNICE_TCP
2814
                                        c->transport = NICE_CANDIDATE_TRANSPORT_UDP;
2815
#endif
2816
                                        strncpy(c->foundation, "1", NICE_CANDIDATE_MAX_FOUNDATION);
2817
                                        c->priority = 1;
2818
                                        nice_address_set_from_string(&c->addr, "127.0.0.1");
2819
                                        nice_address_set_port(&c->addr, janus_ice_get_rtcpmux_blackhole_port());
2820
                                        c->username = g_strdup(ice_handle->video_stream->ruser);
2821
                                        c->password = g_strdup(ice_handle->video_stream->rpass);
2822
                                        if(!nice_agent_set_selected_remote_candidate(ice_handle->agent, ice_handle->video_stream->stream_id, 2, c)) {
2823
                                                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);
2824
                                                nice_candidate_free(c);
2825
                                        }
2826
                                }
2827
                        }
2828
                        janus_mutex_lock(&ice_handle->mutex);
2829
                        /* We got our answer */
2830
                        janus_flags_clear(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
2831
                        /* Any pending trickles? */
2832
                        if(ice_handle->pending_trickles) {
2833
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Processing %d pending trickle candidates\n", ice_handle->handle_id, g_list_length(ice_handle->pending_trickles));
2834
                                GList *temp = NULL;
2835
                                while(ice_handle->pending_trickles) {
2836
                                        temp = g_list_first(ice_handle->pending_trickles);
2837
                                        ice_handle->pending_trickles = g_list_remove_link(ice_handle->pending_trickles, temp);
2838
                                        janus_ice_trickle *trickle = (janus_ice_trickle *)temp->data;
2839
                                        g_list_free(temp);
2840
                                        if(trickle == NULL)
2841
                                                continue;
2842
                                        if((janus_get_monotonic_time() - trickle->received) > 15*G_USEC_PER_SEC) {
2843
                                                /* FIXME Candidate is too old, discard it */
2844
                                                janus_ice_trickle_destroy(trickle);
2845
                                                /* FIXME We should report that */
2846
                                                continue;
2847
                                        }
2848
                                        json_t *candidate = trickle->candidate;
2849
                                        if(candidate == NULL) {
2850
                                                janus_ice_trickle_destroy(trickle);
2851
                                                continue;
2852
                                        }
2853
                                        if(json_is_object(candidate)) {
2854
                                                /* We got a single candidate */
2855
                                                int error = 0;
2856
                                                const char *error_string = NULL;
2857
                                                if((error = janus_ice_trickle_parse(ice_handle, candidate, &error_string)) != 0) {
2858
                                                        /* FIXME We should report the error parsing the trickle candidate */
2859
                                                }
2860
                                        } else if(json_is_array(candidate)) {
2861
                                                /* We got multiple candidates in an array */
2862
                                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Got multiple candidates (%zu)\n", ice_handle->handle_id, json_array_size(candidate));
2863
                                                if(json_array_size(candidate) > 0) {
2864
                                                        /* Handle remote candidates */
2865
                                                        size_t i = 0;
2866
                                                        for(i=0; i<json_array_size(candidate); i++) {
2867
                                                                json_t *c = json_array_get(candidate, i);
2868
                                                                /* FIXME We don't care if any trickle fails to parse */
2869
                                                                janus_ice_trickle_parse(ice_handle, c, NULL);
2870
                                                        }
2871
                                                }
2872
                                        }
2873
                                        /* Done, free candidate */
2874
                                        janus_ice_trickle_destroy(trickle);
2875
                                }
2876
                        }
2877
                        /* This was an answer, check if it's time to start ICE */
2878
                        if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE) &&
2879
                                        !janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALL_TRICKLES)) {
2880
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- ICE Trickling is supported by the browser, waiting for remote candidates...\n", ice_handle->handle_id);
2881
                                janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_START);
2882
                        } else {
2883
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Done! Sending connectivity checks...\n", ice_handle->handle_id);
2884
                                if(ice_handle->audio_id > 0) {
2885
                                        janus_ice_setup_remote_candidates(ice_handle, ice_handle->audio_id, 1);
2886
                                        if(!janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX))        /* http://tools.ietf.org/html/rfc5761#section-5.1.3 */
2887
                                                janus_ice_setup_remote_candidates(ice_handle, ice_handle->audio_id, 2);
2888
                                }
2889
                                if(ice_handle->video_id > 0) {
2890
                                        janus_ice_setup_remote_candidates(ice_handle, ice_handle->video_id, 1);
2891
                                        if(!janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX))        /* http://tools.ietf.org/html/rfc5761#section-5.1.3 */
2892
                                                janus_ice_setup_remote_candidates(ice_handle, ice_handle->video_id, 2);
2893
                                }
2894
                                if(ice_handle->data_id > 0) {
2895
                                        janus_ice_setup_remote_candidates(ice_handle, ice_handle->data_id, 1);
2896
                                }
2897
                        }
2898
                        janus_mutex_unlock(&ice_handle->mutex);
2899
                }
2900
        }
2901

    
2902
        /* Prepare JSON event */
2903
        json_t *jsep = json_object();
2904
        json_object_set_new(jsep, "type", json_string(sdp_type));
2905
        json_object_set_new(jsep, "sdp", json_string(sdp_merged));
2906
        g_free(sdp_stripped);
2907
        //~ g_free(sdp_merged);
2908
        ice_handle->local_sdp = sdp_merged;
2909
        return jsep;
2910
}
2911

    
2912
void janus_plugin_relay_rtp(janus_plugin_session *plugin_session, int video, char *buf, int len) {
2913
        if((plugin_session < (janus_plugin_session *)0x1000) || plugin_session->stopped || buf == NULL || len < 1)
2914
                return;
2915
        janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
2916
        if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2917
                        || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
2918
                return;
2919
        janus_ice_relay_rtp(handle, video, buf, len);
2920
}
2921

    
2922
void janus_plugin_relay_rtcp(janus_plugin_session *plugin_session, int video, char *buf, int len) {
2923
        if((plugin_session < (janus_plugin_session *)0x1000) || plugin_session->stopped || buf == NULL || len < 1)
2924
                return;
2925
        janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
2926
        if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2927
                        || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
2928
                return;
2929
        janus_ice_relay_rtcp(handle, video, buf, len);
2930
}
2931

    
2932
void janus_plugin_relay_data(janus_plugin_session *plugin_session, char *buf, int len) {
2933
        if((plugin_session < (janus_plugin_session *)0x1000) || plugin_session->stopped || buf == NULL || len < 1)
2934
                return;
2935
        janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
2936
        if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2937
                        || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
2938
                return;
2939
#ifdef HAVE_SCTP
2940
        janus_ice_relay_data(handle, buf, len);
2941
#else
2942
        JANUS_LOG(LOG_WARN, "Asked to relay data, but Data Channels support has not been compiled...\n");
2943
#endif
2944
}
2945

    
2946
void janus_plugin_close_pc(janus_plugin_session *plugin_session) {
2947
        /* A plugin asked to get rid of a PeerConnection */
2948
        if((plugin_session < (janus_plugin_session *)0x1000) || !janus_plugin_session_is_alive(plugin_session) || plugin_session->stopped)
2949
                return;
2950
        janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
2951
        if(!ice_handle)
2952
                return;
2953
        if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2954
                        || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
2955
                return;
2956
        janus_session *session = (janus_session *)ice_handle->session;
2957
        if(!session)
2958
                return;
2959

    
2960
        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Plugin asked to hangup PeerConnection: sending alert\n", ice_handle->handle_id);
2961
        /* Send an alert on all the DTLS connections */
2962
        janus_ice_webrtc_hangup(ice_handle);
2963
}
2964

    
2965
void janus_plugin_end_session(janus_plugin_session *plugin_session) {
2966
        /* A plugin asked to get rid of a handle */
2967
        if((plugin_session < (janus_plugin_session *)0x1000) || !janus_plugin_session_is_alive(plugin_session) || plugin_session->stopped)
2968
                return;
2969
        janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
2970
        if(!ice_handle)
2971
                return;
2972
        janus_session *session = (janus_session *)ice_handle->session;
2973
        if(!session)
2974
                return;
2975
        /* Destroy the handle */
2976
        janus_ice_handle_destroy(session, ice_handle->handle_id);
2977
        janus_mutex_lock(&session->mutex);
2978
        g_hash_table_remove(session->ice_handles, &ice_handle->handle_id);
2979
        janus_mutex_unlock(&session->mutex);
2980
}
2981

    
2982
void janus_plugin_notify_event(janus_plugin_session *plugin_session, json_t *event) {
2983
        /* A plugin asked to notify an event to the handlers */
2984
        if(!event || !json_is_object(event))
2985
                return;
2986
        if((plugin_session < (janus_plugin_session *)0x1000) || !janus_plugin_session_is_alive(plugin_session) || plugin_session->stopped) {
2987
                json_decref(event);
2988
                return;
2989
        }
2990
        janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
2991
        if(!ice_handle) {
2992
                json_decref(event);
2993
                return;
2994
        }
2995
        janus_session *session = (janus_session *)ice_handle->session;
2996
        if(!session) {
2997
                json_decref(event);
2998
                return;
2999
        }
3000
        janus_plugin *plugin_t = (janus_plugin *)ice_handle->app;
3001
        /* Notify event handlers */
3002
        if(janus_events_is_enabled()) {
3003
                janus_events_notify_handlers(JANUS_EVENT_TYPE_PLUGIN,
3004
                        session->session_id, ice_handle->handle_id, plugin_t->get_package(), event);
3005
        } else {
3006
                json_decref(event);
3007
        }
3008
}
3009

    
3010

    
3011
static void janus_detect_local_ip(gchar *buf, size_t buflen) {
3012
        JANUS_LOG(LOG_VERB, "Autodetecting local IP...\n");
3013
        struct sockaddr_in addr;
3014
        socklen_t len;
3015
        int fd = socket(AF_INET, SOCK_DGRAM, 0);
3016
        if (fd == -1)
3017
                goto error;
3018
        addr.sin_family = AF_INET;
3019
        addr.sin_port = htons(1);
3020
        inet_pton(AF_INET, "1.2.3.4", &addr.sin_addr.s_addr);
3021
        if (connect(fd, (const struct sockaddr*) &addr, sizeof(addr)) < 0)
3022
                goto error;
3023
        len = sizeof(addr);
3024
        if (getsockname(fd, (struct sockaddr*) &addr, &len) < 0)
3025
                goto error;
3026
        if (getnameinfo((const struct sockaddr*) &addr, sizeof(addr),
3027
                        buf, buflen,
3028
                        NULL, 0, NI_NUMERICHOST) != 0)
3029
                goto error;
3030
        close(fd);
3031
        return;
3032
error:
3033
        if (fd != -1)
3034
                close(fd);
3035
        JANUS_LOG(LOG_VERB, "Couldn't find any address! using 127.0.0.1 as the local IP... (which is NOT going to work out of your machine)\n");
3036
        g_strlcpy(buf, "127.0.0.1", buflen);
3037
}
3038

    
3039

    
3040
/* Main */
3041
gint main(int argc, char *argv[])
3042
{
3043
        /* Core dumps may be disallowed by parent of this process; change that */
3044
        struct rlimit core_limits;
3045
        core_limits.rlim_cur = core_limits.rlim_max = RLIM_INFINITY;
3046
        setrlimit(RLIMIT_CORE, &core_limits);
3047

    
3048
        struct gengetopt_args_info args_info;
3049
        /* Let's call our cmdline parser */
3050
        if(cmdline_parser(argc, argv, &args_info) != 0)
3051
                exit(1);
3052

    
3053
        /* Any configuration to open? */
3054
        if(args_info.config_given) {
3055
                config_file = g_strdup(args_info.config_arg);
3056
        }
3057
        if(args_info.configs_folder_given) {
3058
                configs_folder = g_strdup(args_info.configs_folder_arg);
3059
        } else {
3060
                configs_folder = g_strdup (CONFDIR);
3061
        }
3062
        if(config_file == NULL) {
3063
                char file[255];
3064
                g_snprintf(file, 255, "%s/janus.cfg", configs_folder);
3065
                config_file = g_strdup(file);
3066
        }
3067
        if((config = janus_config_parse(config_file)) == NULL) {
3068
                if(args_info.config_given) {
3069
                        /* We only give up if the configuration file was explicitly provided */
3070
                        g_print("Error reading configuration from %s\n", config_file);
3071
                        exit(1);
3072
                }
3073
                g_print("Error reading/parsing the configuration file, going on with the defaults and the command line arguments\n");
3074
                config = janus_config_create("janus.cfg");
3075
                if(config == NULL) {
3076
                        /* If we can't even create an empty configuration, something's definitely wrong */
3077
                        exit(1);
3078
                }
3079
        }
3080

    
3081
        /* Check if we need to log to console and/or file */
3082
        gboolean use_stdout = TRUE;
3083
        if(args_info.disable_stdout_given) {
3084
                use_stdout = FALSE;
3085
                janus_config_add_item(config, "general", "log_to_stdout", "no");
3086
        } else {
3087
                /* Check if the configuration file is saying anything about this */
3088
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "log_to_stdout");
3089
                if(item && item->value && !janus_is_true(item->value))
3090
                        use_stdout = FALSE;
3091
        }
3092
        const char *logfile = NULL;
3093
        if(args_info.log_file_given) {
3094
                logfile = args_info.log_file_arg;
3095
                janus_config_add_item(config, "general", "log_to_file", "no");
3096
        } else {
3097
                /* Check if the configuration file is saying anything about this */
3098
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "log_to_file");
3099
                if(item && item->value)
3100
                        logfile = item->value;
3101
        }
3102

    
3103
        /* Check if we're going to daemonize Janus */
3104
        if(args_info.daemon_given) {
3105
                daemonize = TRUE;
3106
                janus_config_add_item(config, "general", "daemonize", "yes");
3107
        } else {
3108
                /* Check if the configuration file is saying anything about this */
3109
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "daemonize");
3110
                if(item && item->value && janus_is_true(item->value))
3111
                        daemonize = TRUE;
3112
        }
3113
        /* If we're going to daemonize, make sure logging to stdout is disabled and a log file has been specified */
3114
        if(daemonize && use_stdout) {
3115
                use_stdout = FALSE;
3116
        }
3117
        if(daemonize && logfile == NULL) {
3118
                g_print("Running Janus as a daemon but no log file provided, giving up...\n");
3119
                exit(1);
3120
        }
3121
        /* Daemonize now, if we need to */
3122
        if(daemonize) {
3123
                g_print("Running Janus as a daemon\n");
3124

    
3125
                /* Create a pipe for parent<->child communication during the startup phase */
3126
                if(pipe(pipefd) == -1) {
3127
                        g_print("pipe error!\n");
3128
                        exit(1);
3129
                }
3130

    
3131
                /* Fork off the parent process */
3132
                pid_t pid = fork();
3133
                if(pid < 0) {
3134
                        g_print("Fork error!\n");
3135
                        exit(1);
3136
                }
3137
                if(pid > 0) {
3138
                        /* Ok, we're the parent: let's wait for the child to tell us everything started fine */
3139
                        close(pipefd[1]);
3140
                        int code = -1;
3141
                        struct pollfd pollfds;
3142

    
3143
                        while(code < 0) {
3144
                                pollfds.fd = pipefd[0];
3145
                                pollfds.events = POLLIN;
3146
                                int res = poll(&pollfds, 1, -1);
3147
                                if(res < 0)
3148
                                        break;
3149
                                if(res == 0)
3150
                                        continue;
3151
                                if(pollfds.revents & POLLERR || pollfds.revents & POLLHUP)
3152
                                        break;
3153
                                if(pollfds.revents & POLLIN) {
3154
                                        res = read(pipefd[0], &code, sizeof(int));
3155
                                        break;
3156
                                }
3157
                        }
3158
                        if(code < 0)
3159
                                code = 1;
3160

    
3161
                        /* Leave the parent and return the exit code we received from the child */
3162
                        if(code)
3163
                                g_print("Error launching Janus (error code %d), check the logs for more details\n", code);
3164
                        exit(code);
3165
                }
3166
                /* Change the file mode mask */
3167
                umask(0);
3168

    
3169
                /* Create a new SID for the child process */
3170
                pid_t sid = setsid();
3171
                if(sid < 0) {
3172
                        g_print("Error setting SID!\n");
3173
                        exit(1);
3174
                }
3175
                /* Change the current working directory */
3176
                if((chdir("/")) < 0) {
3177
                        g_print("Error changing the current working directory!\n");
3178
                        exit(1);
3179
                }
3180
                /* We close stdin/stdout/stderr when initializing the logger */
3181
        }
3182

    
3183
        /* Initialize logger */
3184
        if(janus_log_init(daemonize, use_stdout, logfile) < 0)
3185
                exit(1);
3186

    
3187
        JANUS_PRINT("---------------------------------------------------\n");
3188
        JANUS_PRINT("  Starting Meetecho Janus (WebRTC Gateway) v%s\n", JANUS_VERSION_STRING);
3189
        JANUS_PRINT("---------------------------------------------------\n\n");
3190

    
3191
        /* Handle SIGINT (CTRL-C), SIGTERM (from service managers) */
3192
        signal(SIGINT, janus_handle_signal);
3193
        signal(SIGTERM, janus_handle_signal);
3194
        atexit(janus_termination_handler);
3195

    
3196
        /* Setup Glib */
3197
#if !GLIB_CHECK_VERSION(2, 36, 0)
3198
        g_type_init();
3199
#endif
3200

    
3201
        /* Logging level: default is info and no timestamps */
3202
        janus_log_level = LOG_INFO;
3203
        janus_log_timestamps = FALSE;
3204
        janus_log_colors = TRUE;
3205
        if(args_info.debug_level_given) {
3206
                if(args_info.debug_level_arg < LOG_NONE)
3207
                        args_info.debug_level_arg = 0;
3208
                else if(args_info.debug_level_arg > LOG_MAX)
3209
                        args_info.debug_level_arg = LOG_MAX;
3210
                janus_log_level = args_info.debug_level_arg;
3211
        }
3212

    
3213
        /* Any PID we need to create? */
3214
        const char *pidfile = NULL;
3215
        if(args_info.pid_file_given) {
3216
                pidfile = args_info.pid_file_arg;
3217
                janus_config_add_item(config, "general", "pid_file", pidfile);
3218
        } else {
3219
                /* Check if the configuration file is saying anything about this */
3220
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "pid_file");
3221
                if(item && item->value)
3222
                        pidfile = item->value;
3223
        }
3224
        if(janus_pidfile_create(pidfile) < 0)
3225
                exit(1);
3226

    
3227
        /* Proceed with the rest of the configuration */
3228
        janus_config_print(config);
3229
        if(args_info.debug_level_given) {
3230
                char debug[5];
3231
                g_snprintf(debug, 5, "%d", args_info.debug_level_arg);
3232
                janus_config_add_item(config, "general", "debug_level", debug);
3233
        } else {
3234
                /* No command line directive on logging, try the configuration file */
3235
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "debug_level");
3236
                if(item && item->value) {
3237
                        int temp_level = atoi(item->value);
3238
                        if(temp_level == 0 && strcmp(item->value, "0")) {
3239
                                JANUS_PRINT("Invalid debug level %s (configuration), using default (info=4)\n", item->value);
3240
                        } else {
3241
                                janus_log_level = temp_level;
3242
                                if(janus_log_level < LOG_NONE)
3243
                                        janus_log_level = 0;
3244
                                else if(janus_log_level > LOG_MAX)
3245
                                        janus_log_level = LOG_MAX;
3246
                        }
3247
                }
3248
        }
3249
        /* Any command line argument that should overwrite the configuration? */
3250
        JANUS_PRINT("Checking command line arguments...\n");
3251
        if(args_info.debug_timestamps_given) {
3252
                janus_config_add_item(config, "general", "debug_timestamps", "yes");
3253
        }
3254
        if(args_info.disable_colors_given) {
3255
                janus_config_add_item(config, "general", "debug_colors", "no");
3256
        }
3257
        if(args_info.server_name_given) {
3258
                janus_config_add_item(config, "general", "server_name", args_info.server_name_arg);
3259
        }
3260
         if(args_info.interface_given) {
3261
                janus_config_add_item(config, "general", "interface", args_info.interface_arg);
3262
        }
3263
        if(args_info.configs_folder_given) {
3264
                janus_config_add_item(config, "general", "configs_folder", args_info.configs_folder_arg);
3265
        }
3266
        if(args_info.plugins_folder_given) {
3267
                janus_config_add_item(config, "general", "plugins_folder", args_info.plugins_folder_arg);
3268
        }
3269
        if(args_info.apisecret_given) {
3270
                janus_config_add_item(config, "general", "api_secret", args_info.apisecret_arg);
3271
        }
3272
        if(args_info.token_auth_given) {
3273
                janus_config_add_item(config, "general", "token_auth", "yes");
3274
        }
3275
        if(args_info.cert_pem_given) {
3276
                janus_config_add_item(config, "certificates", "cert_pem", args_info.cert_pem_arg);
3277
        }
3278
        if(args_info.cert_key_given) {
3279
                janus_config_add_item(config, "certificates", "cert_key", args_info.cert_key_arg);
3280
        }
3281
        if(args_info.stun_server_given) {
3282
                /* Split in server and port (if port missing, use 3478 as default) */
3283
                char *stunport = strrchr(args_info.stun_server_arg, ':');
3284
                if(stunport != NULL) {
3285
                        *stunport = '\0';
3286
                        stunport++;
3287
                        janus_config_add_item(config, "nat", "stun_server", args_info.stun_server_arg);
3288
                        janus_config_add_item(config, "nat", "stun_port", stunport);
3289
                } else {
3290
                        janus_config_add_item(config, "nat", "stun_server", args_info.stun_server_arg);
3291
                        janus_config_add_item(config, "nat", "stun_port", "3478");
3292
                }
3293
        }
3294
        if(args_info.nat_1_1_given) {
3295
                janus_config_add_item(config, "nat", "nat_1_1_mapping", args_info.nat_1_1_arg);
3296
        }
3297
        if(args_info.ice_enforce_list_given) {
3298
                janus_config_add_item(config, "nat", "ice_enforce_list", args_info.ice_enforce_list_arg);
3299
        }
3300
        if(args_info.ice_ignore_list_given) {
3301
                janus_config_add_item(config, "nat", "ice_ignore_list", args_info.ice_ignore_list_arg);
3302
        }
3303
        if(args_info.libnice_debug_given) {
3304
                janus_config_add_item(config, "nat", "nice_debug", "true");
3305
        }
3306
        if(args_info.ice_lite_given) {
3307
                janus_config_add_item(config, "nat", "ice_lite", "true");
3308
        }
3309
        if(args_info.ice_tcp_given) {
3310
                janus_config_add_item(config, "nat", "ice_tcp", "true");
3311
        }
3312
        if(args_info.ipv6_candidates_given) {
3313
                janus_config_add_item(config, "media", "ipv6", "true");
3314
        }
3315
        if(args_info.force_bundle_given) {
3316
                janus_config_add_item(config, "media", "force-bundle", "true");
3317
        }
3318
        if(args_info.force_rtcp_mux_given) {
3319
                janus_config_add_item(config, "media", "force-rtcp-mux", "true");
3320
        }
3321
        if(args_info.max_nack_queue_given) {
3322
                char mnq[20];
3323
                g_snprintf(mnq, 20, "%d", args_info.max_nack_queue_arg);
3324
                janus_config_add_item(config, "media", "max_nack_queue", mnq);
3325
        }
3326
        if(args_info.rtp_port_range_given) {
3327
                janus_config_add_item(config, "media", "rtp_port_range", args_info.rtp_port_range_arg);
3328
        }
3329
        if(args_info.event_handlers_given) {
3330
                janus_config_add_item(config, "events", "broadcast", "yes");
3331
        }
3332
        janus_config_print(config);
3333

    
3334
        /* Logging/debugging */
3335
        JANUS_PRINT("Debug/log level is %d\n", janus_log_level);
3336
        janus_config_item *item = janus_config_get_item_drilldown(config, "general", "debug_timestamps");
3337
        if(item && item->value)
3338
                janus_log_timestamps = janus_is_true(item->value);
3339
        JANUS_PRINT("Debug/log timestamps are %s\n", janus_log_timestamps ? "enabled" : "disabled");
3340
        item = janus_config_get_item_drilldown(config, "general", "debug_colors");
3341
        if(item && item->value)
3342
                janus_log_colors = janus_is_true(item->value);
3343
        JANUS_PRINT("Debug/log colors are %s\n", janus_log_colors ? "enabled" : "disabled");
3344

    
3345
        /* Any IP/interface to enforce/ignore? */
3346
        item = janus_config_get_item_drilldown(config, "nat", "ice_enforce_list");
3347
        if(item && item->value) {
3348
                gchar **list = g_strsplit(item->value, ",", -1);
3349
                gchar *index = list[0];
3350
                if(index != NULL) {
3351
                        int i=0;
3352
                        while(index != NULL) {
3353
                                if(strlen(index) > 0) {
3354
                                        JANUS_LOG(LOG_INFO, "Adding '%s' to the ICE enforce list...\n", index);
3355
                                        janus_ice_enforce_interface(g_strdup(index));
3356
                                }
3357
                                i++;
3358
                                index = list[i];
3359
                        }
3360
                }
3361
                g_clear_pointer(&list, g_strfreev);
3362
        }
3363
        item = janus_config_get_item_drilldown(config, "nat", "ice_ignore_list");
3364
        if(item && item->value) {
3365
                gchar **list = g_strsplit(item->value, ",", -1);
3366
                gchar *index = list[0];
3367
                if(index != NULL) {
3368
                        int i=0;
3369
                        while(index != NULL) {
3370
                                if(strlen(index) > 0) {
3371
                                        JANUS_LOG(LOG_INFO, "Adding '%s' to the ICE ignore list...\n", index);
3372
                                        janus_ice_ignore_interface(g_strdup(index));
3373
                                }
3374
                                i++;
3375
                                index = list[i];
3376
                        }
3377
                }
3378
                g_clear_pointer(&list, g_strfreev);
3379
        }
3380
        /* What is the local IP? */
3381
        JANUS_LOG(LOG_VERB, "Selecting local IP address...\n");
3382
        gboolean local_ip_set = FALSE;
3383
        item = janus_config_get_item_drilldown(config, "general", "interface");
3384
        if(item && item->value) {
3385
                JANUS_LOG(LOG_VERB, "  -- Will try to use %s\n", item->value);
3386
                int family;
3387
                if (!janus_is_ip_valid(item->value, &family)) {
3388
                        JANUS_LOG(LOG_WARN, "Invalid local IP specified: %s, guessing the default...\n", item->value);
3389
                } else {
3390
                        /* Verify that we can actually bind to that address */
3391
                        int fd = socket(family, SOCK_DGRAM, 0);
3392
                        if (fd == -1) {
3393
                                JANUS_LOG(LOG_WARN, "Error creating test socket, falling back to detecting IP address...\n");
3394
                        } else {
3395
                                int r;
3396
                                struct sockaddr_storage ss;
3397
                                socklen_t addrlen;
3398
                                memset(&ss, 0, sizeof(ss));
3399
                                if (family == AF_INET) {
3400
                                        struct sockaddr_in *addr4 = (struct sockaddr_in*)&ss;
3401
                                        addr4->sin_family = AF_INET;
3402
                                        addr4->sin_port = 0;
3403
                                        inet_pton(AF_INET, item->value, &(addr4->sin_addr.s_addr));
3404
                                        addrlen = sizeof(struct sockaddr_in);
3405
                                } else {
3406
                                        struct sockaddr_in6 *addr6 = (struct sockaddr_in6*)&ss;
3407
                                        addr6->sin6_family = AF_INET6;
3408
                                        addr6->sin6_port = 0;
3409
                                        inet_pton(AF_INET6, item->value, &(addr6->sin6_addr.s6_addr));
3410
                                        addrlen = sizeof(struct sockaddr_in6);
3411
                                }
3412
                                r = bind(fd, (const struct sockaddr*)&ss, addrlen);
3413
                                close(fd);
3414
                                if (r < 0) {
3415
                                        JANUS_LOG(LOG_WARN, "Error setting local IP address to %s, falling back to detecting IP address...\n", item->value);
3416
                                } else {
3417
                                        g_strlcpy(local_ip, item->value, sizeof(local_ip));
3418
                                        local_ip_set = TRUE;
3419
                                }
3420
                        }
3421
                }
3422
        }
3423
        if (!local_ip_set)
3424
                janus_detect_local_ip(local_ip, sizeof(local_ip));
3425
        JANUS_LOG(LOG_INFO, "Using %s as local IP...\n", local_ip);
3426

    
3427
        /* Was a custom instance name provided? */
3428
        item = janus_config_get_item_drilldown(config, "general", "server_name");
3429
        if(item && item->value) {
3430
                server_name = g_strdup(item->value);
3431
        }
3432

    
3433
        /* Is there any API secret to consider? */
3434
        api_secret = NULL;
3435
        item = janus_config_get_item_drilldown(config, "general", "api_secret");
3436
        if(item && item->value) {
3437
                api_secret = g_strdup(item->value);
3438
        }
3439
        /* Is there any API secret to consider? */
3440
        admin_api_secret = NULL;
3441
        item = janus_config_get_item_drilldown(config, "general", "admin_secret");
3442
        if(item && item->value) {
3443
                admin_api_secret = g_strdup(item->value);
3444
        }
3445
        /* Also check if the token based authentication mechanism needs to be enabled */
3446
        item = janus_config_get_item_drilldown(config, "general", "token_auth");
3447
        janus_auth_init(item && item->value && janus_is_true(item->value));
3448

    
3449
        /* Setup ICE stuff (e.g., checking if the provided STUN server is correct) */
3450
        char *stun_server = NULL, *turn_server = NULL;
3451
        uint16_t stun_port = 0, turn_port = 0;
3452
        char *turn_type = NULL, *turn_user = NULL, *turn_pwd = NULL;
3453
        char *turn_rest_api = NULL, *turn_rest_api_key = NULL;
3454
#ifdef HAVE_LIBCURL
3455
        char *turn_rest_api_method = NULL;
3456
#endif
3457
        const char *nat_1_1_mapping = NULL;
3458
        uint16_t rtp_min_port = 0, rtp_max_port = 0;
3459
        gboolean ice_lite = FALSE, ice_tcp = FALSE, ipv6 = FALSE;
3460
        item = janus_config_get_item_drilldown(config, "media", "ipv6");
3461
        ipv6 = (item && item->value) ? janus_is_true(item->value) : FALSE;
3462
        item = janus_config_get_item_drilldown(config, "media", "rtp_port_range");
3463
        if(item && item->value) {
3464
                /* Split in min and max port */
3465
                char *maxport = strrchr(item->value, '-');
3466
                if(maxport != NULL) {
3467
                        *maxport = '\0';
3468
                        maxport++;
3469
                        rtp_min_port = atoi(item->value);
3470
                        rtp_max_port = atoi(maxport);
3471
                        maxport--;
3472
                        *maxport = '-';
3473
                }
3474
                if(rtp_min_port > rtp_max_port) {
3475
                        int temp_port = rtp_min_port;
3476
                        rtp_min_port = rtp_max_port;
3477
                        rtp_max_port = temp_port;
3478
                }
3479
                if(rtp_max_port == 0)
3480
                        rtp_max_port = 65535;
3481
                JANUS_LOG(LOG_INFO, "RTP port range: %u -- %u\n", rtp_min_port, rtp_max_port);
3482
        }
3483
        /* Check if we need to enable the ICE Lite mode */
3484
        item = janus_config_get_item_drilldown(config, "nat", "ice_lite");
3485
        ice_lite = (item && item->value) ? janus_is_true(item->value) : FALSE;
3486
        /* Check if we need to enable ICE-TCP support (warning: still broken, for debugging only) */
3487
        item = janus_config_get_item_drilldown(config, "nat", "ice_tcp");
3488
        ice_tcp = (item && item->value) ? janus_is_true(item->value) : FALSE;
3489
        /* Any STUN server to use in Janus? */
3490
        item = janus_config_get_item_drilldown(config, "nat", "stun_server");
3491
        if(item && item->value)
3492
                stun_server = (char *)item->value;
3493
        item = janus_config_get_item_drilldown(config, "nat", "stun_port");
3494
        if(item && item->value)
3495
                stun_port = atoi(item->value);
3496
        /* Any 1:1 NAT mapping to take into account? */
3497
        item = janus_config_get_item_drilldown(config, "nat", "nat_1_1_mapping");
3498
        if(item && item->value) {
3499
                JANUS_LOG(LOG_VERB, "Using nat_1_1_mapping for public ip - %s\n", item->value);
3500
                nat_1_1_mapping = item->value;
3501
                janus_set_public_ip(item->value);
3502
                janus_ice_enable_nat_1_1();
3503
        }
3504
        /* Any TURN server to use in Janus? */
3505
        item = janus_config_get_item_drilldown(config, "nat", "turn_server");
3506
        if(item && item->value)
3507
                turn_server = (char *)item->value;
3508
        item = janus_config_get_item_drilldown(config, "nat", "turn_port");
3509
        if(item && item->value)
3510
                turn_port = atoi(item->value);
3511
        item = janus_config_get_item_drilldown(config, "nat", "turn_type");
3512
        if(item && item->value)
3513
                turn_type = (char *)item->value;
3514
        item = janus_config_get_item_drilldown(config, "nat", "turn_user");
3515
        if(item && item->value)
3516
                turn_user = (char *)item->value;
3517
        item = janus_config_get_item_drilldown(config, "nat", "turn_pwd");
3518
        if(item && item->value)
3519
                turn_pwd = (char *)item->value;
3520
        /* Check if there's any TURN REST API backend to use */
3521
        item = janus_config_get_item_drilldown(config, "nat", "turn_rest_api");
3522
        if(item && item->value)
3523
                turn_rest_api = (char *)item->value;
3524
        item = janus_config_get_item_drilldown(config, "nat", "turn_rest_api_key");
3525
        if(item && item->value)
3526
                turn_rest_api_key = (char *)item->value;
3527
#ifdef HAVE_LIBCURL
3528
        item = janus_config_get_item_drilldown(config, "nat", "turn_rest_api_method");
3529
        if(item && item->value)
3530
                turn_rest_api_method = (char *)item->value;
3531
#endif
3532
        /* Initialize the ICE stack now */
3533
        janus_ice_init(ice_lite, ice_tcp, ipv6, rtp_min_port, rtp_max_port);
3534
        if(janus_ice_set_stun_server(stun_server, stun_port) < 0) {
3535
                JANUS_LOG(LOG_FATAL, "Invalid STUN address %s:%u\n", stun_server, stun_port);
3536
                exit(1);
3537
        }
3538
        if(janus_ice_set_turn_server(turn_server, turn_port, turn_type, turn_user, turn_pwd) < 0) {
3539
                JANUS_LOG(LOG_FATAL, "Invalid TURN address %s:%u\n", turn_server, turn_port);
3540
                exit(1);
3541
        }
3542
#ifndef HAVE_LIBCURL
3543
        if(turn_rest_api != NULL || turn_rest_api_key != NULL) {
3544
                JANUS_LOG(LOG_WARN, "A TURN REST API backend specified in the settings, but libcurl support has not been built\n");
3545
        }
3546
#else
3547
        if(janus_ice_set_turn_rest_api(turn_rest_api, turn_rest_api_key, turn_rest_api_method) < 0) {
3548
                JANUS_LOG(LOG_FATAL, "Invalid TURN REST API configuration: %s (%s, %s)\n", turn_rest_api, turn_rest_api_key, turn_rest_api_method);
3549
                exit(1);
3550
        }
3551
#endif
3552
        item = janus_config_get_item_drilldown(config, "nat", "nice_debug");
3553
        if(item && item->value && janus_is_true(item->value)) {
3554
                /* Enable libnice debugging */
3555
                janus_ice_debugging_enable();
3556
        }
3557
        if(stun_server == NULL && turn_server == NULL) {
3558
                /* No STUN and TURN server provided for Janus: make sure it isn't on a private address */
3559
                gboolean private_address = FALSE;
3560
                const char *test_ip = nat_1_1_mapping ? nat_1_1_mapping : local_ip;
3561
                struct sockaddr_in addr;
3562
                if(inet_pton(AF_INET, test_ip, &addr) > 0) {
3563
                        unsigned short int ip[4];
3564
                        sscanf(test_ip, "%hu.%hu.%hu.%hu", &ip[0], &ip[1], &ip[2], &ip[3]);
3565
                        if(ip[0] == 10) {
3566
                                /* Class A private address */
3567
                                private_address = TRUE;
3568
                        } else if(ip[0] == 172 && (ip[1] >= 16 && ip[1] <= 31)) {
3569
                                /* Class B private address */
3570
                                private_address = TRUE;
3571
                        } else if(ip[0] == 192 && ip[1] == 168) {
3572
                                /* Class C private address */
3573
                                private_address = TRUE;
3574
                        }
3575
                }
3576
                if(private_address) {
3577
                        JANUS_LOG(LOG_WARN, "Janus is deployed on a private address (%s) but you didn't specify any STUN server!"
3578
                                            " Expect trouble if this is supposed to work over the internet and not just in a LAN...\n", test_ip);
3579
                }
3580
        }
3581
        /* Are we going to force BUNDLE and/or rtcp-mux? */
3582
        gboolean force_bundle = FALSE, force_rtcpmux = FALSE;
3583
        item = janus_config_get_item_drilldown(config, "media", "force-bundle");
3584
        force_bundle = (item && item->value) ? janus_is_true(item->value) : FALSE;
3585
        janus_ice_force_bundle(force_bundle);
3586
        item = janus_config_get_item_drilldown(config, "media", "force-rtcp-mux");
3587
        force_rtcpmux = (item && item->value) ? janus_is_true(item->value) : FALSE;
3588
        janus_ice_force_rtcpmux(force_rtcpmux);
3589
        /* NACK related stuff */
3590
        item = janus_config_get_item_drilldown(config, "media", "max_nack_queue");
3591
        if(item && item->value) {
3592
                int mnq = atoi(item->value);
3593
                if(mnq < 0) {
3594
                        JANUS_LOG(LOG_WARN, "Ignoring max_nack_queue value as it's not a positive integer\n");
3595
                } else if(mnq > 0 && mnq < 200) {
3596
                        JANUS_LOG(LOG_WARN, "Ignoring max_nack_queue value as it's less than 200\n");
3597
                } else {
3598
                        janus_set_max_nack_queue(mnq);
3599
                }
3600
        }
3601

    
3602
        /* Setup OpenSSL stuff */
3603
        const char* server_pem;
3604
        item = janus_config_get_item_drilldown(config, "certificates", "cert_pem");
3605
        if(!item || !item->value) {
3606
                server_pem = NULL;
3607
        } else {
3608
                server_pem = item->value;
3609
        }
3610

    
3611
        const char* server_key;
3612
        item = janus_config_get_item_drilldown(config, "certificates", "cert_key");
3613
        if(!item || !item->value) {
3614
                server_key = NULL;
3615
        } else {
3616
                server_key = item->value;
3617
        }
3618
        JANUS_LOG(LOG_VERB, "Using certificates:\n\t%s\n\t%s\n", server_pem, server_key);
3619

    
3620
        SSL_library_init();
3621
        SSL_load_error_strings();
3622
        OpenSSL_add_all_algorithms();
3623
        /* ... and DTLS-SRTP in particular */
3624
        if(janus_dtls_srtp_init(server_pem, server_key) < 0) {
3625
                exit(1);
3626
        }
3627
        /* Check if there's any custom value for the starting MTU to use in the BIO filter */
3628
        item = janus_config_get_item_drilldown(config, "media", "dtls_mtu");
3629
        if(item && item->value)
3630
                janus_dtls_bio_filter_set_mtu(atoi(item->value));
3631

    
3632
#ifdef HAVE_SCTP
3633
        /* Initialize SCTP for DataChannels */
3634
        if(janus_sctp_init() < 0) {
3635
                exit(1);
3636
        }
3637
#else
3638
        JANUS_LOG(LOG_WARN, "Data Channels support not compiled\n");
3639
#endif
3640

    
3641
        /* Initialize Sofia-SDP */
3642
        if(janus_sdp_init() < 0) {
3643
                exit(1);
3644
        }
3645

    
3646
        /* Sessions */
3647
        sessions = g_hash_table_new_full(g_int64_hash, g_int64_equal, (GDestroyNotify)g_free, NULL);
3648
        old_sessions = g_hash_table_new_full(g_int64_hash, g_int64_equal, (GDestroyNotify)g_free, NULL);
3649
        janus_mutex_init(&sessions_mutex);
3650
        /* Start the sessions watchdog */
3651
        sessions_watchdog_context = g_main_context_new();
3652
        GMainLoop *watchdog_loop = g_main_loop_new(sessions_watchdog_context, FALSE);
3653
        GError *error = NULL;
3654
        GThread *watchdog = g_thread_try_new("sessions watchdog", &janus_sessions_watchdog, watchdog_loop, &error);
3655
        if(error != NULL) {
3656
                JANUS_LOG(LOG_FATAL, "Got error %d (%s) trying to start sessions watchdog...\n", error->code, error->message ? error->message : "??");
3657
                exit(1);
3658
        }
3659

    
3660
        /* Load event handlers */
3661
        const char *path = EVENTDIR;
3662
        item = janus_config_get_item_drilldown(config, "general", "events_folder");
3663
        if(item && item->value)
3664
                path = (char *)item->value;
3665
        JANUS_LOG(LOG_INFO, "Event handler plugins folder: %s\n", path);
3666
        DIR *dir = opendir(path);
3667
        if(!dir) {
3668
                /* Not really fatal, we don't care and go on anyway: event handlers are not fundamental */
3669
                JANUS_LOG(LOG_FATAL, "\tCouldn't access event handler plugins folder...\n");
3670
        } else {
3671
                /* Any event handlers to ignore? */
3672
                gchar **disabled_eventhandlers = NULL;
3673
                item = janus_config_get_item_drilldown(config, "events", "broadcast");
3674
                /* Event handlers are disabled by default: they need to be enabled in the configuration */
3675
                gboolean enable_events = FALSE;
3676
                if(item && item->value)
3677
                        enable_events = janus_is_true(item->value);
3678
                if(!enable_events) {
3679
                        JANUS_LOG(LOG_WARN, "Event handlers support disabled\n");
3680
                } else {
3681
                        item = janus_config_get_item_drilldown(config, "events", "disable");
3682
                        if(item && item->value)
3683
                                disabled_eventhandlers = g_strsplit(item->value, ",", -1);
3684
                        /* Open the shared objects */
3685
                        struct dirent *eventent = NULL;
3686
                        char eventpath[1024];
3687
                        while((eventent = readdir(dir))) {
3688
                                int len = strlen(eventent->d_name);
3689
                                if (len < 4) {
3690
                                        continue;
3691
                                }
3692
                                if (strcasecmp(eventent->d_name+len-strlen(SHLIB_EXT), SHLIB_EXT)) {
3693
                                        continue;
3694
                                }
3695
                                /* Check if this event handler has been disabled in the configuration file */
3696
                                if(disabled_eventhandlers != NULL) {
3697
                                        gchar *index = disabled_eventhandlers[0];
3698
                                        if(index != NULL) {
3699
                                                int i=0;
3700
                                                gboolean skip = FALSE;
3701
                                                while(index != NULL) {
3702
                                                        while(isspace(*index))
3703
                                                                index++;
3704
                                                        if(strlen(index) && !strcmp(index, eventent->d_name)) {
3705
                                                                JANUS_LOG(LOG_WARN, "Event handler plugin '%s' has been disabled, skipping...\n", eventent->d_name);
3706
                                                                skip = TRUE;
3707
                                                                break;
3708
                                                        }
3709
                                                        i++;
3710
                                                        index = disabled_eventhandlers[i];
3711
                                                }
3712
                                                if(skip)
3713
                                                        continue;
3714
                                        }
3715
                                }
3716
                                JANUS_LOG(LOG_INFO, "Loading event handler plugin '%s'...\n", eventent->d_name);
3717
                                memset(eventpath, 0, 1024);
3718
                                g_snprintf(eventpath, 1024, "%s/%s", path, eventent->d_name);
3719
                                void *event = dlopen(eventpath, RTLD_LAZY);
3720
                                if (!event) {
3721
                                        JANUS_LOG(LOG_ERR, "\tCouldn't load event handler plugin '%s': %s\n", eventent->d_name, dlerror());
3722
                                } else {
3723
                                        create_e *create = (create_e*) dlsym(event, "create");
3724
                                        const char *dlsym_error = dlerror();
3725
                                        if (dlsym_error) {
3726
                                                JANUS_LOG(LOG_ERR, "\tCouldn't load symbol 'create': %s\n", dlsym_error);
3727
                                                continue;
3728
                                        }
3729
                                        janus_eventhandler *janus_eventhandler = create();
3730
                                        if(!janus_eventhandler) {
3731
                                                JANUS_LOG(LOG_ERR, "\tCouldn't use function 'create'...\n");
3732
                                                continue;
3733
                                        }
3734
                                        /* Are all the mandatory methods and callbacks implemented? */
3735
                                        if(!janus_eventhandler->init || !janus_eventhandler->destroy ||
3736
                                                        !janus_eventhandler->get_api_compatibility ||
3737
                                                        !janus_eventhandler->get_version ||
3738
                                                        !janus_eventhandler->get_version_string ||
3739
                                                        !janus_eventhandler->get_description ||
3740
                                                        !janus_eventhandler->get_package ||
3741
                                                        !janus_eventhandler->get_name ||
3742
                                                        !janus_eventhandler->incoming_event) {
3743
                                                JANUS_LOG(LOG_ERR, "\tMissing some mandatory methods/callbacks, skipping this event handler plugin...\n");
3744
                                                continue;
3745
                                        }
3746
                                        if(janus_eventhandler->get_api_compatibility() < JANUS_EVENTHANDLER_API_VERSION) {
3747
                                                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",
3748
                                                        janus_eventhandler->get_package(), janus_eventhandler->get_api_compatibility(), JANUS_EVENTHANDLER_API_VERSION);
3749
                                                continue;
3750
                                        }
3751
                                        janus_eventhandler->init(configs_folder);
3752
                                        JANUS_LOG(LOG_VERB, "\tVersion: %d (%s)\n", janus_eventhandler->get_version(), janus_eventhandler->get_version_string());
3753
                                        JANUS_LOG(LOG_VERB, "\t   [%s] %s\n", janus_eventhandler->get_package(), janus_eventhandler->get_name());
3754
                                        JANUS_LOG(LOG_VERB, "\t   %s\n", janus_eventhandler->get_description());
3755
                                        JANUS_LOG(LOG_VERB, "\t   Plugin API version: %d\n", janus_eventhandler->get_api_compatibility());
3756
                                        JANUS_LOG(LOG_VERB, "\t   Subscriptions:");
3757
                                        if(janus_eventhandler->events_mask == 0) {
3758
                                                JANUS_LOG(LOG_VERB, " none");
3759
                                        } else {
3760
                                                if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_SESSION))
3761
                                                        JANUS_LOG(LOG_VERB, " sessions");
3762
                                                if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_HANDLE))
3763
                                                        JANUS_LOG(LOG_VERB, " handles");
3764
                                                if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_JSEP))
3765
                                                        JANUS_LOG(LOG_VERB, " jsep");
3766
                                                if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_WEBRTC))
3767
                                                        JANUS_LOG(LOG_VERB, " webrtc");
3768
                                                if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_MEDIA))
3769
                                                        JANUS_LOG(LOG_VERB, " media");
3770
                                                if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_PLUGIN))
3771
                                                        JANUS_LOG(LOG_VERB, " plugins");
3772
                                                if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_TRANSPORT))
3773
                                                        JANUS_LOG(LOG_VERB, " transports");
3774
                                        }
3775
                                        JANUS_LOG(LOG_VERB, "\n");
3776
                                        if(eventhandlers == NULL)
3777
                                                eventhandlers = g_hash_table_new(g_str_hash, g_str_equal);
3778
                                        g_hash_table_insert(eventhandlers, (gpointer)janus_eventhandler->get_package(), janus_eventhandler);
3779
                                        if(eventhandlers_so == NULL)
3780
                                                eventhandlers_so = g_hash_table_new(g_str_hash, g_str_equal);
3781
                                        g_hash_table_insert(eventhandlers_so, (gpointer)janus_eventhandler->get_package(), event);
3782
                                }
3783
                        }
3784
                }
3785
                closedir(dir);
3786
                if(disabled_eventhandlers != NULL)
3787
                        g_strfreev(disabled_eventhandlers);
3788
                disabled_eventhandlers = NULL;
3789
                /* Initialize the event broadcaster */
3790
                if(janus_events_init(enable_events, eventhandlers) < 0) {
3791
                        JANUS_LOG(LOG_FATAL, "Error initializing the Event handlers mechanism...\n");
3792
                        exit(1);
3793
                }
3794
        }
3795

    
3796
        /* Load plugins */
3797
        path = PLUGINDIR;
3798
        item = janus_config_get_item_drilldown(config, "general", "plugins_folder");
3799
        if(item && item->value)
3800
                path = (char *)item->value;
3801
        JANUS_LOG(LOG_INFO, "Plugins folder: %s\n", path);
3802
        dir = opendir(path);
3803
        if(!dir) {
3804
                JANUS_LOG(LOG_FATAL, "\tCouldn't access plugins folder...\n");
3805
                exit(1);
3806
        }
3807
        /* Any plugin to ignore? */
3808
        gchar **disabled_plugins = NULL;
3809
        item = janus_config_get_item_drilldown(config, "plugins", "disable");
3810
        if(item && item->value)
3811
                disabled_plugins = g_strsplit(item->value, ",", -1);
3812
        /* Open the shared objects */
3813
        struct dirent *pluginent = NULL;
3814
        char pluginpath[1024];
3815
        while((pluginent = readdir(dir))) {
3816
                int len = strlen(pluginent->d_name);
3817
                if (len < 4) {
3818
                        continue;
3819
                }
3820
                if (strcasecmp(pluginent->d_name+len-strlen(SHLIB_EXT), SHLIB_EXT)) {
3821
                        continue;
3822
                }
3823
                /* Check if this plugins has been disabled in the configuration file */
3824
                if(disabled_plugins != NULL) {
3825
                        gchar *index = disabled_plugins[0];
3826
                        if(index != NULL) {
3827
                                int i=0;
3828
                                gboolean skip = FALSE;
3829
                                while(index != NULL) {
3830
                                        while(isspace(*index))
3831
                                                index++;
3832
                                        if(strlen(index) && !strcmp(index, pluginent->d_name)) {
3833
                                                JANUS_LOG(LOG_WARN, "Plugin '%s' has been disabled, skipping...\n", pluginent->d_name);
3834
                                                skip = TRUE;
3835
                                                break;
3836
                                        }
3837
                                        i++;
3838
                                        index = disabled_plugins[i];
3839
                                }
3840
                                if(skip)
3841
                                        continue;
3842
                        }
3843
                }
3844
                JANUS_LOG(LOG_INFO, "Loading plugin '%s'...\n", pluginent->d_name);
3845
                memset(pluginpath, 0, 1024);
3846
                g_snprintf(pluginpath, 1024, "%s/%s", path, pluginent->d_name);
3847
                void *plugin = dlopen(pluginpath, RTLD_LOCAL | RTLD_LAZY);
3848
                if (!plugin) {
3849
                        JANUS_LOG(LOG_ERR, "\tCouldn't load plugin '%s': %s\n", pluginent->d_name, dlerror());
3850
                } else {
3851
                        create_p *create = (create_p*) dlsym(plugin, "create");
3852
                        const char *dlsym_error = dlerror();
3853
                        if (dlsym_error) {
3854
                                JANUS_LOG(LOG_ERR, "\tCouldn't load symbol 'create': %s\n", dlsym_error);
3855
                                continue;
3856
                        }
3857
                        janus_plugin *janus_plugin = create();
3858
                        if(!janus_plugin) {
3859
                                JANUS_LOG(LOG_ERR, "\tCouldn't use function 'create'...\n");
3860
                                continue;
3861
                        }
3862
                        /* Are all the mandatory methods and callbacks implemented? */
3863
                        if(!janus_plugin->init || !janus_plugin->destroy ||
3864
                                        !janus_plugin->get_api_compatibility ||
3865
                                        !janus_plugin->get_version ||
3866
                                        !janus_plugin->get_version_string ||
3867
                                        !janus_plugin->get_description ||
3868
                                        !janus_plugin->get_package ||
3869
                                        !janus_plugin->get_name ||
3870
                                        !janus_plugin->create_session ||
3871
                                        !janus_plugin->query_session ||
3872
                                        !janus_plugin->destroy_session ||
3873
                                        !janus_plugin->handle_message ||
3874
                                        !janus_plugin->setup_media ||
3875
                                        !janus_plugin->hangup_media) {
3876
                                JANUS_LOG(LOG_ERR, "\tMissing some mandatory methods/callbacks, skipping this plugin...\n");
3877
                                continue;
3878
                        }
3879
                        if(janus_plugin->get_api_compatibility() < JANUS_PLUGIN_API_VERSION) {
3880
                                JANUS_LOG(LOG_ERR, "The '%s' plugin was compiled against an older version of the API (%d < %d), skipping it: update it to enable it again\n",
3881
                                        janus_plugin->get_package(), janus_plugin->get_api_compatibility(), JANUS_PLUGIN_API_VERSION);
3882
                                continue;
3883
                        }
3884
                        janus_plugin->init(&janus_handler_plugin, configs_folder);
3885
                        JANUS_LOG(LOG_VERB, "\tVersion: %d (%s)\n", janus_plugin->get_version(), janus_plugin->get_version_string());
3886
                        JANUS_LOG(LOG_VERB, "\t   [%s] %s\n", janus_plugin->get_package(), janus_plugin->get_name());
3887
                        JANUS_LOG(LOG_VERB, "\t   %s\n", janus_plugin->get_description());
3888
                        JANUS_LOG(LOG_VERB, "\t   Plugin API version: %d\n", janus_plugin->get_api_compatibility());
3889
                        if(!janus_plugin->incoming_rtp && !janus_plugin->incoming_rtcp && !janus_plugin->incoming_data) {
3890
                                JANUS_LOG(LOG_WARN, "The '%s' plugin doesn't implement any callback for RTP/RTCP/data... is this on purpose?\n",
3891
                                        janus_plugin->get_package());
3892
                        }
3893
                        if(!janus_plugin->incoming_rtp && !janus_plugin->incoming_rtcp && janus_plugin->incoming_data) {
3894
                                JANUS_LOG(LOG_WARN, "The '%s' plugin will only handle data channels (no RTP/RTCP)... is this on purpose?\n",
3895
                                        janus_plugin->get_package());
3896
                        }
3897
                        if(plugins == NULL)
3898
                                plugins = g_hash_table_new(g_str_hash, g_str_equal);
3899
                        g_hash_table_insert(plugins, (gpointer)janus_plugin->get_package(), janus_plugin);
3900
                        if(plugins_so == NULL)
3901
                                plugins_so = g_hash_table_new(g_str_hash, g_str_equal);
3902
                        g_hash_table_insert(plugins_so, (gpointer)janus_plugin->get_package(), plugin);
3903
                }
3904
        }
3905
        closedir(dir);
3906
        if(disabled_plugins != NULL)
3907
                g_strfreev(disabled_plugins);
3908
        disabled_plugins = NULL;
3909

    
3910
        /* Create a thread pool to handle incoming requests, no matter what the transport */
3911
        error = NULL;
3912
        tasks = g_thread_pool_new(janus_transport_task, NULL, -1, FALSE, &error);
3913
        if(error != NULL) {
3914
                /* Something went wrong... */
3915
                JANUS_LOG(LOG_FATAL, "Got error %d (%s) trying to launch the request pool task thread...\n", error->code, error->message ? error->message : "??");
3916
                exit(1);
3917
        }
3918

    
3919
        /* Load transports */
3920
        gboolean janus_api_enabled = FALSE, admin_api_enabled = FALSE;
3921
        path = TRANSPORTDIR;
3922
        item = janus_config_get_item_drilldown(config, "general", "transports_folder");
3923
        if(item && item->value)
3924
                path = (char *)item->value;
3925
        JANUS_LOG(LOG_INFO, "Transport plugins folder: %s\n", path);
3926
        dir = opendir(path);
3927
        if(!dir) {
3928
                JANUS_LOG(LOG_FATAL, "\tCouldn't access transport plugins folder...\n");
3929
                exit(1);
3930
        }
3931
        /* Any transport to ignore? */
3932
        gchar **disabled_transports = NULL;
3933
        item = janus_config_get_item_drilldown(config, "transports", "disable");
3934
        if(item && item->value)
3935
                disabled_transports = g_strsplit(item->value, ",", -1);
3936
        /* Open the shared objects */
3937
        struct dirent *transportent = NULL;
3938
        char transportpath[1024];
3939
        while((transportent = readdir(dir))) {
3940
                int len = strlen(transportent->d_name);
3941
                if (len < 4) {
3942
                        continue;
3943
                }
3944
                if (strcasecmp(transportent->d_name+len-strlen(SHLIB_EXT), SHLIB_EXT)) {
3945
                        continue;
3946
                }
3947
                /* Check if this transports has been disabled in the configuration file */
3948
                if(disabled_transports != NULL) {
3949
                        gchar *index = disabled_transports[0];
3950
                        if(index != NULL) {
3951
                                int i=0;
3952
                                gboolean skip = FALSE;
3953
                                while(index != NULL) {
3954
                                        while(isspace(*index))
3955
                                                index++;
3956
                                        if(strlen(index) && !strcmp(index, transportent->d_name)) {
3957
                                                JANUS_LOG(LOG_WARN, "Transport plugin '%s' has been disabled, skipping...\n", transportent->d_name);
3958
                                                skip = TRUE;