Statistics
| Branch: | Revision:

janus-gateway / janus.c @ d97e0296

History | View | Annotate | Download (175 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                        11
43
#define JANUS_VERSION_STRING        "0.1.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
/* Certificates */
73
static char *server_pem = NULL;
74
gchar *janus_get_server_pem(void) {
75
        return server_pem;
76
}
77
static char *server_key = NULL;
78
gchar *janus_get_server_key(void) {
79
        return server_key;
80
}
81

    
82

    
83
/* API secrets */
84
static char *api_secret = NULL, *admin_api_secret = NULL;
85

    
86
/* JSON parameters */
87
static int janus_process_error_string(janus_request *request, uint64_t session_id, const char *transaction, gint error, gchar *error_string);
88

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

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

    
140

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

    
162

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

    
166

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

    
274

    
275
/* Logging */
276
int janus_log_level = LOG_INFO;
277
gboolean janus_log_timestamps = FALSE;
278
gboolean janus_log_colors = FALSE;
279
int lock_debug = 0;
280

    
281

    
282
/*! \brief Signal handler (just used to intercept CTRL+C and SIGTERM) */
283
static void janus_handle_signal(int signum) {
284
        switch(g_atomic_int_get(&stop)) {
285
                case 0:
286
                        JANUS_PRINT("Stopping gateway, please wait...\n");
287
                        break;
288
                case 1:
289
                        JANUS_PRINT("In a hurry? I'm trying to free resources cleanly, here!\n");
290
                        break;
291
                default:
292
                        JANUS_PRINT("Ok, leaving immediately...\n");
293
                        break;
294
        }
295
        g_atomic_int_inc(&stop);
296
        if(g_atomic_int_get(&stop) > 2)
297
                exit(1);
298
}
299

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

    
318

    
319
/** @name Transport plugin callback interface
320
 * These are the callbacks implemented by the gateway core, as part of
321
 * the janus_transport_callbacks interface. Everything the transport
322
 * plugins send the gateway is handled here.
323
 */
324
///@{
325
void janus_transport_incoming_request(janus_transport *plugin, void *transport, void *request_id, gboolean admin, json_t *message, json_error_t *error);
326
void janus_transport_gone(janus_transport *plugin, void *transport);
327
gboolean janus_transport_is_api_secret_needed(janus_transport *plugin);
328
gboolean janus_transport_is_api_secret_valid(janus_transport *plugin, const char *apisecret);
329
gboolean janus_transport_is_auth_token_needed(janus_transport *plugin);
330
gboolean janus_transport_is_auth_token_valid(janus_transport *plugin, const char *token);
331

    
332
static janus_transport_callbacks janus_handler_transport =
333
        {
334
                .incoming_request = janus_transport_incoming_request,
335
                .transport_gone = janus_transport_gone,
336
                .is_api_secret_needed = janus_transport_is_api_secret_needed,
337
                .is_api_secret_valid = janus_transport_is_api_secret_valid,
338
                .is_auth_token_needed = janus_transport_is_auth_token_needed,
339
                .is_auth_token_valid = janus_transport_is_auth_token_valid,
340
        }; 
341
GThreadPool *tasks = NULL;
342
void janus_transport_task(gpointer data, gpointer user_data);
343
///@}
344

    
345

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

    
373

    
374
/* Gateway Sessions */
375
static janus_mutex sessions_mutex;
376
static GHashTable *sessions = NULL, *old_sessions = NULL;
377
static GMainContext *sessions_watchdog_context = NULL;
378

    
379

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

    
382
static gboolean janus_cleanup_session(gpointer user_data) {
383
        janus_session *session = (janus_session *) user_data;
384

    
385
        JANUS_LOG(LOG_DBG, "Cleaning up session %"SCNu64"...\n", session->session_id);
386
        janus_session_destroy(session->session_id);
387

    
388
        return G_SOURCE_REMOVE;
389
}
390

    
391
static gboolean janus_check_sessions(gpointer user_data) {
392
        GMainContext *watchdog_context = (GMainContext *) user_data;
393
        janus_mutex_lock(&sessions_mutex);
394
        if(sessions && g_hash_table_size(sessions) > 0) {
395
                GHashTableIter iter;
396
                gpointer value;
397
                g_hash_table_iter_init(&iter, sessions);
398
                while (g_hash_table_iter_next(&iter, NULL, &value)) {
399
                        janus_session *session = (janus_session *) value;
400
                        if (!session || session->destroy) {
401
                                continue;
402
                        }
403
                        gint64 now = janus_get_monotonic_time();
404
                        if (now - session->last_activity >= SESSION_TIMEOUT * G_USEC_PER_SEC && !session->timeout) {
405
                                JANUS_LOG(LOG_INFO, "Timeout expired for session %"SCNu64"...\n", session->session_id);
406

    
407
                                /* Notify the transport */
408
                                if(session->source) {
409
                                        json_t *event = json_object();
410
                                        json_object_set_new(event, "janus", json_string("timeout"));
411
                                        json_object_set_new(event, "session_id", json_integer(session->session_id));
412
                                        /* Send this to the transport client */
413
                                        session->source->transport->send_message(session->source->instance, NULL, FALSE, event);
414
                                        /* Notify the transport plugin about the session timeout */
415
                                        session->source->transport->session_over(session->source->instance, session->session_id, TRUE);
416
                                }
417
                                /* Notify event handlers as well */
418
                                if(janus_events_is_enabled())
419
                                        janus_events_notify_handlers(JANUS_EVENT_TYPE_SESSION, session->session_id, "timeout");
420

    
421
                                /* Mark the session as over, we'll deal with it later */
422
                                session->timeout = 1;
423
                                /* FIXME Is this safe? apparently it causes hash table errors on the console */
424
                                g_hash_table_iter_remove(&iter);
425
                                g_hash_table_insert(old_sessions, GUINT_TO_POINTER(session->session_id), session);
426

    
427
                                /* Schedule the session for deletion */
428
                                GSource *timeout_source = g_timeout_source_new_seconds(3);
429
                                g_source_set_callback(timeout_source, janus_cleanup_session, session, NULL);
430
                                g_source_attach(timeout_source, watchdog_context);
431
                                g_source_unref(timeout_source);
432
                        }
433
                }
434
        }
435
        janus_mutex_unlock(&sessions_mutex);
436

    
437
        return G_SOURCE_CONTINUE;
438
}
439

    
440
static gpointer janus_sessions_watchdog(gpointer user_data) {
441
        GMainLoop *loop = (GMainLoop *) user_data;
442
        GMainContext *watchdog_context = g_main_loop_get_context(loop);
443
        GSource *timeout_source;
444

    
445
        timeout_source = g_timeout_source_new_seconds(2);
446
        g_source_set_callback(timeout_source, janus_check_sessions, watchdog_context, NULL);
447
        g_source_attach(timeout_source, watchdog_context);
448
        g_source_unref(timeout_source);
449

    
450
        JANUS_LOG(LOG_INFO, "Sessions watchdog started\n");
451

    
452
        g_main_loop_run(loop);
453

    
454
        return NULL;
455
}
456

    
457
janus_session *janus_session_create(guint64 session_id) {
458
        if(session_id == 0) {
459
                while(session_id == 0) {
460
                        session_id = g_random_int();
461
                        if(janus_session_find(session_id) != NULL) {
462
                                /* Session ID already taken, try another one */
463
                                session_id = 0;
464
                        }
465
                }
466
        }
467
        JANUS_LOG(LOG_INFO, "Creating new session: %"SCNu64"\n", session_id);
468
        janus_session *session = (janus_session *)g_malloc0(sizeof(janus_session));
469
        if(session == NULL) {
470
                JANUS_LOG(LOG_FATAL, "Memory error!\n");
471
                return NULL;
472
        }
473
        session->session_id = session_id;
474
        session->source = NULL;
475
        session->destroy = 0;
476
        session->timeout = 0;
477
        session->last_activity = janus_get_monotonic_time();
478
        janus_mutex_init(&session->mutex);
479
        janus_mutex_lock(&sessions_mutex);
480
        g_hash_table_insert(sessions, GUINT_TO_POINTER(session_id), session);
481
        janus_mutex_unlock(&sessions_mutex);
482
        /* Notify event handlers */
483
        if(janus_events_is_enabled())
484
                janus_events_notify_handlers(JANUS_EVENT_TYPE_SESSION, session_id, "created");
485
        return session;
486
}
487

    
488
janus_session *janus_session_find(guint64 session_id) {
489
        janus_mutex_lock(&sessions_mutex);
490
        janus_session *session = g_hash_table_lookup(sessions, GUINT_TO_POINTER(session_id));
491
        janus_mutex_unlock(&sessions_mutex);
492
        return session;
493
}
494

    
495
janus_session *janus_session_find_destroyed(guint64 session_id) {
496
        janus_mutex_lock(&sessions_mutex);
497
        janus_session *session = g_hash_table_lookup(old_sessions, GUINT_TO_POINTER(session_id));
498
        janus_mutex_unlock(&sessions_mutex);
499
        return session;
500
}
501

    
502
void janus_session_notify_event(guint64 session_id, json_t *event) {
503
        janus_mutex_lock(&sessions_mutex);
504
        janus_session *session = sessions ? g_hash_table_lookup(sessions, GUINT_TO_POINTER(session_id)) : NULL;
505
        if(session != NULL && !session->destroy && session->source != NULL && session->source->transport != NULL) {
506
                janus_mutex_unlock(&sessions_mutex);
507
                /* Send this to the transport client */
508
                JANUS_LOG(LOG_HUGE, "Sending event to %s (%p)\n", session->source->transport->get_package(), session->source->instance);
509
                session->source->transport->send_message(session->source->instance, NULL, FALSE, event);
510
        } else {
511
                janus_mutex_unlock(&sessions_mutex);
512
                /* No transport, free the event */
513
                json_decref(event);
514
        }
515
}
516

    
517

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

    
542
        /* FIXME Actually destroy session */
543
        janus_session_free(session);
544

    
545
        return 0;
546
}
547

    
548
void janus_session_free(janus_session *session) {
549
        if(session == NULL)
550
                return;
551
        janus_mutex_lock(&session->mutex);
552
        if(session->ice_handles != NULL) {
553
                g_hash_table_destroy(session->ice_handles);
554
                session->ice_handles = NULL;
555
        }
556
        if(session->source != NULL) {
557
                janus_request_destroy(session->source);
558
                session->source = NULL;
559
        }
560
        janus_mutex_unlock(&session->mutex);
561
        g_free(session);
562
        session = NULL;
563
}
564

    
565

    
566
/* Requests management */
567
janus_request *janus_request_new(janus_transport *transport, void *instance, void *request_id, gboolean admin, json_t *message) {
568
        janus_request *request = (janus_request *)g_malloc0(sizeof(janus_request));
569
        request->transport = transport;
570
        request->instance = instance;
571
        request->request_id = request_id;
572
        request->admin = admin;
573
        request->message = message;
574
        return request;
575
}
576

    
577
void janus_request_destroy(janus_request *request) {
578
        if(request == NULL)
579
                return;
580
        request->transport = NULL;
581
        request->instance = NULL;
582
        request->request_id = NULL;
583
        if(request->message)
584
                json_decref(request->message);
585
        request->message = NULL;
586
        g_free(request);
587
}
588

    
589
int janus_process_incoming_request(janus_request *request) {
590
        int ret = -1;
591
        if(request == NULL) {
592
                JANUS_LOG(LOG_ERR, "Missing request or payload to process, giving up...\n");
593
                return ret;
594
        }
595
        int error_code = 0;
596
        char error_cause[100];
597
        json_t *root = request->message;
598
        /* Ok, let's start with the ids */
599
        guint64 session_id = 0, handle_id = 0;
600
        json_t *s = json_object_get(root, "session_id");
601
        if(s && json_is_integer(s))
602
                session_id = json_integer_value(s);
603
        json_t *h = json_object_get(root, "handle_id");
604
        if(h && json_is_integer(h))
605
                handle_id = json_integer_value(h);
606

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

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

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

    
756
        /* What is this? */
757
        if(!strcasecmp(message_text, "keepalive")) {
758
                /* Just a keep-alive message, reply with an ack */
759
                JANUS_LOG(LOG_VERB, "Got a keep-alive on session %"SCNu64"\n", session_id);
760
                json_t *reply = json_object();
761
                json_object_set_new(reply, "janus", json_string("ack"));
762
                json_object_set_new(reply, "session_id", json_integer(session_id));
763
                json_object_set_new(reply, "transaction", json_string(transaction_text));
764
                /* Send the success reply */
765
                ret = janus_process_success(request, reply);
766
        } else if(!strcasecmp(message_text, "attach")) {
767
                if(handle != NULL) {
768
                        /* Attach is a session-level command */
769
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
770
                        goto jsondone;
771
                }
772
                JANUS_VALIDATE_JSON_OBJECT(root, attach_parameters,
773
                        error_code, error_cause, FALSE,
774
                        JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
775
                if(error_code != 0) {
776
                        ret = janus_process_error_string(request, session_id, NULL, error_code, error_cause);
777
                        goto jsondone;
778
                }
779
                json_t *plugin = json_object_get(root, "plugin");
780
                const gchar *plugin_text = json_string_value(plugin);
781
                janus_plugin *plugin_t = janus_plugin_find(plugin_text);
782
                if(plugin_t == NULL) {
783
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_NOT_FOUND, "No such plugin '%s'", plugin_text);
784
                        goto jsondone;
785
                }
786
                /* If the auth token mechanism is enabled, we should check if this token can access this plugin */
787
                if(janus_auth_is_enabled()) {
788
                        json_t *token = json_object_get(root, "token");
789
                        if(token != NULL) {
790
                                const char *token_value = json_string_value(token);
791
                                if(token_value && !janus_auth_check_plugin(token_value, plugin_t)) {
792
                                        JANUS_LOG(LOG_ERR, "Token '%s' can't access plugin '%s'\n", token_value, plugin_text);
793
                                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED_PLUGIN, "Provided token can't access plugin '%s'", plugin_text);
794
                                        goto jsondone;
795
                                }
796
                        }
797
                }
798
                /* Create handle */
799
                handle = janus_ice_handle_create(session);
800
                if(handle == NULL) {
801
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Memory error");
802
                        goto jsondone;
803
                }
804
                handle_id = handle->handle_id;
805
                /* Attach to the plugin */
806
                int error = 0;
807
                if((error = janus_ice_handle_attach_plugin(session, handle_id, plugin_t)) != 0) {
808
                        /* TODO Make error struct to pass verbose information */
809
                        janus_ice_handle_destroy(session, handle_id);
810
                        janus_mutex_lock(&session->mutex);
811
                        g_hash_table_remove(session->ice_handles, GUINT_TO_POINTER(handle_id));
812
                        janus_mutex_unlock(&session->mutex);
813

    
814
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_ATTACH, "Couldn't attach to plugin: error '%d'", error);
815
                        goto jsondone;
816
                }
817
                /* Prepare JSON reply */
818
                json_t *reply = json_object();
819
                json_object_set_new(reply, "janus", json_string("success"));
820
                json_object_set_new(reply, "session_id", json_integer(session_id));
821
                json_object_set_new(reply, "transaction", json_string(transaction_text));
822
                json_t *data = json_object();
823
                json_object_set_new(data, "id", json_integer(handle_id));
824
                json_object_set_new(reply, "data", data);
825
                /* Send the success reply */
826
                ret = janus_process_success(request, reply);
827
        } else if(!strcasecmp(message_text, "destroy")) {
828
                if(handle != NULL) {
829
                        /* Query is a session-level command */
830
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
831
                        goto jsondone;
832
                }
833
                /* Schedule the session for deletion */
834
                session->destroy = 1;
835
                janus_mutex_lock(&sessions_mutex);
836
                g_hash_table_remove(sessions, GUINT_TO_POINTER(session->session_id));
837
                g_hash_table_insert(old_sessions, GUINT_TO_POINTER(session->session_id), session);
838
                GSource *timeout_source = g_timeout_source_new_seconds(3);
839
                g_source_set_callback(timeout_source, janus_cleanup_session, session, NULL);
840
                g_source_attach(timeout_source, sessions_watchdog_context);
841
                g_source_unref(timeout_source);
842
                janus_mutex_unlock(&sessions_mutex);
843
                /* Notify the source that the session has been destroyed */
844
                if(session->source && session->source->transport)
845
                        session->source->transport->session_over(session->source->instance, session->session_id, FALSE);
846

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

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

    
1287
                /* Make sure the app handle is still valid */
1288
                if(handle->app == NULL || handle->app_handle == NULL || !janus_plugin_session_is_alive(handle->app_handle)) {
1289
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "No plugin to handle this message");
1290
                        if(jsep_type)
1291
                                g_free(jsep_type);
1292
                        if(jsep_sdp_stripped)
1293
                                g_free(jsep_sdp_stripped);
1294
                        janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1295
                        goto jsondone;
1296
                }
1297

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

    
1440
trickledone:
1441
                janus_mutex_unlock(&handle->mutex);
1442
                /* We reply right away, not to block the web server... */
1443
                json_t *reply = json_object();
1444
                json_object_set_new(reply, "janus", json_string("ack"));
1445
                json_object_set_new(reply, "session_id", json_integer(session_id));
1446
                json_object_set_new(reply, "transaction", json_string(transaction_text));
1447
                /* Send the success reply */
1448
                ret = janus_process_success(request, reply);
1449
        } else {
1450
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN_REQUEST, "Unknown request '%s'", message_text);
1451
        }
1452

    
1453
jsondone:
1454
        /* Done processing */
1455
        return ret;
1456
}
1457

    
1458
/* Admin/monitor WebServer requests handler */
1459
int janus_process_incoming_admin_request(janus_request *request) {
1460
        int ret = -1;
1461
        int error_code = 0;
1462
        char error_cause[100];
1463
        if(request == NULL) {
1464
                JANUS_LOG(LOG_ERR, "Missing request or payload to process, giving up...\n");
1465
                return ret;
1466
        }
1467
        json_t *root = request->message;
1468
        /* Ok, let's start with the ids */
1469
        guint64 session_id = 0, handle_id = 0;
1470
        json_t *s = json_object_get(root, "session_id");
1471
        if(s && json_is_integer(s))
1472
                session_id = json_integer_value(s);
1473
        json_t *h = json_object_get(root, "handle_id");
1474
        if(h && json_is_integer(h))
1475
                handle_id = json_integer_value(h);
1476

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

    
2040
        /* Go on with the processing */
2041
        if(admin_api_secret != NULL) {
2042
                /* There's an API secret, check that the client provided it */
2043
                json_t *secret = json_object_get(root, "admin_secret");
2044
                if(!secret || !json_is_string(secret) || !janus_strcmp_const_time(json_string_value(secret), admin_api_secret)) {
2045
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED, NULL);
2046
                        goto jsondone;
2047
                }
2048
        }
2049

    
2050
        /* If we got here, make sure we have a session (and/or a handle) */
2051
        janus_session *session = janus_session_find(session_id);
2052
        if(!session) {
2053
                JANUS_LOG(LOG_ERR, "Couldn't find any session %"SCNu64"...\n", session_id);
2054
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, "No such session %"SCNu64"", session_id);
2055
                goto jsondone;
2056
        }
2057
        janus_ice_handle *handle = NULL;
2058
        if(handle_id > 0) {
2059
                handle = janus_ice_handle_find(session, handle_id);
2060
                if(!handle) {
2061
                        JANUS_LOG(LOG_ERR, "Couldn't find any handle %"SCNu64" in session %"SCNu64"...\n", handle_id, session_id);
2062
                        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);
2063
                        goto jsondone;
2064
                }
2065
        }
2066

    
2067
        /* What is this? */
2068
        if(handle == NULL) {
2069
                /* Session-related */
2070
                if(strcasecmp(message_text, "list_handles")) {
2071
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
2072
                        goto jsondone;
2073
                }
2074
                /* List handles */
2075
                json_t *list = json_array();
2076
                if(session->ice_handles != NULL && g_hash_table_size(session->ice_handles) > 0) {
2077
                        GHashTableIter iter;
2078
                        gpointer value;
2079
                        janus_mutex_lock(&session->mutex);
2080
                        g_hash_table_iter_init(&iter, session->ice_handles);
2081
                        while (g_hash_table_iter_next(&iter, NULL, &value)) {
2082
                                janus_ice_handle *handle = value;
2083
                                if(handle == NULL) {
2084
                                        continue;
2085
                                }
2086
                                json_array_append_new(list, json_integer(handle->handle_id));
2087
                        }
2088
                        janus_mutex_unlock(&session->mutex);
2089
                }
2090
                /* Prepare JSON reply */
2091
                json_t *reply = json_object();
2092
                json_object_set_new(reply, "janus", json_string("success"));
2093
                json_object_set_new(reply, "transaction", json_string(transaction_text));
2094
                json_object_set_new(reply, "session_id", json_integer(session_id));
2095
                json_object_set_new(reply, "handles", list);
2096
                /* Send the success reply */
2097
                ret = janus_process_success(request, reply);
2098
                goto jsondone;
2099
        } else {
2100
                /* Handle-related */
2101
                if(strcasecmp(message_text, "handle_info")) {
2102
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
2103
                        goto jsondone;
2104
                }
2105
                /* Prepare info */
2106
                janus_mutex_lock(&handle->mutex);
2107
                json_t *info = json_object();
2108
                json_object_set_new(info, "session_id", json_integer(session_id));
2109
                json_object_set_new(info, "session_last_activity", json_integer(session->last_activity));
2110
                if(session->source && session->source->transport)
2111
                        json_object_set_new(info, "session_transport", json_string(session->source->transport->get_package()));
2112
                json_object_set_new(info, "handle_id", json_integer(handle_id));
2113
                json_object_set_new(info, "created", json_integer(handle->created));
2114
                json_object_set_new(info, "send_thread_created", json_integer(g_atomic_int_get(&handle->send_thread_created)));
2115
                json_object_set_new(info, "current_time", json_integer(janus_get_monotonic_time()));
2116
                if(handle->app && handle->app_handle && janus_plugin_session_is_alive(handle->app_handle)) {
2117
                        janus_plugin *plugin = (janus_plugin *)handle->app;
2118
                        json_object_set_new(info, "plugin", json_string(plugin->get_package()));
2119
                        if(plugin->query_session) {
2120
                                /* FIXME This check will NOT work with legacy plugins that were compiled BEFORE the method was specified in plugin.h */
2121
                                char *query = plugin->query_session(handle->app_handle);
2122
                                if(query != NULL) {
2123
                                        /* Make sure this is JSON */
2124
                                        json_error_t error;
2125
                                        json_t *query_info = json_loads(query, 0, &error);
2126
                                        if(!query_info || !json_is_object(query_info)) {
2127
                                                JANUS_LOG(LOG_WARN, "Ignoring invalid query response from the plugin\n");
2128
                                        } else {
2129
                                                json_object_set_new(info, "plugin_specific", query_info);
2130
                                        }
2131
                                        g_free(query);
2132
                                        query = NULL;
2133
                                }
2134
                        }
2135
                }
2136
                json_t *flags = json_object();
2137
                json_object_set_new(flags, "got-offer", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER)));
2138
                json_object_set_new(flags, "got-answer", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER)));
2139
                json_object_set_new(flags, "processing-offer", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER)));
2140
                json_object_set_new(flags, "starting", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_START)));
2141
                json_object_set_new(flags, "ready", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_READY)));
2142
                json_object_set_new(flags, "stopped", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)));
2143
                json_object_set_new(flags, "alert", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)));
2144
                json_object_set_new(flags, "bundle", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)));
2145
                json_object_set_new(flags, "rtcp-mux", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX)));
2146
                json_object_set_new(flags, "trickle", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE)));
2147
                json_object_set_new(flags, "all-trickles", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALL_TRICKLES)));
2148
                json_object_set_new(flags, "trickle-synced", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE_SYNCED)));
2149
                json_object_set_new(flags, "data-channels", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS)));
2150
                json_object_set_new(flags, "has-audio", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO)));
2151
                json_object_set_new(flags, "has-video", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO)));
2152
                json_object_set_new(flags, "plan-b", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PLAN_B)));
2153
                json_object_set_new(flags, "cleaning", json_integer(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING)));
2154
                json_object_set_new(info, "flags", flags);
2155
                if(handle->agent) {
2156
                        json_object_set_new(info, "agent-created", json_integer(handle->agent_created));
2157
                        json_object_set_new(info, "ice-mode", json_string(janus_ice_is_ice_lite_enabled() ? "lite" : "full"));
2158
                        json_object_set_new(info, "ice-role", json_string(handle->controlling ? "controlling" : "controlled"));
2159
                }
2160
                json_t *sdps = json_object();
2161
                if(handle->rtp_profile)
2162
                        json_object_set_new(sdps, "profile", json_string(handle->rtp_profile));
2163
                if(handle->local_sdp)
2164
                        json_object_set_new(sdps, "local", json_string(handle->local_sdp));
2165
                if(handle->remote_sdp)
2166
                        json_object_set_new(sdps, "remote", json_string(handle->remote_sdp));
2167
                json_object_set_new(info, "sdps", sdps);
2168
                if(handle->pending_trickles)
2169
                        json_object_set_new(info, "pending-trickles", json_integer(g_list_length(handle->pending_trickles)));
2170
                json_t *streams = json_array();
2171
                if(handle->audio_stream) {
2172
                        json_t *s = janus_admin_stream_summary(handle->audio_stream);
2173
                        if(s)
2174
                                json_array_append_new(streams, s);
2175
                }
2176
                if(handle->video_stream) {
2177
                        json_t *s = janus_admin_stream_summary(handle->video_stream);
2178
                        if(s)
2179
                                json_array_append_new(streams, s);
2180
                }
2181
                if(handle->data_stream) {
2182
                        json_t *s = janus_admin_stream_summary(handle->data_stream);
2183
                        if(s)
2184
                                json_array_append_new(streams, s);
2185
                }
2186
                json_object_set_new(info, "streams", streams);
2187
                janus_mutex_unlock(&handle->mutex);
2188
                /* Prepare JSON reply */
2189
                json_t *reply = json_object();
2190
                json_object_set_new(reply, "janus", json_string("success"));
2191
                json_object_set_new(reply, "transaction", json_string(transaction_text));
2192
                json_object_set_new(reply, "session_id", json_integer(session_id));
2193
                json_object_set_new(reply, "handle_id", json_integer(handle_id));
2194
                json_object_set_new(reply, "info", info);
2195
                /* Send the success reply */
2196
                ret = janus_process_success(request, reply);
2197
                goto jsondone;
2198
        }
2199

    
2200
jsondone:
2201
        /* Done processing */
2202
        return ret;
2203
}
2204

    
2205
int janus_process_success(janus_request *request, json_t *payload)
2206
{
2207
        if(!request || !payload)
2208
                return -1;
2209
        /* Pass to the right transport plugin */
2210
        JANUS_LOG(LOG_HUGE, "Sending %s API response to %s (%p)\n", request->admin ? "admin" : "Janus", request->transport->get_package(), request->instance);
2211
        return request->transport->send_message(request->instance, request->request_id, request->admin, payload);
2212
}
2213

    
2214
static int janus_process_error_string(janus_request *request, uint64_t session_id, const char *transaction, gint error, gchar *error_string)
2215
{
2216
        if(!request)
2217
                return -1;
2218
        /* Done preparing error */
2219
        JANUS_LOG(LOG_VERB, "[%s] Returning %s API error %d (%s)\n", transaction, request->admin ? "admin" : "Janus", error, error_string);
2220
        /* Prepare JSON error */
2221
        json_t *reply = json_object();
2222
        json_object_set_new(reply, "janus", json_string("error"));
2223
        if(session_id > 0)
2224
                json_object_set_new(reply, "session_id", json_integer(session_id));
2225
        if(transaction != NULL)
2226
                json_object_set_new(reply, "transaction", json_string(transaction));
2227
        json_t *error_data = json_object();
2228
        json_object_set_new(error_data, "code", json_integer(error));
2229
        json_object_set_new(error_data, "reason", json_string(error_string));
2230
        json_object_set_new(reply, "error", error_data);
2231
        /* Pass to the right transport plugin */
2232
        return request->transport->send_message(request->instance, request->request_id, request->admin, reply);
2233
}
2234

    
2235
int janus_process_error(janus_request *request, uint64_t session_id, const char *transaction, gint error, const char *format, ...)
2236
{
2237
        if(!request)
2238
                return -1;
2239
        gchar *error_string = NULL;
2240
        gchar error_buf[512];
2241
        if(format == NULL) {
2242
                /* No error string provided, use the default one */
2243
                error_string = (gchar *)janus_get_api_error(error);
2244
        } else {
2245
                /* This callback has variable arguments (error string) */
2246
                va_list ap;
2247
                va_start(ap, format);
2248
                g_vsnprintf(error_buf, sizeof(error_buf), format, ap);
2249
                va_end(ap);
2250
                error_string = error_buf;
2251
        }
2252
        return janus_process_error_string(request, session_id, transaction, error, error_string);
2253
}
2254

    
2255
/* Admin/monitor helpers */
2256
json_t *janus_admin_stream_summary(janus_ice_stream *stream) {
2257
        if(stream == NULL)
2258
                return NULL;
2259
        json_t *s = json_object();
2260
        json_object_set_new(s, "id", json_integer(stream->stream_id));
2261
        json_object_set_new(s, "ready", json_integer(stream->cdone));
2262
        json_object_set_new(s, "disabled", json_string(stream->disabled ? "true" : "false"));
2263
        json_t *ss = json_object();
2264
        if(stream->audio_ssrc)
2265
                json_object_set_new(ss, "audio", json_integer(stream->audio_ssrc));
2266
        if(stream->video_ssrc)
2267
                json_object_set_new(ss, "video", json_integer(stream->video_ssrc));
2268
        if(stream->audio_ssrc_peer)
2269
                json_object_set_new(ss, "audio-peer", json_integer(stream->audio_ssrc_peer));
2270
        if(stream->video_ssrc_peer)
2271
                json_object_set_new(ss, "video-peer", json_integer(stream->video_ssrc_peer));
2272
        if(stream->video_ssrc_peer_rtx)
2273
                json_object_set_new(ss, "video-peer-rtx", json_integer(stream->video_ssrc_peer_rtx));
2274
        json_object_set_new(s, "ssrc", ss);
2275
        json_t *components = json_array();
2276
        if(stream->rtp_component) {
2277
                json_t *c = janus_admin_component_summary(stream->rtp_component);
2278
                if(c)
2279
                        json_array_append_new(components, c);
2280
        }
2281
        if(stream->rtcp_component) {
2282
                json_t *c = janus_admin_component_summary(stream->rtcp_component);
2283
                if(c)
2284
                        json_array_append_new(components, c);
2285
        }
2286
        json_t *rtcp_stats = NULL;
2287
        if(stream->audio_rtcp_ctx != NULL) {
2288
                rtcp_stats = json_object();
2289
                json_t *audio_rtcp_stats = json_object();
2290
                json_object_set_new(audio_rtcp_stats, "base", json_integer(stream->audio_rtcp_ctx->tb));
2291
                json_object_set_new(audio_rtcp_stats, "lsr", json_integer(janus_rtcp_context_get_lsr(stream->audio_rtcp_ctx)));
2292
                json_object_set_new(audio_rtcp_stats, "lost", json_integer(janus_rtcp_context_get_lost_all(stream->audio_rtcp_ctx, FALSE)));
2293
                json_object_set_new(audio_rtcp_stats, "lost-by-remote", json_integer(janus_rtcp_context_get_lost_all(stream->audio_rtcp_ctx, TRUE)));
2294
                json_object_set_new(audio_rtcp_stats, "jitter-local", json_integer(janus_rtcp_context_get_jitter(stream->audio_rtcp_ctx, FALSE)));
2295
                json_object_set_new(audio_rtcp_stats, "jitter-remote", json_integer(janus_rtcp_context_get_jitter(stream->audio_rtcp_ctx, TRUE)));
2296
                json_object_set_new(rtcp_stats, "audio", audio_rtcp_stats);
2297
        }
2298
        if(stream->video_rtcp_ctx != NULL) {
2299
                if(rtcp_stats == NULL)
2300
                        rtcp_stats = json_object();
2301
                json_t *video_rtcp_stats = json_object();
2302
                json_object_set_new(video_rtcp_stats, "base", json_integer(stream->video_rtcp_ctx->tb));
2303
                json_object_set_new(video_rtcp_stats, "lsr", json_integer(janus_rtcp_context_get_lsr(stream->video_rtcp_ctx)));
2304
                json_object_set_new(video_rtcp_stats, "lost", json_integer(janus_rtcp_context_get_lost_all(stream->video_rtcp_ctx, FALSE)));
2305
                json_object_set_new(video_rtcp_stats, "lost-by-remote", json_integer(janus_rtcp_context_get_lost_all(stream->video_rtcp_ctx, TRUE)));
2306
                json_object_set_new(video_rtcp_stats, "jitter-local", json_integer(janus_rtcp_context_get_jitter(stream->video_rtcp_ctx, FALSE)));
2307
                json_object_set_new(video_rtcp_stats, "jitter-remote", json_integer(janus_rtcp_context_get_jitter(stream->video_rtcp_ctx, TRUE)));
2308
                json_object_set_new(rtcp_stats, "video", video_rtcp_stats);
2309
        }
2310
        if(rtcp_stats != NULL)
2311
                json_object_set_new(s, "rtcp_stats", rtcp_stats);
2312
        json_object_set_new(s, "components", components);
2313
        return s;
2314
}
2315

    
2316
json_t *janus_admin_component_summary(janus_ice_component *component) {
2317
        if(component == NULL)
2318
                return NULL;
2319
        janus_ice_handle *handle = component->stream ? component->stream->handle : NULL;
2320
        json_t *c = json_object();
2321
        json_object_set_new(c, "id", json_integer(component->component_id));
2322
        json_object_set_new(c, "state", json_string(janus_get_ice_state_name(component->state)));
2323
        if(component->component_connected > 0)
2324
                json_object_set_new(c, "connected", json_integer(component->component_connected));
2325
        if(component->local_candidates) {
2326
                json_t *cs = json_array();
2327
                GSList *candidates = component->local_candidates, *i = NULL;
2328
                for (i = candidates; i; i = i->next) {
2329
                        gchar *lc = (gchar *) i->data;
2330
                        if(lc)
2331
                                json_array_append_new(cs, json_string(lc));
2332
                }
2333
                json_object_set_new(c, "local-candidates", cs);
2334
        }
2335
        if(component->remote_candidates) {
2336
                json_t *cs = json_array();
2337
                GSList *candidates = component->remote_candidates, *i = NULL;
2338
                for (i = candidates; i; i = i->next) {
2339
                        gchar *rc = (gchar *) i->data;
2340
                        if(rc)
2341
                                json_array_append_new(cs, json_string(rc));
2342
                }
2343
                json_object_set_new(c, "remote-candidates", cs);
2344
        }
2345
        if(component->selected_pair) {
2346
                json_object_set_new(c, "selected-pair", json_string(component->selected_pair));
2347
        }
2348
        json_t *d = json_object();
2349
        json_t *in_stats = json_object();
2350
        json_t *out_stats = json_object();
2351
        if(component->dtls) {
2352
                janus_dtls_srtp *dtls = component->dtls;
2353
                json_object_set_new(d, "fingerprint", json_string(janus_dtls_get_local_fingerprint()));
2354
                json_object_set_new(d, "remote-fingerprint", json_string(component->stream->remote_fingerprint));
2355
                json_object_set_new(d, "remote-fingerprint-hash", json_string(component->stream->remote_hashing));
2356
                json_object_set_new(d, "dtls-role", json_string(janus_get_dtls_srtp_role(component->stream->dtls_role)));
2357
                json_object_set_new(d, "dtls-state", json_string(janus_get_dtls_srtp_state(dtls->dtls_state)));
2358
                json_object_set_new(d, "valid", json_integer(dtls->srtp_valid));
2359
                json_object_set_new(d, "ready", json_integer(dtls->ready));
2360
                if(dtls->dtls_connected > 0)
2361
                        json_object_set_new(d, "connected", json_integer(dtls->dtls_connected));
2362
                if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO)) {
2363
                        json_object_set_new(in_stats, "audio_packets", json_integer(component->in_stats.audio_packets));
2364
                        json_object_set_new(in_stats, "audio_bytes", json_integer(component->in_stats.audio_bytes));
2365
                        json_object_set_new(in_stats, "audio_nacks", json_integer(component->in_stats.audio_nacks));
2366
                        /* Compute the last second stuff too */
2367
                        gint64 now = janus_get_monotonic_time();
2368
                        guint64 bytes = 0;
2369
                        if(component->in_stats.audio_bytes_lastsec) {
2370
                                GList *lastsec = component->in_stats.audio_bytes_lastsec;
2371
                                while(lastsec) {
2372
                                        janus_ice_stats_item *s = (janus_ice_stats_item *)lastsec->data;
2373
                                        if(s && now-s->when < G_USEC_PER_SEC)
2374
                                                bytes += s->bytes;
2375
                                        lastsec = lastsec->next;
2376
                                }
2377
                        }
2378
                        json_object_set_new(in_stats, "audio_bytes_lastsec", json_integer(bytes));
2379
                }
2380
                if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO)) {
2381
                        json_object_set_new(in_stats, "video_packets", json_integer(component->in_stats.video_packets));
2382
                        json_object_set_new(in_stats, "video_bytes", json_integer(component->in_stats.video_bytes));
2383
                        json_object_set_new(in_stats, "video_nacks", json_integer(component->in_stats.video_nacks));
2384
                        /* Compute the last second stuff too */
2385
                        gint64 now = janus_get_monotonic_time();
2386
                        guint64 bytes = 0;
2387
                        if(component->in_stats.video_bytes_lastsec) {
2388
                                GList *lastsec = component->in_stats.video_bytes_lastsec;
2389
                                while(lastsec) {
2390
                                        janus_ice_stats_item *s = (janus_ice_stats_item *)lastsec->data;
2391
                                        if(s && now-s->when < G_USEC_PER_SEC)
2392
                                                bytes += s->bytes;
2393
                                        lastsec = lastsec->next;
2394
                                }
2395
                        }
2396
                        json_object_set_new(in_stats, "video_bytes_lastsec", json_integer(bytes));
2397
                }
2398
                json_object_set_new(in_stats, "data_packets", json_integer(component->in_stats.data_packets));
2399
                json_object_set_new(in_stats, "data_bytes", json_integer(component->in_stats.data_bytes));
2400
                if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO)) {
2401
                        json_object_set_new(out_stats, "audio_packets", json_integer(component->out_stats.audio_packets));
2402
                        json_object_set_new(out_stats, "audio_bytes", json_integer(component->out_stats.audio_bytes));
2403
                        json_object_set_new(out_stats, "audio_nacks", json_integer(component->out_stats.audio_nacks));
2404
                }
2405
                if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO)) {
2406
                        json_object_set_new(out_stats, "video_packets", json_integer(component->out_stats.video_packets));
2407
                        json_object_set_new(out_stats, "video_bytes", json_integer(component->out_stats.video_bytes));
2408
                        json_object_set_new(out_stats, "video_nacks", json_integer(component->out_stats.video_nacks));
2409
                }
2410
                json_object_set_new(out_stats, "data_packets", json_integer(component->out_stats.data_packets));
2411
                json_object_set_new(out_stats, "data_bytes", json_integer(component->out_stats.data_bytes));
2412
#ifdef HAVE_SCTP
2413
                if(dtls->sctp)        /* FIXME */
2414
                        json_object_set_new(d, "sctp-association", json_integer(1));
2415
#endif
2416
        }
2417
        json_object_set_new(c, "dtls", d);
2418
        json_object_set_new(c, "in_stats", in_stats);
2419
        json_object_set_new(c, "out_stats", out_stats);
2420
        return c;
2421
}
2422

    
2423

    
2424
/* Transports */
2425
void janus_transport_close(gpointer key, gpointer value, gpointer user_data) {
2426
        janus_transport *transport = (janus_transport *)value;
2427
        if(!transport)
2428
                return;
2429
        transport->destroy();
2430
}
2431

    
2432
void janus_transportso_close(gpointer key, gpointer value, gpointer user_data) {
2433
        void *transport = (janus_transport *)value;
2434
        if(!transport)
2435
                return;
2436
        //~ dlclose(transport);
2437
}
2438

    
2439
/* Transport callback interface */
2440
void janus_transport_incoming_request(janus_transport *plugin, void *transport, void *request_id, gboolean admin, json_t *message, json_error_t *error) {
2441
        JANUS_LOG(LOG_VERB, "Got %s API request from %s (%p)\n", admin ? "an admin" : "a Janus", plugin->get_package(), transport);
2442
        /* Create a janus_request instance to handle the request */
2443
        janus_request *request = janus_request_new(plugin, transport, request_id, admin, message);
2444
        GError *tperror = NULL;
2445
        g_thread_pool_push(tasks, request, &tperror);
2446
        if(tperror != NULL) {
2447
                /* Something went wrong... */
2448
                JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to push task in thread pool...\n", tperror->code, tperror->message ? tperror->message : "??");
2449
                json_t *transaction = json_object_get(message, "transaction");
2450
                const char *transaction_text = json_is_string(transaction) ? json_string_value(transaction) : NULL;
2451
                janus_process_error(request, 0, transaction_text, JANUS_ERROR_UNKNOWN, "Thread pool error");
2452
                janus_request_destroy(request);
2453
        }
2454
}
2455

    
2456
void janus_transport_gone(janus_transport *plugin, void *transport) {
2457
        /* Get rid of sessions this transport was handling */
2458
        JANUS_LOG(LOG_VERB, "A %s transport instance has gone away (%p)\n", plugin->get_package(), transport);
2459
        janus_mutex_lock(&sessions_mutex);
2460
        if(sessions && g_hash_table_size(sessions) > 0) {
2461
                GHashTableIter iter;
2462
                gpointer value;
2463
                g_hash_table_iter_init(&iter, sessions);
2464
                while(g_hash_table_iter_next(&iter, NULL, &value)) {
2465
                        janus_session *session = (janus_session *) value;
2466
                        if(!session || session->destroy || session->timeout || session->last_activity == 0)
2467
                                continue;
2468
                        if(session->source && session->source->instance == transport) {
2469
                                JANUS_LOG(LOG_VERB, "  -- Marking Session %"SCNu64" as over\n", session->session_id);
2470
                                session->last_activity = 0;        /* This will trigger a timeout */
2471
                        }
2472
                }
2473
        }
2474
        janus_mutex_unlock(&sessions_mutex);
2475
}
2476

    
2477
gboolean janus_transport_is_api_secret_needed(janus_transport *plugin) {
2478
        return api_secret != NULL;
2479
}
2480

    
2481
gboolean janus_transport_is_api_secret_valid(janus_transport *plugin, const char *apisecret) {
2482
        if(api_secret == NULL)
2483
                return TRUE;
2484
        return apisecret && janus_strcmp_const_time(apisecret, api_secret);
2485
}
2486

    
2487
gboolean janus_transport_is_auth_token_needed(janus_transport *plugin) {
2488
        return janus_auth_is_enabled();
2489
}
2490

    
2491
gboolean janus_transport_is_auth_token_valid(janus_transport *plugin, const char *token) {
2492
        if(!janus_auth_is_enabled())
2493
                return TRUE;
2494
        return token && janus_auth_check_token(token);
2495
}
2496

    
2497
void janus_transport_task(gpointer data, gpointer user_data) {
2498
        JANUS_LOG(LOG_VERB, "Transport task pool, serving request\n");
2499
        janus_request *request = (janus_request *)data;
2500
        if(request == NULL) {
2501
                JANUS_LOG(LOG_ERR, "Missing request\n");
2502
                return;
2503
        }
2504
        if(!request->admin)
2505
                janus_process_incoming_request(request);
2506
        else
2507
                janus_process_incoming_admin_request(request);
2508
        /* Done */
2509
        janus_request_destroy(request);
2510
}
2511

    
2512

    
2513
/* Event handlers */
2514
void janus_eventhandler_close(gpointer key, gpointer value, gpointer user_data) {
2515
        janus_eventhandler *eventhandler = (janus_eventhandler *)value;
2516
        if(!eventhandler)
2517
                return;
2518
        eventhandler->destroy();
2519
}
2520

    
2521
void janus_eventhandlerso_close(gpointer key, gpointer value, gpointer user_data) {
2522
        void *eventhandler = (janus_eventhandler *)value;
2523
        if(!eventhandler)
2524
                return;
2525
        //~ dlclose(eventhandler);
2526
}
2527

    
2528

    
2529
/* Plugins */
2530
void janus_plugin_close(gpointer key, gpointer value, gpointer user_data) {
2531
        janus_plugin *plugin = (janus_plugin *)value;
2532
        if(!plugin)
2533
                return;
2534
        plugin->destroy();
2535
}
2536

    
2537
void janus_pluginso_close(gpointer key, gpointer value, gpointer user_data) {
2538
        void *plugin = (janus_plugin *)value;
2539
        if(!plugin)
2540
                return;
2541
        //~ dlclose(plugin);
2542
}
2543

    
2544
janus_plugin *janus_plugin_find(const gchar *package) {
2545
        if(package != NULL && plugins != NULL)        /* FIXME Do we need to fix the key pointer? */
2546
                return g_hash_table_lookup(plugins, package);
2547
        return NULL;
2548
}
2549

    
2550

    
2551
/* Plugin callback interface */
2552
int janus_plugin_push_event(janus_plugin_session *plugin_session, janus_plugin *plugin, const char *transaction, const char *message, const char *sdp_type, const char *sdp) {
2553
        if(!plugin || !message)
2554
                return -1;
2555
        if(!plugin_session || plugin_session < (janus_plugin_session *)0x1000 ||
2556
                        !janus_plugin_session_is_alive(plugin_session) || plugin_session->stopped)
2557
                return -2;
2558
        janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
2559
        if(!ice_handle || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP))
2560
                return JANUS_ERROR_SESSION_NOT_FOUND;
2561
        janus_session *session = ice_handle->session;
2562
        if(!session || session->destroy)
2563
                return JANUS_ERROR_SESSION_NOT_FOUND;
2564
        /* Make sure this is JSON */
2565
        json_error_t error;
2566
        json_t *plugin_event = json_loads(message, 0, &error);
2567
        if(!plugin_event) {
2568
                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (JSON error: on line %d: %s)\n", ice_handle->handle_id, error.line, error.text);
2569
                return JANUS_ERROR_INVALID_JSON;
2570
        }
2571
        if(!json_is_object(plugin_event)) {
2572
                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (JSON error: not an object)\n", ice_handle->handle_id);
2573
                return JANUS_ERROR_INVALID_JSON_OBJECT;
2574
        }
2575
        /* Attach JSEP if possible? */
2576
        json_t *jsep = NULL;
2577
        if(sdp_type != NULL && sdp != NULL) {
2578
                jsep = janus_plugin_handle_sdp(plugin_session, plugin, sdp_type, sdp);
2579
                if(jsep == NULL) {
2580
                        if(ice_handle == NULL || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2581
                                        || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)) {
2582
                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (handle not available anymore or negotiation stopped)\n", ice_handle->handle_id);
2583
                                return JANUS_ERROR_HANDLE_NOT_FOUND;
2584
                        } else {
2585
                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (JSON error: problem with the SDP)\n", ice_handle->handle_id);
2586
                                return JANUS_ERROR_JSEP_INVALID_SDP;
2587
                        }
2588
                }
2589
        }
2590
        /* Prepare JSON event */
2591
        json_t *event = json_object();
2592
        json_object_set_new(event, "janus", json_string("event"));
2593
        json_object_set_new(event, "session_id", json_integer(session->session_id));
2594
        json_object_set_new(event, "sender", json_integer(ice_handle->handle_id));
2595
        if(transaction != NULL)
2596
                json_object_set_new(event, "transaction", json_string(transaction));
2597
        json_t *plugin_data = json_object();
2598
        json_object_set_new(plugin_data, "plugin", json_string(plugin->get_package()));
2599
        json_object_set_new(plugin_data, "data", plugin_event);
2600
        json_object_set_new(event, "plugindata", plugin_data);
2601
        if(jsep != NULL)
2602
                json_object_set_new(event, "jsep", jsep);
2603
        /* Send the event */
2604
        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Sending event to transport...\n", ice_handle->handle_id);
2605
        janus_session_notify_event(session->session_id, event);
2606

    
2607
        if(jsep != NULL && janus_events_is_enabled()) {
2608
                /* Notify event handlers as well */
2609
                janus_events_notify_handlers(JANUS_EVENT_TYPE_JSEP,
2610
                        session->session_id, ice_handle->handle_id, "local", sdp_type, sdp);
2611
        }
2612

    
2613
        return JANUS_OK;
2614
}
2615

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

    
2771
        if(!updating) {
2772
                if(offer) {
2773
                        /* We set the flag to wait for an answer before handling trickle candidates */
2774
                        janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
2775
                } else {
2776
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Done! Ready to setup remote candidates and send connectivity checks...\n", ice_handle->handle_id);
2777
                        if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
2778
                                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);
2779
                                if(audio) {
2780
                                        /* Get rid of video and data, if present */
2781
                                        if(ice_handle->streams && ice_handle->video_stream) {
2782
                                                ice_handle->audio_stream->video_ssrc = ice_handle->video_stream->video_ssrc;
2783
                                                ice_handle->audio_stream->video_ssrc_peer = ice_handle->video_stream->video_ssrc_peer;
2784
                                                ice_handle->audio_stream->video_ssrc_peer_rtx = ice_handle->video_stream->video_ssrc_peer_rtx;
2785
                                                nice_agent_attach_recv(ice_handle->agent, ice_handle->video_stream->stream_id, 1, g_main_loop_get_context (ice_handle->iceloop), NULL, NULL);
2786
                                                if(!janus_ice_is_rtcpmux_forced())
2787
                                                        nice_agent_attach_recv(ice_handle->agent, ice_handle->video_stream->stream_id, 2, g_main_loop_get_context (ice_handle->iceloop), NULL, NULL);
2788
                                                nice_agent_remove_stream(ice_handle->agent, ice_handle->video_stream->stream_id);
2789
                                                janus_ice_stream_free(ice_handle->streams, ice_handle->video_stream);
2790
                                        }
2791
                                        ice_handle->video_stream = NULL;
2792
                                        ice_handle->video_id = 0;
2793
                                        if(ice_handle->streams && ice_handle->data_stream) {
2794
                                                nice_agent_attach_recv(ice_handle->agent, ice_handle->data_stream->stream_id, 1, g_main_loop_get_context (ice_handle->iceloop), NULL, NULL);
2795
                                                nice_agent_remove_stream(ice_handle->agent, ice_handle->data_stream->stream_id);
2796
                                                janus_ice_stream_free(ice_handle->streams, ice_handle->data_stream);
2797
                                        }
2798
                                        ice_handle->data_stream = NULL;
2799
                                        ice_handle->data_id = 0;
2800
                                        if(!video) {
2801
                                                ice_handle->audio_stream->video_ssrc = 0;
2802
                                                ice_handle->audio_stream->video_ssrc_peer = 0;
2803
                                                g_free(ice_handle->audio_stream->video_rtcp_ctx);
2804
                                                ice_handle->audio_stream->video_rtcp_ctx = NULL;
2805
                                        }
2806
                                } else if(video) {
2807
                                        /* Get rid of data, if present */
2808
                                        if(ice_handle->streams && ice_handle->data_stream) {
2809
                                                nice_agent_attach_recv(ice_handle->agent, ice_handle->data_stream->stream_id, 1, g_main_loop_get_context (ice_handle->iceloop), NULL, NULL);
2810
                                                nice_agent_remove_stream(ice_handle->agent, ice_handle->data_stream->stream_id);
2811
                                                janus_ice_stream_free(ice_handle->streams, ice_handle->data_stream);
2812
                                        }
2813
                                        ice_handle->data_stream = NULL;
2814
                                        ice_handle->data_id = 0;
2815
                                }
2816
                        }
2817
                        if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX) && !janus_ice_is_rtcpmux_forced()) {
2818
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- rtcp-mux is supported by the browser, getting rid of RTCP components, if any...\n", ice_handle->handle_id);
2819
                                if(ice_handle->audio_stream && ice_handle->audio_stream->rtcp_component && ice_handle->audio_stream->components != NULL) {
2820
                                        nice_agent_attach_recv(ice_handle->agent, ice_handle->audio_id, 2, g_main_loop_get_context (ice_handle->iceloop), NULL, NULL);
2821
                                        /* Free the component */
2822
                                        janus_ice_component_free(ice_handle->audio_stream->components, ice_handle->audio_stream->rtcp_component);
2823
                                        ice_handle->audio_stream->rtcp_component = NULL;
2824
                                        /* Create a dummy candidate and enforce it as the one to use for this now unneeded component */
2825
                                        NiceCandidate *c = nice_candidate_new(NICE_CANDIDATE_TYPE_HOST);
2826
                                        c->component_id = 2;
2827
                                        c->stream_id = ice_handle->audio_stream->stream_id;
2828
#ifndef HAVE_LIBNICE_TCP
2829
                                        c->transport = NICE_CANDIDATE_TRANSPORT_UDP;
2830
#endif
2831
                                        strncpy(c->foundation, "1", NICE_CANDIDATE_MAX_FOUNDATION);
2832
                                        c->priority = 1;
2833
                                        nice_address_set_from_string(&c->addr, "127.0.0.1");
2834
                                        nice_address_set_port(&c->addr, janus_ice_get_rtcpmux_blackhole_port());
2835
                                        c->username = g_strdup(ice_handle->audio_stream->ruser);
2836
                                        c->password = g_strdup(ice_handle->audio_stream->rpass);
2837
                                        if(!nice_agent_set_selected_remote_candidate(ice_handle->agent, ice_handle->audio_stream->stream_id, 2, c)) {
2838
                                                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);
2839
                                                nice_candidate_free(c);
2840
                                        }
2841
                                }
2842
                                if(ice_handle->video_stream && ice_handle->video_stream->rtcp_component && ice_handle->video_stream->components != NULL) {
2843
                                        nice_agent_attach_recv(ice_handle->agent, ice_handle->video_id, 2, g_main_loop_get_context (ice_handle->iceloop), NULL, NULL);
2844
                                        /* Free the component */
2845
                                        janus_ice_component_free(ice_handle->video_stream->components, ice_handle->video_stream->rtcp_component);
2846
                                        ice_handle->video_stream->rtcp_component = NULL;
2847
                                        /* Create a dummy candidate and enforce it as the one to use for this now unneeded component */
2848
                                        NiceCandidate *c = nice_candidate_new(NICE_CANDIDATE_TYPE_HOST);
2849
                                        c->component_id = 2;
2850
                                        c->stream_id = ice_handle->video_stream->stream_id;
2851
#ifndef HAVE_LIBNICE_TCP
2852
                                        c->transport = NICE_CANDIDATE_TRANSPORT_UDP;
2853
#endif
2854
                                        strncpy(c->foundation, "1", NICE_CANDIDATE_MAX_FOUNDATION);
2855
                                        c->priority = 1;
2856
                                        nice_address_set_from_string(&c->addr, "127.0.0.1");
2857
                                        nice_address_set_port(&c->addr, janus_ice_get_rtcpmux_blackhole_port());
2858
                                        c->username = g_strdup(ice_handle->video_stream->ruser);
2859
                                        c->password = g_strdup(ice_handle->video_stream->rpass);
2860
                                        if(!nice_agent_set_selected_remote_candidate(ice_handle->agent, ice_handle->video_stream->stream_id, 2, c)) {
2861
                                                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);
2862
                                                nice_candidate_free(c);
2863
                                        }
2864
                                }
2865
                        }
2866
                        janus_mutex_lock(&ice_handle->mutex);
2867
                        /* We got our answer */
2868
                        janus_flags_clear(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
2869
                        /* Any pending trickles? */
2870
                        if(ice_handle->pending_trickles) {
2871
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Processing %d pending trickle candidates\n", ice_handle->handle_id, g_list_length(ice_handle->pending_trickles));
2872
                                GList *temp = NULL;
2873
                                while(ice_handle->pending_trickles) {
2874
                                        temp = g_list_first(ice_handle->pending_trickles);
2875
                                        ice_handle->pending_trickles = g_list_remove_link(ice_handle->pending_trickles, temp);
2876
                                        janus_ice_trickle *trickle = (janus_ice_trickle *)temp->data;
2877
                                        g_list_free(temp);
2878
                                        if(trickle == NULL)
2879
                                                continue;
2880
                                        if((janus_get_monotonic_time() - trickle->received) > 15*G_USEC_PER_SEC) {
2881
                                                /* FIXME Candidate is too old, discard it */
2882
                                                janus_ice_trickle_destroy(trickle);
2883
                                                /* FIXME We should report that */
2884
                                                continue;
2885
                                        }
2886
                                        json_t *candidate = trickle->candidate;
2887
                                        if(candidate == NULL) {
2888
                                                janus_ice_trickle_destroy(trickle);
2889
                                                continue;
2890
                                        }
2891
                                        if(json_is_object(candidate)) {
2892
                                                /* We got a single candidate */
2893
                                                int error = 0;
2894
                                                const char *error_string = NULL;
2895
                                                if((error = janus_ice_trickle_parse(ice_handle, candidate, &error_string)) != 0) {
2896
                                                        /* FIXME We should report the error parsing the trickle candidate */
2897
                                                }
2898
                                        } else if(json_is_array(candidate)) {
2899
                                                /* We got multiple candidates in an array */
2900
                                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Got multiple candidates (%zu)\n", ice_handle->handle_id, json_array_size(candidate));
2901
                                                if(json_array_size(candidate) > 0) {
2902
                                                        /* Handle remote candidates */
2903
                                                        size_t i = 0;
2904
                                                        for(i=0; i<json_array_size(candidate); i++) {
2905
                                                                json_t *c = json_array_get(candidate, i);
2906
                                                                /* FIXME We don't care if any trickle fails to parse */
2907
                                                                janus_ice_trickle_parse(ice_handle, c, NULL);
2908
                                                        }
2909
                                                }
2910
                                        }
2911
                                        /* Done, free candidate */
2912
                                        janus_ice_trickle_destroy(trickle);
2913
                                }
2914
                        }
2915
                        /* This was an answer, check if it's time to start ICE */
2916
                        if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE) &&
2917
                                        !janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALL_TRICKLES)) {
2918
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- ICE Trickling is supported by the browser, waiting for remote candidates...\n", ice_handle->handle_id);
2919
                                janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_START);
2920
                        } else {
2921
                                JANUS_LOG(LOG_VERB, "[%"SCNu64"] Done! Sending connectivity checks...\n", ice_handle->handle_id);
2922
                                if(ice_handle->audio_id > 0) {
2923
                                        janus_ice_setup_remote_candidates(ice_handle, ice_handle->audio_id, 1);
2924
                                        if(!janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX))        /* http://tools.ietf.org/html/rfc5761#section-5.1.3 */
2925
                                                janus_ice_setup_remote_candidates(ice_handle, ice_handle->audio_id, 2);
2926
                                }
2927
                                if(ice_handle->video_id > 0) {
2928
                                        janus_ice_setup_remote_candidates(ice_handle, ice_handle->video_id, 1);
2929
                                        if(!janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX))        /* http://tools.ietf.org/html/rfc5761#section-5.1.3 */
2930
                                                janus_ice_setup_remote_candidates(ice_handle, ice_handle->video_id, 2);
2931
                                }
2932
                                if(ice_handle->data_id > 0) {
2933
                                        janus_ice_setup_remote_candidates(ice_handle, ice_handle->data_id, 1);
2934
                                }
2935
                        }
2936
                        janus_mutex_unlock(&ice_handle->mutex);
2937
                }
2938
        }
2939
        
2940
        /* Prepare JSON event */
2941
        json_t *jsep = json_object();
2942
        json_object_set_new(jsep, "type", json_string(sdp_type));
2943
        json_object_set_new(jsep, "sdp", json_string(sdp_merged));
2944
        g_free(sdp_stripped);
2945
        //~ g_free(sdp_merged);
2946
        ice_handle->local_sdp = sdp_merged;
2947
        return jsep;
2948
}
2949

    
2950
void janus_plugin_relay_rtp(janus_plugin_session *plugin_session, int video, char *buf, int len) {
2951
        if((plugin_session < (janus_plugin_session *)0x1000) || plugin_session->stopped || buf == NULL || len < 1)
2952
                return;
2953
        janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
2954
        if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2955
                        || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
2956
                return;
2957
        janus_ice_relay_rtp(handle, video, buf, len);
2958
}
2959

    
2960
void janus_plugin_relay_rtcp(janus_plugin_session *plugin_session, int video, char *buf, int len) {
2961
        if((plugin_session < (janus_plugin_session *)0x1000) || plugin_session->stopped || buf == NULL || len < 1)
2962
                return;
2963
        janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
2964
        if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2965
                        || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
2966
                return;
2967
        janus_ice_relay_rtcp(handle, video, buf, len);
2968
}
2969

    
2970
void janus_plugin_relay_data(janus_plugin_session *plugin_session, char *buf, int len) {
2971
        if((plugin_session < (janus_plugin_session *)0x1000) || plugin_session->stopped || buf == NULL || len < 1)
2972
                return;
2973
        janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
2974
        if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2975
                        || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
2976
                return;
2977
#ifdef HAVE_SCTP
2978
        janus_ice_relay_data(handle, buf, len);
2979
#else
2980
        JANUS_LOG(LOG_WARN, "Asked to relay data, but Data Channels support has not been compiled...\n");
2981
#endif
2982
}
2983

    
2984
void janus_plugin_close_pc(janus_plugin_session *plugin_session) {
2985
        /* A plugin asked to get rid of a PeerConnection */
2986
        if((plugin_session < (janus_plugin_session *)0x1000) || !janus_plugin_session_is_alive(plugin_session) || plugin_session->stopped)
2987
                return;
2988
        janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
2989
        if(!ice_handle)
2990
                return;
2991
        if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2992
                        || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
2993
                return;
2994
        janus_session *session = (janus_session *)ice_handle->session;
2995
        if(!session)
2996
                return;
2997
                
2998
        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Plugin asked to hangup PeerConnection: sending alert\n", ice_handle->handle_id);
2999
        /* Send an alert on all the DTLS connections */
3000
        janus_ice_webrtc_hangup(ice_handle);
3001
}
3002

    
3003
void janus_plugin_end_session(janus_plugin_session *plugin_session) {
3004
        /* A plugin asked to get rid of a handle */
3005
        if((plugin_session < (janus_plugin_session *)0x1000) || !janus_plugin_session_is_alive(plugin_session) || plugin_session->stopped)
3006
                return;
3007
        janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
3008
        if(!ice_handle)
3009
                return;
3010
        janus_session *session = (janus_session *)ice_handle->session;
3011
        if(!session)
3012
                return;
3013
        /* Destroy the handle */
3014
        janus_ice_handle_destroy(session, ice_handle->handle_id);
3015
        janus_mutex_lock(&session->mutex);
3016
        g_hash_table_remove(session->ice_handles, GUINT_TO_POINTER(ice_handle->handle_id));
3017
        janus_mutex_unlock(&session->mutex);
3018
}
3019

    
3020
void janus_plugin_notify_event(janus_plugin_session *plugin_session, json_t *event) {
3021
        /* A plugin asked to notify an event to the handlers */
3022
        if(!event || !json_is_object(event))
3023
                return;
3024
        if((plugin_session < (janus_plugin_session *)0x1000) || !janus_plugin_session_is_alive(plugin_session) || plugin_session->stopped) {
3025
                json_decref(event);
3026
                return;
3027
        }
3028
        janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
3029
        if(!ice_handle) {
3030
                json_decref(event);
3031
                return;
3032
        }
3033
        janus_session *session = (janus_session *)ice_handle->session;
3034
        if(!session) {
3035
                json_decref(event);
3036
                return;
3037
        }
3038
        janus_plugin *plugin_t = (janus_plugin *)ice_handle->app;
3039
        /* Notify event handlers */
3040
        if(janus_events_is_enabled()) {
3041
                janus_events_notify_handlers(JANUS_EVENT_TYPE_PLUGIN,
3042
                        session->session_id, ice_handle->handle_id, plugin_t->get_package(), event);
3043
        } else {
3044
                json_decref(event);
3045
        }
3046
}
3047

    
3048

    
3049
static void janus_detect_local_ip(gchar *buf, size_t buflen) {
3050
        JANUS_LOG(LOG_VERB, "Autodetecting local IP...\n");
3051
        struct sockaddr_in addr;
3052
        socklen_t len;
3053
        int fd = socket(AF_INET, SOCK_DGRAM, 0);
3054
        if (fd == -1)
3055
                goto error;
3056
        addr.sin_family = AF_INET;
3057
        addr.sin_port = htons(1);
3058
        inet_pton(AF_INET, "1.2.3.4", &addr.sin_addr.s_addr);
3059
        if (connect(fd, (const struct sockaddr*) &addr, sizeof(addr)) < 0)
3060
                goto error;
3061
        len = sizeof(addr);
3062
        if (getsockname(fd, (struct sockaddr*) &addr, &len) < 0)
3063
                goto error;
3064
        if (getnameinfo((const struct sockaddr*) &addr, sizeof(addr),
3065
                        buf, buflen,
3066
                        NULL, 0, NI_NUMERICHOST) != 0)
3067
                goto error;
3068
        close(fd);
3069
        return;
3070
error:
3071
        if (fd != -1)
3072
                close(fd);
3073
        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");
3074
        g_strlcpy(buf, "127.0.0.1", buflen);
3075
}
3076

    
3077

    
3078
/* Main */
3079
gint main(int argc, char *argv[])
3080
{
3081
        /* Core dumps may be disallowed by parent of this process; change that */
3082
        struct rlimit core_limits;
3083
        core_limits.rlim_cur = core_limits.rlim_max = RLIM_INFINITY;
3084
        setrlimit(RLIMIT_CORE, &core_limits);
3085

    
3086
        struct gengetopt_args_info args_info;
3087
        /* Let's call our cmdline parser */
3088
        if(cmdline_parser(argc, argv, &args_info) != 0)
3089
                exit(1);
3090
        
3091
        /* Any configuration to open? */
3092
        if(args_info.config_given) {
3093
                config_file = g_strdup(args_info.config_arg);
3094
        }
3095
        if(args_info.configs_folder_given) {
3096
                configs_folder = g_strdup(args_info.configs_folder_arg);
3097
        } else {
3098
                configs_folder = g_strdup (CONFDIR);
3099
        }
3100
        if(config_file == NULL) {
3101
                char file[255];
3102
                g_snprintf(file, 255, "%s/janus.cfg", configs_folder);
3103
                config_file = g_strdup(file);
3104
        }
3105
        if((config = janus_config_parse(config_file)) == NULL) {
3106
                if(args_info.config_given) {
3107
                        /* We only give up if the configuration file was explicitly provided */
3108
                        g_print("Error reading configuration from %s\n", config_file);
3109
                        exit(1);
3110
                }
3111
                g_print("Error reading/parsing the configuration file, going on with the defaults and the command line arguments\n");
3112
                config = janus_config_create("janus.cfg");
3113
                if(config == NULL) {
3114
                        /* If we can't even create an empty configuration, something's definitely wrong */
3115
                        exit(1);
3116
                }
3117
        }
3118

    
3119
        /* Check if we need to log to console and/or file */
3120
        gboolean use_stdout = TRUE;
3121
        if(args_info.disable_stdout_given) {
3122
                use_stdout = FALSE;
3123
                janus_config_add_item(config, "general", "log_to_stdout", "no");
3124
        } else {
3125
                /* Check if the configuration file is saying anything about this */
3126
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "log_to_stdout");
3127
                if(item && item->value && !janus_is_true(item->value))
3128
                        use_stdout = FALSE;
3129
        }
3130
        const char *logfile = NULL;
3131
        if(args_info.log_file_given) {
3132
                logfile = args_info.log_file_arg;
3133
                janus_config_add_item(config, "general", "log_to_file", "no");
3134
        } else {
3135
                /* Check if the configuration file is saying anything about this */
3136
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "log_to_file");
3137
                if(item && item->value)
3138
                        logfile = item->value;
3139
        }
3140

    
3141
        /* Check if we're going to daemonize Janus */
3142
        if(args_info.daemon_given) {
3143
                daemonize = TRUE;
3144
                janus_config_add_item(config, "general", "daemonize", "yes");
3145
        } else {
3146
                /* Check if the configuration file is saying anything about this */
3147
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "daemonize");
3148
                if(item && item->value && janus_is_true(item->value))
3149
                        daemonize = TRUE;
3150
        }
3151
        /* If we're going to daemonize, make sure logging to stdout is disabled and a log file has been specified */
3152
        if(daemonize && use_stdout) {
3153
                use_stdout = FALSE;
3154
        }
3155
        if(daemonize && logfile == NULL) {
3156
                g_print("Running Janus as a daemon but no log file provided, giving up...\n");
3157
                exit(1);
3158
        }
3159
        /* Daemonize now, if we need to */
3160
        if(daemonize) {
3161
                g_print("Running Janus as a daemon\n");
3162

    
3163
                /* Create a pipe for parent<->child communication during the startup phase */
3164
                if(pipe(pipefd) == -1) {
3165
                        g_print("pipe error!\n");
3166
                        exit(1);
3167
                }
3168

    
3169
                /* Fork off the parent process */
3170
                pid_t pid = fork();
3171
                if(pid < 0) {
3172
                        g_print("Fork error!\n");
3173
                        exit(1);
3174
                }
3175
                if(pid > 0) {
3176
                        /* Ok, we're the parent: let's wait for the child to tell us everything started fine */
3177
                        close(pipefd[1]);
3178
                        int code = -1;
3179
                        struct pollfd pollfds;
3180

    
3181
                        while(code < 0) {
3182
                                pollfds.fd = pipefd[0];
3183
                                pollfds.events = POLLIN;
3184
                                int res = poll(&pollfds, 1, -1);
3185
                                if(res < 0)
3186
                                        break;
3187
                                if(res == 0)
3188
                                        continue;
3189
                                if(pollfds.revents & POLLERR || pollfds.revents & POLLHUP)
3190
                                        break;
3191
                                if(pollfds.revents & POLLIN) {
3192
                                        read(pipefd[0], &code, sizeof(int));
3193
                                        break;
3194
                                }
3195
                        }
3196
                        if(code < 0)
3197
                                code = 1;
3198

    
3199
                        /* Leave the parent and return the exit code we received from the child */
3200
                        if(code)
3201
                                g_print("Error launching Janus (error code %d), check the logs for more details\n", code);
3202
                        exit(code);
3203
                }
3204
                /* Change the file mode mask */
3205
                umask(0);
3206

    
3207
                /* Create a new SID for the child process */
3208
                pid_t sid = setsid();
3209
                if(sid < 0) {
3210
                        g_print("Error setting SID!\n");
3211
                        exit(1);
3212
                }
3213
                /* Change the current working directory */
3214
                if((chdir("/")) < 0) {
3215
                        g_print("Error changing the current working directory!\n");
3216
                        exit(1);
3217
                }
3218
                /* We close stdin/stdout/stderr when initializing the logger */
3219
        }
3220

    
3221
        /* Initialize logger */
3222
        if(janus_log_init(daemonize, use_stdout, logfile) < 0)
3223
                exit(1);
3224

    
3225
        JANUS_PRINT("---------------------------------------------------\n");
3226
        JANUS_PRINT("  Starting Meetecho Janus (WebRTC Gateway) v%s\n", JANUS_VERSION_STRING);
3227
        JANUS_PRINT("---------------------------------------------------\n\n");
3228

    
3229
        /* Handle SIGINT (CTRL-C), SIGTERM (from service managers) */
3230
        signal(SIGINT, janus_handle_signal);
3231
        signal(SIGTERM, janus_handle_signal);
3232
        atexit(janus_termination_handler);
3233

    
3234
        /* Setup Glib */
3235
#if !GLIB_CHECK_VERSION(2, 36, 0)
3236
        g_type_init();
3237
#endif
3238

    
3239
        /* Logging level: default is info and no timestamps */
3240
        janus_log_level = LOG_INFO;
3241
        janus_log_timestamps = FALSE;
3242
        janus_log_colors = TRUE;
3243
        if(args_info.debug_level_given) {
3244
                if(args_info.debug_level_arg < LOG_NONE)
3245
                        args_info.debug_level_arg = 0;
3246
                else if(args_info.debug_level_arg > LOG_MAX)
3247
                        args_info.debug_level_arg = LOG_MAX;
3248
                janus_log_level = args_info.debug_level_arg;
3249
        }
3250

    
3251
        /* Any PID we need to create? */
3252
        const char *pidfile = NULL;
3253
        if(args_info.pid_file_given) {
3254
                pidfile = args_info.pid_file_arg;
3255
                janus_config_add_item(config, "general", "pid_file", pidfile);
3256
        } else {
3257
                /* Check if the configuration file is saying anything about this */
3258
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "pid_file");
3259
                if(item && item->value)
3260
                        pidfile = item->value;
3261
        }
3262
        if(janus_pidfile_create(pidfile) < 0)
3263
                exit(1);
3264

    
3265
        /* Proceed with the rest of the configuration */
3266
        janus_config_print(config);
3267
        if(args_info.debug_level_given) {
3268
                char debug[5];
3269
                g_snprintf(debug, 5, "%d", args_info.debug_level_arg);
3270
                janus_config_add_item(config, "general", "debug_level", debug);
3271
        } else {
3272
                /* No command line directive on logging, try the configuration file */
3273
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "debug_level");
3274
                if(item && item->value) {
3275
                        int temp_level = atoi(item->value);
3276
                        if(temp_level == 0 && strcmp(item->value, "0")) {
3277
                                JANUS_PRINT("Invalid debug level %s (configuration), using default (info=4)\n", item->value);
3278
                        } else {
3279
                                janus_log_level = temp_level;
3280
                                if(janus_log_level < LOG_NONE)
3281
                                        janus_log_level = 0;
3282
                                else if(janus_log_level > LOG_MAX)
3283
                                        janus_log_level = LOG_MAX;
3284
                        }
3285
                }
3286
        }
3287
        /* Any command line argument that should overwrite the configuration? */
3288
        JANUS_PRINT("Checking command line arguments...\n");
3289
        if(args_info.debug_timestamps_given) {
3290
                janus_config_add_item(config, "general", "debug_timestamps", "yes");
3291
        }
3292
        if(args_info.disable_colors_given) {
3293
                janus_config_add_item(config, "general", "debug_colors", "no");
3294
        }
3295
        if(args_info.server_name_given) {
3296
                janus_config_add_item(config, "general", "server_name", args_info.server_name_arg);
3297
        }
3298
         if(args_info.interface_given) {
3299
                janus_config_add_item(config, "general", "interface", args_info.interface_arg);
3300
        }
3301
        if(args_info.configs_folder_given) {
3302
                janus_config_add_item(config, "general", "configs_folder", args_info.configs_folder_arg);
3303
        }
3304
        if(args_info.plugins_folder_given) {
3305
                janus_config_add_item(config, "general", "plugins_folder", args_info.plugins_folder_arg);
3306
        }
3307
        if(args_info.apisecret_given) {
3308
                janus_config_add_item(config, "general", "api_secret", args_info.apisecret_arg);
3309
        }
3310
        if(args_info.token_auth_given) {
3311
                janus_config_add_item(config, "general", "token_auth", "yes");
3312
        }
3313
        if(args_info.cert_pem_given) {
3314
                janus_config_add_item(config, "certificates", "cert_pem", args_info.cert_pem_arg);
3315
        }
3316
        if(args_info.cert_key_given) {
3317
                janus_config_add_item(config, "certificates", "cert_key", args_info.cert_key_arg);
3318
        }
3319
        if(args_info.stun_server_given) {
3320
                /* Split in server and port (if port missing, use 3478 as default) */
3321
                char *stunport = strrchr(args_info.stun_server_arg, ':');
3322
                if(stunport != NULL) {
3323
                        *stunport = '\0';
3324
                        stunport++;
3325
                        janus_config_add_item(config, "nat", "stun_server", args_info.stun_server_arg);
3326
                        janus_config_add_item(config, "nat", "stun_port", stunport);
3327
                } else {
3328
                        janus_config_add_item(config, "nat", "stun_server", args_info.stun_server_arg);
3329
                        janus_config_add_item(config, "nat", "stun_port", "3478");
3330
                }
3331
        }
3332
        if(args_info.nat_1_1_given) {
3333
                janus_config_add_item(config, "nat", "nat_1_1_mapping", args_info.nat_1_1_arg);
3334
        }
3335
        if(args_info.ice_enforce_list_given) {
3336
                janus_config_add_item(config, "nat", "ice_enforce_list", args_info.ice_enforce_list_arg);
3337
        }
3338
        if(args_info.ice_ignore_list_given) {
3339
                janus_config_add_item(config, "nat", "ice_ignore_list", args_info.ice_ignore_list_arg);
3340
        }
3341
        if(args_info.libnice_debug_given) {
3342
                janus_config_add_item(config, "nat", "nice_debug", "true");
3343
        }
3344
        if(args_info.ice_lite_given) {
3345
                janus_config_add_item(config, "nat", "ice_lite", "true");
3346
        }
3347
        if(args_info.ice_tcp_given) {
3348
                janus_config_add_item(config, "nat", "ice_tcp", "true");
3349
        }
3350
        if(args_info.ipv6_candidates_given) {
3351
                janus_config_add_item(config, "media", "ipv6", "true");
3352
        }
3353
        if(args_info.force_bundle_given) {
3354
                janus_config_add_item(config, "media", "force-bundle", "true");
3355
        }
3356
        if(args_info.force_rtcp_mux_given) {
3357
                janus_config_add_item(config, "media", "force-rtcp-mux", "true");
3358
        }
3359
        if(args_info.max_nack_queue_given) {
3360
                char mnq[20];
3361
                g_snprintf(mnq, 20, "%d", args_info.max_nack_queue_arg);
3362
                janus_config_add_item(config, "media", "max_nack_queue", mnq);
3363
        }
3364
        if(args_info.rtp_port_range_given) {
3365
                janus_config_add_item(config, "media", "rtp_port_range", args_info.rtp_port_range_arg);
3366
        }
3367
        janus_config_print(config);
3368

    
3369
        /* Logging/debugging */
3370
        JANUS_PRINT("Debug/log level is %d\n", janus_log_level);
3371
        janus_config_item *item = janus_config_get_item_drilldown(config, "general", "debug_timestamps");
3372
        if(item && item->value)
3373
                janus_log_timestamps = janus_is_true(item->value);
3374
        JANUS_PRINT("Debug/log timestamps are %s\n", janus_log_timestamps ? "enabled" : "disabled");
3375
        item = janus_config_get_item_drilldown(config, "general", "debug_colors");
3376
        if(item && item->value)
3377
                janus_log_colors = janus_is_true(item->value);
3378
        JANUS_PRINT("Debug/log colors are %s\n", janus_log_colors ? "enabled" : "disabled");
3379

    
3380
        /* Any IP/interface to enforce/ignore? */
3381
        item = janus_config_get_item_drilldown(config, "nat", "ice_enforce_list");
3382
        if(item && item->value) {
3383
                gchar **list = g_strsplit(item->value, ",", -1);
3384
                gchar *index = list[0];
3385
                if(index != NULL) {
3386
                        int i=0;
3387
                        while(index != NULL) {
3388
                                if(strlen(index) > 0) {
3389
                                        JANUS_LOG(LOG_INFO, "Adding '%s' to the ICE enforce list...\n", index);
3390
                                        janus_ice_enforce_interface(g_strdup(index));
3391
                                }
3392
                                i++;
3393
                                index = list[i];
3394
                        }
3395
                }
3396
                g_strfreev(list);
3397
                list = NULL;
3398
        }
3399
        item = janus_config_get_item_drilldown(config, "nat", "ice_ignore_list");
3400
        if(item && item->value) {
3401
                gchar **list = g_strsplit(item->value, ",", -1);
3402
                gchar *index = list[0];
3403
                if(index != NULL) {
3404
                        int i=0;
3405
                        while(index != NULL) {
3406
                                if(strlen(index) > 0) {
3407
                                        JANUS_LOG(LOG_INFO, "Adding '%s' to the ICE ignore list...\n", index);
3408
                                        janus_ice_ignore_interface(g_strdup(index));
3409
                                }
3410
                                i++;
3411
                                index = list[i];
3412
                        }
3413
                }
3414
                g_strfreev(list);
3415
                list = NULL;
3416
        }
3417
        /* What is the local IP? */
3418
        JANUS_LOG(LOG_VERB, "Selecting local IP address...\n");
3419
        gboolean local_ip_set = FALSE;
3420
        item = janus_config_get_item_drilldown(config, "general", "interface");
3421
        if(item && item->value) {
3422
                JANUS_LOG(LOG_VERB, "  -- Will try to use %s\n", item->value);
3423
                int family;
3424
                if (!janus_is_ip_valid(item->value, &family)) {
3425
                        JANUS_LOG(LOG_WARN, "Invalid local IP specified: %s, guessing the default...\n", item->value);
3426
                } else {
3427
                        /* Verify that we can actually bind to that address */
3428
                        int fd = socket(family, SOCK_DGRAM, 0);
3429
                        if (fd == -1) {
3430
                                JANUS_LOG(LOG_WARN, "Error creating test socket, falling back to detecting IP address...\n");
3431
                        } else {
3432
                                int r;
3433
                                struct sockaddr_storage ss;
3434
                                socklen_t addrlen;
3435
                                memset(&ss, 0, sizeof(ss));
3436
                                if (family == AF_INET) {
3437
                                        struct sockaddr_in *addr4 = (struct sockaddr_in*)&ss;
3438
                                        addr4->sin_family = AF_INET;
3439
                                        addr4->sin_port = 0;
3440
                                        inet_pton(AF_INET, item->value, &(addr4->sin_addr.s_addr));
3441
                                        addrlen = sizeof(struct sockaddr_in);
3442
                                } else {
3443
                                        struct sockaddr_in6 *addr6 = (struct sockaddr_in6*)&ss;
3444
                                        addr6->sin6_family = AF_INET6;
3445
                                        addr6->sin6_port = 0;
3446
                                        inet_pton(AF_INET6, item->value, &(addr6->sin6_addr.s6_addr));
3447
                                        addrlen = sizeof(struct sockaddr_in6);
3448
                                }
3449
                                r = bind(fd, (const struct sockaddr*)&ss, addrlen);
3450
                                close(fd);
3451
                                if (r < 0) {
3452
                                        JANUS_LOG(LOG_WARN, "Error setting local IP address to %s, falling back to detecting IP address...\n", item->value);
3453
                                } else {
3454
                                        g_strlcpy(local_ip, item->value, sizeof(local_ip));
3455
                                        local_ip_set = TRUE;
3456
                                }
3457
                        }
3458
                }
3459
        }
3460
        if (!local_ip_set)
3461
                janus_detect_local_ip(local_ip, sizeof(local_ip));
3462
        JANUS_LOG(LOG_INFO, "Using %s as local IP...\n", local_ip);
3463

    
3464
        /* Was a custom instance name provided? */
3465
        item = janus_config_get_item_drilldown(config, "general", "server_name");
3466
        if(item && item->value) {
3467
                server_name = g_strdup(item->value);
3468
        }
3469

    
3470
        /* Is there any API secret to consider? */
3471
        api_secret = NULL;
3472
        item = janus_config_get_item_drilldown(config, "general", "api_secret");
3473
        if(item && item->value) {
3474
                api_secret = g_strdup(item->value);
3475
        }
3476
        /* Is there any API secret to consider? */
3477
        admin_api_secret = NULL;
3478
        item = janus_config_get_item_drilldown(config, "general", "admin_secret");
3479
        if(item && item->value) {
3480
                admin_api_secret = g_strdup(item->value);
3481
        }
3482
        /* Also check if the token based authentication mechanism needs to be enabled */
3483
        item = janus_config_get_item_drilldown(config, "general", "token_auth");
3484
        janus_auth_init(item && item->value && janus_is_true(item->value));
3485

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

    
3634
        /* Setup OpenSSL stuff */
3635
        item = janus_config_get_item_drilldown(config, "certificates", "cert_pem");
3636
        if(!item || !item->value) {
3637
                JANUS_LOG(LOG_FATAL, "Missing certificate/key path, use the command line or the configuration to provide one\n");
3638
                exit(1);
3639
        }
3640
        server_pem = (char *)item->value;
3641
        item = janus_config_get_item_drilldown(config, "certificates", "cert_key");
3642
        if(!item || !item->value) {
3643
                JANUS_LOG(LOG_FATAL, "Missing certificate/key path, use the command line or the configuration to provide one\n");
3644
                exit(1);
3645
        }
3646
        server_key = (char *)item->value;
3647
        JANUS_LOG(LOG_VERB, "Using certificates:\n\t%s\n\t%s\n", server_pem, server_key);
3648
        SSL_library_init();
3649
        SSL_load_error_strings();
3650
        OpenSSL_add_all_algorithms();
3651
        /* ... and DTLS-SRTP in particular */
3652
        if(janus_dtls_srtp_init(server_pem, server_key) < 0) {
3653
                exit(1);
3654
        }
3655
        /* Check if there's any custom value for the starting MTU to use in the BIO filter */
3656
        item = janus_config_get_item_drilldown(config, "media", "dtls_mtu");
3657
        if(item && item->value)
3658
                janus_dtls_bio_filter_set_mtu(atoi(item->value));
3659

    
3660
#ifdef HAVE_SCTP
3661
        /* Initialize SCTP for DataChannels */
3662
        if(janus_sctp_init() < 0) {
3663
                exit(1);
3664
        }
3665
#else
3666
        JANUS_LOG(LOG_WARN, "Data Channels support not compiled\n");
3667
#endif
3668

    
3669
        /* Initialize Sofia-SDP */
3670
        if(janus_sdp_init() < 0) {
3671
                exit(1);
3672
        }
3673

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

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

    
3923
        /* Create a thread pool to handle incoming requests, no matter what the transport */
3924
        GError *error = NULL;
3925
        tasks = g_thread_pool_new(janus_transport_task, NULL, -1, FALSE, &error);
3926
        if(error != NULL) {
3927
                /* Something went wrong... */
3928
                JANUS_LOG(LOG_FATAL, "Got error %d (%s) trying to launch the request pool task thread...\n", error->code, error->message ? error->message : "??");
3929
                exit(1);
3930
        }
3931

    
3932
        /* Load transports */
3933
        gboolean janus_api_enabled = FALSE, admin_api_enabled = FALSE;
3934
        path = TRANSPORTDIR;
3935
        item = janus_config_get_item_drilldown(config, "general", "transports_folder");
3936
        if(item && item->value)
3937
                path = (char *)item->value;
3938
        JANUS_LOG(LOG_INFO, "Transport plugins folder: %s\n", path);
3939
        dir = opendir(path);
3940
        if(!dir) {
3941
                JANUS_LOG(LOG_FATAL, "\tCouldn't access transport plugins folder...\n");
3942
                exit(1);
3943
        }
3944
        /* Any transport to ignore? */
3945
        gchar **disabled_transports = NULL;
3946
        item = janus_config_get_item_drilldown(config, "tra