Statistics
| Branch: | Revision:

janus-gateway / janus.c @ 0351dafc

History | View | Annotate | Download (165 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 "utils.h"
38

    
39

    
40
#define JANUS_NAME                                "Janus WebRTC Gateway"
41
#define JANUS_AUTHOR                        "Meetecho s.r.l."
42
#define JANUS_VERSION                        12
43
#define JANUS_VERSION_STRING        "0.1.2"
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 *plugins = NULL;
61
static GHashTable *plugins_so = NULL;
62

    
63

    
64
/* Daemonization */
65
static gboolean daemonize = FALSE;
66
static int pipefd[2];
67

    
68

    
69
/* API secrets */
70
static char *api_secret = NULL, *admin_api_secret = NULL;
71

    
72
/* JSON parameters */
73
static int janus_process_error_string(janus_request *request, uint64_t session_id, const char *transaction, gint error, gchar *error_string);
74

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

    
122
/* Admin/Monitor helpers */
123
json_t *janus_admin_stream_summary(janus_ice_stream *stream);
124
json_t *janus_admin_component_summary(janus_ice_component *component);
125

    
126

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

    
148

    
149
/* Public instance name */
150
static gchar *server_name = NULL;
151

    
152

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

    
235
        return info;
236
}
237

    
238

    
239
/* Logging */
240
int janus_log_level = LOG_INFO;
241
gboolean janus_log_timestamps = FALSE;
242
gboolean janus_log_colors = FALSE;
243
int lock_debug = 0;
244

    
245

    
246
/*! \brief Signal handler (just used to intercept CTRL+C and SIGTERM) */
247
static void janus_handle_signal(int signum) {
248
        switch(g_atomic_int_get(&stop)) {
249
                case 0:
250
                        JANUS_PRINT("Stopping gateway, please wait...\n");
251
                        break;
252
                case 1:
253
                        JANUS_PRINT("In a hurry? I'm trying to free resources cleanly, here!\n");
254
                        break;
255
                default:
256
                        JANUS_PRINT("Ok, leaving immediately...\n");
257
                        break;
258
        }
259
        g_atomic_int_inc(&stop);
260
        if(g_atomic_int_get(&stop) > 2)
261
                exit(1);
262
}
263

    
264
/*! \brief Termination handler (atexit) */
265
static void janus_termination_handler(void) {
266
        /* Free the instance name, if provided */
267
        g_free(server_name);
268
        /* Remove the PID file if we created it */
269
        janus_pidfile_remove();
270
        /* Close the logger */
271
        janus_log_destroy();
272
        /* If we're daemonizing, we send an error code to the parent */
273
        if(daemonize) {
274
                int code = 1;
275
                ssize_t res = 0;
276
                do {
277
                        res = write(pipefd[1], &code, sizeof(int));
278
                } while(res == -1 && errno == EINTR);
279
        }
280
}
281

    
282

    
283
/** @name Transport plugin callback interface
284
 * These are the callbacks implemented by the gateway core, as part of
285
 * the janus_transport_callbacks interface. Everything the transport
286
 * plugins send the gateway is handled here.
287
 */
288
///@{
289
void janus_transport_incoming_request(janus_transport *plugin, void *transport, void *request_id, gboolean admin, json_t *message, json_error_t *error);
290
void janus_transport_gone(janus_transport *plugin, void *transport);
291
gboolean janus_transport_is_api_secret_needed(janus_transport *plugin);
292
gboolean janus_transport_is_api_secret_valid(janus_transport *plugin, const char *apisecret);
293
gboolean janus_transport_is_auth_token_needed(janus_transport *plugin);
294
gboolean janus_transport_is_auth_token_valid(janus_transport *plugin, const char *token);
295

    
296
static janus_transport_callbacks janus_handler_transport =
297
        {
298
                .incoming_request = janus_transport_incoming_request,
299
                .transport_gone = janus_transport_gone,
300
                .is_api_secret_needed = janus_transport_is_api_secret_needed,
301
                .is_api_secret_valid = janus_transport_is_api_secret_valid,
302
                .is_auth_token_needed = janus_transport_is_auth_token_needed,
303
                .is_auth_token_valid = janus_transport_is_auth_token_valid,
304
        };
305
GThreadPool *tasks = NULL;
306
void janus_transport_task(gpointer data, gpointer user_data);
307
///@}
308

    
309

    
310
/** @name Plugin callback interface
311
 * These are the callbacks implemented by the gateway core, as part of
312
 * the janus_callbacks interface. Everything the plugins send the
313
 * gateway is handled here.
314
 */
315
///@{
316
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);
317
json_t *janus_plugin_handle_sdp(janus_plugin_session *plugin_session, janus_plugin *plugin, const char *sdp_type, const char *sdp);
318
void janus_plugin_relay_rtp(janus_plugin_session *plugin_session, int video, char *buf, int len);
319
void janus_plugin_relay_rtcp(janus_plugin_session *plugin_session, int video, char *buf, int len);
320
void janus_plugin_relay_data(janus_plugin_session *plugin_session, char *buf, int len);
321
void janus_plugin_close_pc(janus_plugin_session *plugin_session);
322
void janus_plugin_end_session(janus_plugin_session *plugin_session);
323
static janus_callbacks janus_handler_plugin =
324
        {
325
                .push_event = janus_plugin_push_event,
326
                .relay_rtp = janus_plugin_relay_rtp,
327
                .relay_rtcp = janus_plugin_relay_rtcp,
328
                .relay_data = janus_plugin_relay_data,
329
                .close_pc = janus_plugin_close_pc,
330
                .end_session = janus_plugin_end_session,
331
        };
332
///@}
333

    
334

    
335
/* Gateway Sessions */
336
static janus_mutex sessions_mutex;
337
static GHashTable *sessions = NULL, *old_sessions = NULL;
338
static GMainContext *sessions_watchdog_context = NULL;
339

    
340

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

    
343
static gboolean janus_cleanup_session(gpointer user_data) {
344
        janus_session *session = (janus_session *) user_data;
345

    
346
        JANUS_LOG(LOG_DBG, "Cleaning up session %"SCNu64"...\n", session->session_id);
347
        janus_session_destroy(session->session_id);
348

    
349
        return G_SOURCE_REMOVE;
350
}
351

    
352
static gboolean janus_check_sessions(gpointer user_data) {
353
        GMainContext *watchdog_context = (GMainContext *) user_data;
354
        janus_mutex_lock(&sessions_mutex);
355
        if(sessions && g_hash_table_size(sessions) > 0) {
356
                GHashTableIter iter;
357
                gpointer value;
358
                g_hash_table_iter_init(&iter, sessions);
359
                while (g_hash_table_iter_next(&iter, NULL, &value)) {
360
                        janus_session *session = (janus_session *) value;
361
                        if (!session || session->destroy) {
362
                                continue;
363
                        }
364
                        gint64 now = janus_get_monotonic_time();
365
                        if (now - session->last_activity >= SESSION_TIMEOUT * G_USEC_PER_SEC && !session->timeout) {
366
                                JANUS_LOG(LOG_INFO, "Timeout expired for session %"SCNu64"...\n", session->session_id);
367

    
368
                                /* Notify the transport */
369
                                if(session->source) {
370
                                        json_t *event = json_object();
371
                                        json_object_set_new(event, "janus", json_string("timeout"));
372
                                        json_object_set_new(event, "session_id", json_integer(session->session_id));
373
                                        /* Send this to the transport client */
374
                                        session->source->transport->send_message(session->source->instance, NULL, FALSE, event);
375
                                        /* Notify the transport plugin about the session timeout */
376
                                        session->source->transport->session_over(session->source->instance, session->session_id, TRUE);
377
                                }
378

    
379
                                /* Mark the session as over, we'll deal with it later */
380
                                session->timeout = 1;
381
                                /* FIXME Is this safe? apparently it causes hash table errors on the console */
382
                                g_hash_table_iter_remove(&iter);
383
                                g_hash_table_insert(old_sessions, janus_uint64_dup(session->session_id), session);
384

    
385
                                /* Schedule the session for deletion */
386
                                GSource *timeout_source = g_timeout_source_new_seconds(3);
387
                                g_source_set_callback(timeout_source, janus_cleanup_session, session, NULL);
388
                                g_source_attach(timeout_source, watchdog_context);
389
                                g_source_unref(timeout_source);
390
                        }
391
                }
392
        }
393
        janus_mutex_unlock(&sessions_mutex);
394

    
395
        return G_SOURCE_CONTINUE;
396
}
397

    
398
static gpointer janus_sessions_watchdog(gpointer user_data) {
399
        GMainLoop *loop = (GMainLoop *) user_data;
400
        GMainContext *watchdog_context = g_main_loop_get_context(loop);
401
        GSource *timeout_source;
402

    
403
        timeout_source = g_timeout_source_new_seconds(2);
404
        g_source_set_callback(timeout_source, janus_check_sessions, watchdog_context, NULL);
405
        g_source_attach(timeout_source, watchdog_context);
406
        g_source_unref(timeout_source);
407

    
408
        JANUS_LOG(LOG_INFO, "Sessions watchdog started\n");
409

    
410
        g_main_loop_run(loop);
411

    
412
        return NULL;
413
}
414

    
415
janus_session *janus_session_create(guint64 session_id) {
416
        if(session_id == 0) {
417
                while(session_id == 0) {
418
                        session_id = janus_random_uint64();
419
                        if(janus_session_find(session_id) != NULL) {
420
                                /* Session ID already taken, try another one */
421
                                session_id = 0;
422
                        }
423
                }
424
        }
425
        JANUS_LOG(LOG_INFO, "Creating new session: %"SCNu64"\n", session_id);
426
        janus_session *session = (janus_session *)g_malloc0(sizeof(janus_session));
427
        if(session == NULL) {
428
                JANUS_LOG(LOG_FATAL, "Memory error!\n");
429
                return NULL;
430
        }
431
        session->session_id = session_id;
432
        session->source = NULL;
433
        session->destroy = 0;
434
        session->timeout = 0;
435
        session->last_activity = janus_get_monotonic_time();
436
        janus_mutex_init(&session->mutex);
437
        janus_mutex_lock(&sessions_mutex);
438
        g_hash_table_insert(sessions, janus_uint64_dup(session->session_id), session);
439
        janus_mutex_unlock(&sessions_mutex);
440
        return session;
441
}
442

    
443
janus_session *janus_session_find(guint64 session_id) {
444
        janus_mutex_lock(&sessions_mutex);
445
        janus_session *session = g_hash_table_lookup(sessions, &session_id);
446
        janus_mutex_unlock(&sessions_mutex);
447
        return session;
448
}
449

    
450
janus_session *janus_session_find_destroyed(guint64 session_id) {
451
        janus_mutex_lock(&sessions_mutex);
452
        janus_session *session = g_hash_table_lookup(old_sessions, &session_id);
453
        janus_mutex_unlock(&sessions_mutex);
454
        return session;
455
}
456

    
457
void janus_session_notify_event(guint64 session_id, json_t *event) {
458
        janus_mutex_lock(&sessions_mutex);
459
        janus_session *session = sessions ? g_hash_table_lookup(sessions, &session_id) : NULL;
460
        if(session != NULL && !session->destroy && session->source != NULL && session->source->transport != NULL) {
461
                janus_mutex_unlock(&sessions_mutex);
462
                /* Send this to the transport client */
463
                JANUS_LOG(LOG_HUGE, "Sending event to %s (%p)\n", session->source->transport->get_package(), session->source->instance);
464
                session->source->transport->send_message(session->source->instance, NULL, FALSE, event);
465
        } else {
466
                janus_mutex_unlock(&sessions_mutex);
467
                /* No transport, free the event */
468
                json_decref(event);
469
        }
470
}
471

    
472

    
473
/* Destroys a session but does not remove it from the sessions hash table. */
474
gint janus_session_destroy(guint64 session_id) {
475
        janus_session *session = janus_session_find_destroyed(session_id);
476
        if(session == NULL) {
477
                JANUS_LOG(LOG_ERR, "Couldn't find session to destroy: %"SCNu64"\n", session_id);
478
                return -1;
479
        }
480
        JANUS_LOG(LOG_VERB, "Destroying session %"SCNu64"\n", session_id);
481
        session->destroy = 1;
482
        if (session->ice_handles != NULL && g_hash_table_size(session->ice_handles) > 0) {
483
                GHashTableIter iter;
484
                gpointer value;
485
                /* Remove all handles */
486
                g_hash_table_iter_init(&iter, session->ice_handles);
487
                while (g_hash_table_iter_next(&iter, NULL, &value)) {
488
                        janus_ice_handle *handle = value;
489
                        if(!handle || g_atomic_int_get(&stop)) {
490
                                continue;
491
                        }
492
                        janus_ice_handle_destroy(session, handle->handle_id);
493
                        g_hash_table_iter_remove(&iter);
494
                }
495
        }
496

    
497
        /* FIXME Actually destroy session */
498
        janus_session_free(session);
499

    
500
        return 0;
501
}
502

    
503
void janus_session_free(janus_session *session) {
504
        if(session == NULL)
505
                return;
506
        janus_mutex_lock(&session->mutex);
507
        if(session->ice_handles != NULL) {
508
                g_hash_table_destroy(session->ice_handles);
509
                session->ice_handles = NULL;
510
        }
511
        if(session->source != NULL) {
512
                janus_request_destroy(session->source);
513
                session->source = NULL;
514
        }
515
        janus_mutex_unlock(&session->mutex);
516
        g_free(session);
517
        session = NULL;
518
}
519

    
520

    
521
/* Requests management */
522
janus_request *janus_request_new(janus_transport *transport, void *instance, void *request_id, gboolean admin, json_t *message) {
523
        janus_request *request = (janus_request *)g_malloc0(sizeof(janus_request));
524
        request->transport = transport;
525
        request->instance = instance;
526
        request->request_id = request_id;
527
        request->admin = admin;
528
        request->message = message;
529
        return request;
530
}
531

    
532
void janus_request_destroy(janus_request *request) {
533
        if(request == NULL)
534
                return;
535
        request->transport = NULL;
536
        request->instance = NULL;
537
        request->request_id = NULL;
538
        if(request->message)
539
                json_decref(request->message);
540
        request->message = NULL;
541
        g_free(request);
542
}
543

    
544
int janus_process_incoming_request(janus_request *request) {
545
        int ret = -1;
546
        if(request == NULL) {
547
                JANUS_LOG(LOG_ERR, "Missing request or payload to process, giving up...\n");
548
                return ret;
549
        }
550
        int error_code = 0;
551
        char error_cause[100];
552
        json_t *root = request->message;
553
        /* Ok, let's start with the ids */
554
        guint64 session_id = 0, handle_id = 0;
555
        json_t *s = json_object_get(root, "session_id");
556
        if(s && json_is_integer(s))
557
                session_id = json_integer_value(s);
558
        json_t *h = json_object_get(root, "handle_id");
559
        if(h && json_is_integer(h))
560
                handle_id = json_integer_value(h);
561

    
562
        /* Get transaction and message request */
563
        JANUS_VALIDATE_JSON_OBJECT(root, incoming_request_parameters,
564
                error_code, error_cause, FALSE,
565
                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
566
        if(error_code != 0) {
567
                ret = janus_process_error_string(request, session_id, NULL, error_code, error_cause);
568
                goto jsondone;
569
        }
570
        json_t *transaction = json_object_get(root, "transaction");
571
        const gchar *transaction_text = json_string_value(transaction);
572
        json_t *message = json_object_get(root, "janus");
573
        const gchar *message_text = json_string_value(message);
574

    
575
        if(session_id == 0 && handle_id == 0) {
576
                /* Can only be a 'Create new session', a 'Get info' or a 'Ping/Pong' request */
577
                if(!strcasecmp(message_text, "info")) {
578
                        ret = janus_process_success(request, janus_info(transaction_text));
579
                        goto jsondone;
580
                }
581
                if(!strcasecmp(message_text, "ping")) {
582
                        /* Prepare JSON reply */
583
                        json_t *reply = json_object();
584
                        json_object_set_new(reply, "janus", json_string("pong"));
585
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
586
                        ret = janus_process_success(request, reply);
587
                        goto jsondone;
588
                }
589
                if(strcasecmp(message_text, "create")) {
590
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
591
                        goto jsondone;
592
                }
593
                /* Any secret/token to check? */
594
                gboolean secret_authorized = FALSE, token_authorized = FALSE;
595
                if(api_secret == NULL && !janus_auth_is_enabled()) {
596
                        /* Nothing to check */
597
                        secret_authorized = TRUE;
598
                        token_authorized = TRUE;
599
                } else {
600
                        if(api_secret != NULL) {
601
                                /* There's an API secret, check that the client provided it */
602
                                json_t *secret = json_object_get(root, "apisecret");
603
                                if(secret && json_is_string(secret) && janus_strcmp_const_time(json_string_value(secret), api_secret)) {
604
                                        secret_authorized = TRUE;
605
                                }
606
                        }
607
                        if(janus_auth_is_enabled()) {
608
                                /* The token based authentication mechanism is enabled, check that the client provided it */
609
                                json_t *token = json_object_get(root, "token");
610
                                if(token && json_is_string(token) && janus_auth_check_token(json_string_value(token))) {
611
                                        token_authorized = TRUE;
612
                                }
613
                        }
614
                        /* We consider a request authorized if either the proper API secret or a valid token has been provided */
615
                        if(!secret_authorized && !token_authorized) {
616
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED, NULL);
617
                                goto jsondone;
618
                        }
619
                }
620
                session_id = 0;
621
                json_t *id = json_object_get(root, "id");
622
                if(id != NULL) {
623
                        /* The application provided the session ID to use */
624
                        session_id = json_integer_value(id);
625
                        if(session_id > 0 && janus_session_find(session_id) != NULL) {
626
                                /* Session ID already taken */
627
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_CONFLICT, "Session ID already in use");
628
                                goto jsondone;
629
                        }
630
                }
631
                /* Handle it */
632
                janus_session *session = janus_session_create(session_id);
633
                if(session == NULL) {
634
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Memory error");
635
                        goto jsondone;
636
                }
637
                session_id = session->session_id;
638
                /* Take note of the request source that originated this session (HTTP, WebSockets, RabbitMQ?) */
639
                session->source = janus_request_new(request->transport, request->instance, NULL, FALSE, NULL);
640
                /* Notify the source that a new session has been created */
641
                request->transport->session_created(request->instance, session->session_id);
642
                /* Prepare JSON reply */
643
                json_t *reply = json_object();
644
                json_object_set_new(reply, "janus", json_string("success"));
645
                json_object_set_new(reply, "transaction", json_string(transaction_text));
646
                json_t *data = json_object();
647
                json_object_set_new(data, "id", json_integer(session_id));
648
                json_object_set_new(reply, "data", data);
649
                /* Send the success reply */
650
                ret = janus_process_success(request, reply);
651
                goto jsondone;
652
        }
653
        if(session_id < 1) {
654
                JANUS_LOG(LOG_ERR, "Invalid session\n");
655
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, NULL);
656
                goto jsondone;
657
        }
658
        if(h && handle_id < 1) {
659
                JANUS_LOG(LOG_ERR, "Invalid handle\n");
660
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, NULL);
661
                goto jsondone;
662
        }
663

    
664
        /* Go on with the processing */
665
        gboolean secret_authorized = FALSE, token_authorized = FALSE;
666
        if(api_secret == NULL && !janus_auth_is_enabled()) {
667
                /* Nothing to check */
668
                secret_authorized = TRUE;
669
                token_authorized = TRUE;
670
        } else {
671
                if(api_secret != NULL) {
672
                        /* There's an API secret, check that the client provided it */
673
                        json_t *secret = json_object_get(root, "apisecret");
674
                        if(secret && json_is_string(secret) && janus_strcmp_const_time(json_string_value(secret), api_secret)) {
675
                                secret_authorized = TRUE;
676
                        }
677
                }
678
                if(janus_auth_is_enabled()) {
679
                        /* The token based authentication mechanism is enabled, check that the client provided it */
680
                        json_t *token = json_object_get(root, "token");
681
                        if(token && json_is_string(token) && janus_auth_check_token(json_string_value(token))) {
682
                                token_authorized = TRUE;
683
                        }
684
                }
685
                /* We consider a request authorized if either the proper API secret or a valid token has been provided */
686
                if(!secret_authorized && !token_authorized) {
687
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED, NULL);
688
                        goto jsondone;
689
                }
690
        }
691

    
692
        /* If we got here, make sure we have a session (and/or a handle) */
693
        janus_session *session = janus_session_find(session_id);
694
        if(!session) {
695
                JANUS_LOG(LOG_ERR, "Couldn't find any session %"SCNu64"...\n", session_id);
696
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, "No such session %"SCNu64"", session_id);
697
                goto jsondone;
698
        }
699
        /* Update the last activity timer */
700
        session->last_activity = janus_get_monotonic_time();
701
        janus_ice_handle *handle = NULL;
702
        if(handle_id > 0) {
703
                handle = janus_ice_handle_find(session, handle_id);
704
                if(!handle) {
705
                        JANUS_LOG(LOG_ERR, "Couldn't find any handle %"SCNu64" in session %"SCNu64"...\n", handle_id, session_id);
706
                        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);
707
                        goto jsondone;
708
                }
709
        }
710

    
711
        /* What is this? */
712
        if(!strcasecmp(message_text, "keepalive")) {
713
                /* Just a keep-alive message, reply with an ack */
714
                JANUS_LOG(LOG_VERB, "Got a keep-alive on session %"SCNu64"\n", session_id);
715
                json_t *reply = json_object();
716
                json_object_set_new(reply, "janus", json_string("ack"));
717
                json_object_set_new(reply, "session_id", json_integer(session_id));
718
                json_object_set_new(reply, "transaction", json_string(transaction_text));
719
                /* Send the success reply */
720
                ret = janus_process_success(request, reply);
721
        } else if(!strcasecmp(message_text, "attach")) {
722
                if(handle != NULL) {
723
                        /* Attach is a session-level command */
724
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
725
                        goto jsondone;
726
                }
727
                JANUS_VALIDATE_JSON_OBJECT(root, attach_parameters,
728
                        error_code, error_cause, FALSE,
729
                        JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
730
                if(error_code != 0) {
731
                        ret = janus_process_error_string(request, session_id, NULL, error_code, error_cause);
732
                        goto jsondone;
733
                }
734
                json_t *plugin = json_object_get(root, "plugin");
735
                const gchar *plugin_text = json_string_value(plugin);
736
                janus_plugin *plugin_t = janus_plugin_find(plugin_text);
737
                if(plugin_t == NULL) {
738
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_NOT_FOUND, "No such plugin '%s'", plugin_text);
739
                        goto jsondone;
740
                }
741
                /* If the auth token mechanism is enabled, we should check if this token can access this plugin */
742
                if(janus_auth_is_enabled()) {
743
                        json_t *token = json_object_get(root, "token");
744
                        if(token != NULL) {
745
                                const char *token_value = json_string_value(token);
746
                                if(token_value && !janus_auth_check_plugin(token_value, plugin_t)) {
747
                                        JANUS_LOG(LOG_ERR, "Token '%s' can't access plugin '%s'\n", token_value, plugin_text);
748
                                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED_PLUGIN, "Provided token can't access plugin '%s'", plugin_text);
749
                                        goto jsondone;
750
                                }
751
                        }
752
                }
753
                /* Create handle */
754
                handle = janus_ice_handle_create(session);
755
                if(handle == NULL) {
756
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Memory error");
757
                        goto jsondone;
758
                }
759
                handle_id = handle->handle_id;
760
                /* Attach to the plugin */
761
                int error = 0;
762
                if((error = janus_ice_handle_attach_plugin(session, handle_id, plugin_t)) != 0) {
763
                        /* TODO Make error struct to pass verbose information */
764
                        janus_ice_handle_destroy(session, handle_id);
765
                        janus_mutex_lock(&session->mutex);
766
                        g_hash_table_remove(session->ice_handles, &handle_id);
767
                        janus_mutex_unlock(&session->mutex);
768
                        JANUS_LOG(LOG_ERR, "Couldn't attach to plugin '%s', error '%d'\n", plugin_text, error);
769
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_ATTACH, "Couldn't attach to plugin: error '%d'", error);
770
                        goto jsondone;
771
                }
772
                /* Prepare JSON reply */
773
                json_t *reply = json_object();
774
                json_object_set_new(reply, "janus", json_string("success"));
775
                json_object_set_new(reply, "session_id", json_integer(session_id));
776
                json_object_set_new(reply, "transaction", json_string(transaction_text));
777
                json_t *data = json_object();
778
                json_object_set_new(data, "id", json_integer(handle_id));
779
                json_object_set_new(reply, "data", data);
780
                /* Send the success reply */
781
                ret = janus_process_success(request, reply);
782
        } else if(!strcasecmp(message_text, "destroy")) {
783
                if(handle != NULL) {
784
                        /* Query is a session-level command */
785
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
786
                        goto jsondone;
787
                }
788
                /* Schedule the session for deletion */
789
                session->destroy = 1;
790
                janus_mutex_lock(&sessions_mutex);
791
                g_hash_table_remove(sessions, &session->session_id);
792
                g_hash_table_insert(old_sessions, janus_uint64_dup(session->session_id), session);
793
                GSource *timeout_source = g_timeout_source_new_seconds(3);
794
                g_source_set_callback(timeout_source, janus_cleanup_session, session, NULL);
795
                g_source_attach(timeout_source, sessions_watchdog_context);
796
                g_source_unref(timeout_source);
797
                janus_mutex_unlock(&sessions_mutex);
798
                /* Notify the source that the session has been destroyed */
799
                if(session->source && session->source->transport)
800
                        session->source->transport->session_over(session->source->instance, session->session_id, FALSE);
801

    
802
                /* Prepare JSON reply */
803
                json_t *reply = json_object();
804
                json_object_set_new(reply, "janus", json_string("success"));
805
                json_object_set_new(reply, "session_id", json_integer(session_id));
806
                json_object_set_new(reply, "transaction", json_string(transaction_text));
807
                /* Send the success reply */
808
                ret = janus_process_success(request, reply);
809
        } else if(!strcasecmp(message_text, "detach")) {
810
                if(handle == NULL) {
811
                        /* Query is an handle-level command */
812
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
813
                        goto jsondone;
814
                }
815
                if(handle->app == NULL || handle->app_handle == NULL) {
816
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_DETACH, "No plugin to detach from");
817
                        goto jsondone;
818
                }
819
                int error = janus_ice_handle_destroy(session, handle_id);
820
                janus_mutex_lock(&session->mutex);
821
                g_hash_table_remove(session->ice_handles, &handle_id);
822
                janus_mutex_unlock(&session->mutex);
823

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

    
1234
                /* Make sure the app handle is still valid */
1235
                if(handle->app == NULL || handle->app_handle == NULL || !janus_plugin_session_is_alive(handle->app_handle)) {
1236
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "No plugin to handle this message");
1237
                        if(jsep_type)
1238
                                g_free(jsep_type);
1239
                        if(jsep_sdp_stripped)
1240
                                g_free(jsep_sdp_stripped);
1241
                        janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1242
                        goto jsondone;
1243
                }
1244

    
1245
                /* Send the message to the plugin (which must eventually free transaction_text, body_text, jsep_type and sdp) */
1246
                char *body_text = json_dumps(body, JSON_INDENT(3) | JSON_PRESERVE_ORDER);
1247
                janus_plugin_result *result = plugin_t->handle_message(handle->app_handle, g_strdup((char *)transaction_text), body_text, jsep_type, jsep_sdp_stripped);
1248
                if(result == NULL) {
1249
                        /* Something went horribly wrong! */
1250
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "Plugin didn't give a result");
1251
                        goto jsondone;
1252
                }
1253
                if(result->type == JANUS_PLUGIN_OK) {
1254
                        /* The plugin gave a result already (synchronous request/response) */
1255
                        if(result->content == NULL) {
1256
                                /* Missing content... */
1257
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "Plugin didn't provide any content for this synchronous response");
1258
                                janus_plugin_result_destroy(result);
1259
                                goto jsondone;
1260
                        }
1261
                        json_error_t error;
1262
                        json_t *event = json_loads(result->content, 0, &error);
1263
                        if(!event) {
1264
                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot send response from plugin (JSON error: on line %d: %s)\n", handle->handle_id, error.line, error.text);
1265
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "Plugin returned an invalid JSON response");
1266
                                janus_plugin_result_destroy(result);
1267
                                goto jsondone;
1268
                        }
1269
                        if(!json_is_object(event)) {
1270
                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot send response from plugin (JSON error: not an object)\n", handle->handle_id);
1271
                                json_decref(event);
1272
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "Plugin returned an invalid JSON response");
1273
                                janus_plugin_result_destroy(result);
1274
                                goto jsondone;
1275
                        }
1276
                        /* Prepare JSON response */
1277
                        json_t *reply = json_object();
1278
                        json_object_set_new(reply, "janus", json_string("success"));
1279
                        json_object_set_new(reply, "session_id", json_integer(session->session_id));
1280
                        json_object_set_new(reply, "sender", json_integer(handle->handle_id));
1281
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1282
                        json_t *plugin_data = json_object();
1283
                        json_object_set_new(plugin_data, "plugin", json_string(plugin_t->get_package()));
1284
                        json_object_set_new(plugin_data, "data", event);
1285
                        json_object_set_new(reply, "plugindata", plugin_data);
1286
                        /* Send the success reply */
1287
                        ret = janus_process_success(request, reply);
1288
                } else if(result->type == JANUS_PLUGIN_OK_WAIT) {
1289
                        /* The plugin received the request but didn't process it yet, send an ack (asynchronous notifications may follow) */
1290
                        json_t *reply = json_object();
1291
                        json_object_set_new(reply, "janus", json_string("ack"));
1292
                        json_object_set_new(reply, "session_id", json_integer(session_id));
1293
                        if(result->content)
1294
                                json_object_set_new(reply, "hint", json_string(result->content));
1295
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
1296
                        /* Send the success reply */
1297
                        ret = janus_process_success(request, reply);
1298
                } else {
1299
                        /* Something went horribly wrong! */
1300
                        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");
1301
                        janus_plugin_result_destroy(result);
1302
                        goto jsondone;
1303
                }
1304
                janus_plugin_result_destroy(result);
1305
        } else if(!strcasecmp(message_text, "trickle")) {
1306
                if(handle == NULL) {
1307
                        /* Trickle is an handle-level command */
1308
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
1309
                        goto jsondone;
1310
                }
1311
                if(handle->app == NULL || handle->app_handle == NULL || !janus_plugin_session_is_alive(handle->app_handle)) {
1312
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "No plugin to handle this trickle candidate");
1313
                        goto jsondone;
1314
                }
1315
                json_t *candidate = json_object_get(root, "candidate");
1316
                json_t *candidates = json_object_get(root, "candidates");
1317
                if(candidate == NULL && candidates == NULL) {
1318
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "Missing mandatory element (candidate|candidates)");
1319
                        goto jsondone;
1320
                }
1321
                if(candidate != NULL && candidates != NULL) {
1322
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_JSON, "Can't have both candidate and candidates");
1323
                        goto jsondone;
1324
                }
1325
                janus_mutex_lock(&handle->mutex);
1326
                if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE)) {
1327
                        /* It looks like this peer supports Trickle, after all */
1328
                        JANUS_LOG(LOG_VERB, "Handle %"SCNu64" supports trickle even if it didn't negotiate it...\n", handle->handle_id);
1329
                        janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE);
1330
                }
1331
                /* Is there any stream ready? this trickle may get here before the SDP it relates to */
1332
                if(handle->audio_stream == NULL && handle->video_stream == NULL && handle->data_stream == NULL) {
1333
                        JANUS_LOG(LOG_WARN, "[%"SCNu64"] No stream, queueing this trickle as it got here before the SDP...\n", handle->handle_id);
1334
                        /* Enqueue this trickle candidate(s), we'll process this later */
1335
                        janus_ice_trickle *early_trickle = janus_ice_trickle_new(handle, transaction_text, candidate ? candidate : candidates);
1336
                        handle->pending_trickles = g_list_append(handle->pending_trickles, early_trickle);
1337
                        /* Send the ack right away, an event will tell the application if the candidate(s) failed */
1338
                        goto trickledone;
1339
                }
1340
                /* Is the ICE stack ready already? */
1341
                if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER) ||
1342
                                !janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER) ||
1343
                                !janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER)) {
1344
                        const char *cause = NULL;
1345
                        if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER))
1346
                                cause = "processing the offer";
1347
                        else if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER))
1348
                                cause = "waiting for the answer";
1349
                        else if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER))
1350
                                cause = "waiting for the offer";
1351
                        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Still %s, queueing this trickle to wait until we're done there...\n",
1352
                                handle->handle_id, cause);
1353
                        /* Enqueue this trickle candidate(s), we'll process this later */
1354
                        janus_ice_trickle *early_trickle = janus_ice_trickle_new(handle, transaction_text, candidate ? candidate : candidates);
1355
                        handle->pending_trickles = g_list_append(handle->pending_trickles, early_trickle);
1356
                        /* Send the ack right away, an event will tell the application if the candidate(s) failed */
1357
                        goto trickledone;
1358
                }
1359
                if(candidate != NULL) {
1360
                        /* We got a single candidate */
1361
                        int error = 0;
1362
                        const char *error_string = NULL;
1363
                        if((error = janus_ice_trickle_parse(handle, candidate, &error_string)) != 0) {
1364
                                ret = janus_process_error(request, session_id, transaction_text, error, "%s", error_string);
1365
                                janus_mutex_unlock(&handle->mutex);
1366
                                goto jsondone;
1367
                        }
1368
                } else {
1369
                        /* We got multiple candidates in an array */
1370
                        if(!json_is_array(candidates)) {
1371
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "candidates is not an array");
1372
                                janus_mutex_unlock(&handle->mutex);
1373
                                goto jsondone;
1374
                        }
1375
                        JANUS_LOG(LOG_VERB, "Got multiple candidates (%zu)\n", json_array_size(candidates));
1376
                        if(json_array_size(candidates) > 0) {
1377
                                /* Handle remote candidates */
1378
                                size_t i = 0;
1379
                                for(i=0; i<json_array_size(candidates); i++) {
1380
                                        json_t *c = json_array_get(candidates, i);
1381
                                        /* FIXME We don't care if any trickle fails to parse */
1382
                                        janus_ice_trickle_parse(handle, c, NULL);
1383
                                }
1384
                        }
1385
                }
1386

    
1387
trickledone:
1388
                janus_mutex_unlock(&handle->mutex);
1389
                /* We reply right away, not to block the web server... */
1390
                json_t *reply = json_object();
1391
                json_object_set_new(reply, "janus", json_string("ack"));
1392
                json_object_set_new(reply, "session_id", json_integer(session_id));
1393
                json_object_set_new(reply, "transaction", json_string(transaction_text));
1394
                /* Send the success reply */
1395
                ret = janus_process_success(request, reply);
1396
        } else {
1397
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN_REQUEST, "Unknown request '%s'", message_text);
1398
        }
1399

    
1400
jsondone:
1401
        /* Done processing */
1402
        return ret;
1403
}
1404

    
1405
/* Admin/monitor WebServer requests handler */
1406
int janus_process_incoming_admin_request(janus_request *request) {
1407
        int ret = -1;
1408
        int error_code = 0;
1409
        char error_cause[100];
1410
        if(request == NULL) {
1411
                JANUS_LOG(LOG_ERR, "Missing request or payload to process, giving up...\n");
1412
                return ret;
1413
        }
1414
        json_t *root = request->message;
1415
        /* Ok, let's start with the ids */
1416
        guint64 session_id = 0, handle_id = 0;
1417
        json_t *s = json_object_get(root, "session_id");
1418
        if(s && json_is_integer(s))
1419
                session_id = json_integer_value(s);
1420
        json_t *h = json_object_get(root, "handle_id");
1421
        if(h && json_is_integer(h))
1422
                handle_id = json_integer_value(h);
1423

    
1424
        /* Get transaction and message request */
1425
        JANUS_VALIDATE_JSON_OBJECT(root, admin_parameters,
1426
                error_code, error_cause, FALSE,
1427
                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1428
        if(error_code != 0) {
1429
                ret = janus_process_error_string(request, session_id, NULL, error_code, error_cause);
1430
                goto jsondone;
1431
        }
1432
        json_t *transaction = json_object_get(root, "transaction");
1433
        const gchar *transaction_text = json_string_value(transaction);
1434
        json_t *message = json_object_get(root, "janus");
1435
        if(!message) {
1436
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "Missing mandatory element (janus)");
1437
                goto jsondone;
1438
        }
1439
        if(!json_is_string(message)) {
1440
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (janus should be a string)");
1441
                goto jsondone;
1442
        }
1443
        const gchar *message_text = json_string_value(message);
1444

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

    
1987
        /* Go on with the processing */
1988
        if(admin_api_secret != NULL) {
1989
                /* There's an API secret, check that the client provided it */
1990
                json_t *secret = json_object_get(root, "admin_secret");
1991
                if(!secret || !json_is_string(secret) || !janus_strcmp_const_time(json_string_value(secret), admin_api_secret)) {
1992
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED, NULL);
1993
                        goto jsondone;
1994
                }
1995
        }
1996

    
1997
        /* If we got here, make sure we have a session (and/or a handle) */
1998
        janus_session *session = janus_session_find(session_id);
1999
        if(!session) {
2000
                JANUS_LOG(LOG_ERR, "Couldn't find any session %"SCNu64"...\n", session_id);
2001
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, "No such session %"SCNu64"", session_id);
2002
                goto jsondone;
2003
        }
2004
        janus_ice_handle *handle = NULL;
2005
        if(handle_id > 0) {
2006
                handle = janus_ice_handle_find(session, handle_id);
2007
                if(!handle) {
2008
                        JANUS_LOG(LOG_ERR, "Couldn't find any handle %"SCNu64" in session %"SCNu64"...\n", handle_id, session_id);
2009
                        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);
2010
                        goto jsondone;
2011
                }
2012
        }
2013

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

    
2147
jsondone:
2148
        /* Done processing */
2149
        return ret;
2150
}
2151

    
2152
int janus_process_success(janus_request *request, json_t *payload)
2153
{
2154
        if(!request || !payload)
2155
                return -1;
2156
        /* Pass to the right transport plugin */
2157
        JANUS_LOG(LOG_HUGE, "Sending %s API response to %s (%p)\n", request->admin ? "admin" : "Janus", request->transport->get_package(), request->instance);
2158
        return request->transport->send_message(request->instance, request->request_id, request->admin, payload);
2159
}
2160

    
2161
static int janus_process_error_string(janus_request *request, uint64_t session_id, const char *transaction, gint error, gchar *error_string)
2162
{
2163
        if(!request)
2164
                return -1;
2165
        /* Done preparing error */
2166
        JANUS_LOG(LOG_VERB, "[%s] Returning %s API error %d (%s)\n", transaction, request->admin ? "admin" : "Janus", error, error_string);
2167
        /* Prepare JSON error */
2168
        json_t *reply = json_object();
2169
        json_object_set_new(reply, "janus", json_string("error"));
2170
        if(session_id > 0)
2171
                json_object_set_new(reply, "session_id", json_integer(session_id));
2172
        if(transaction != NULL)
2173
                json_object_set_new(reply, "transaction", json_string(transaction));
2174
        json_t *error_data = json_object();
2175
        json_object_set_new(error_data, "code", json_integer(error));
2176
        json_object_set_new(error_data, "reason", json_string(error_string));
2177
        json_object_set_new(reply, "error", error_data);
2178
        /* Pass to the right transport plugin */
2179
        return request->transport->send_message(request->instance, request->request_id, request->admin, reply);
2180
}
2181

    
2182
int janus_process_error(janus_request *request, uint64_t session_id, const char *transaction, gint error, const char *format, ...)
2183
{
2184
        if(!request)
2185
                return -1;
2186
        gchar *error_string = NULL;
2187
        gchar error_buf[512];
2188
        if(format == NULL) {
2189
                /* No error string provided, use the default one */
2190
                error_string = (gchar *)janus_get_api_error(error);
2191
        } else {
2192
                /* This callback has variable arguments (error string) */
2193
                va_list ap;
2194
                va_start(ap, format);
2195
                g_vsnprintf(error_buf, sizeof(error_buf), format, ap);
2196
                va_end(ap);
2197
                error_string = error_buf;
2198
        }
2199
        return janus_process_error_string(request, session_id, transaction, error, error_string);
2200
}
2201

    
2202
/* Admin/monitor helpers */
2203
json_t *janus_admin_stream_summary(janus_ice_stream *stream) {
2204
        if(stream == NULL)
2205
                return NULL;
2206
        json_t *s = json_object();
2207
        json_object_set_new(s, "id", json_integer(stream->stream_id));
2208
        json_object_set_new(s, "ready", json_integer(stream->cdone));
2209
        json_object_set_new(s, "disabled", json_string(stream->disabled ? "true" : "false"));
2210
        json_t *ss = json_object();
2211
        if(stream->audio_ssrc)
2212
                json_object_set_new(ss, "audio", json_integer(stream->audio_ssrc));
2213
        if(stream->video_ssrc)
2214
                json_object_set_new(ss, "video", json_integer(stream->video_ssrc));
2215
        if(stream->audio_ssrc_peer)
2216
                json_object_set_new(ss, "audio-peer", json_integer(stream->audio_ssrc_peer));
2217
        if(stream->video_ssrc_peer)
2218
                json_object_set_new(ss, "video-peer", json_integer(stream->video_ssrc_peer));
2219
        if(stream->video_ssrc_peer_rtx)
2220
                json_object_set_new(ss, "video-peer-rtx", json_integer(stream->video_ssrc_peer_rtx));
2221
        json_object_set_new(s, "ssrc", ss);
2222
        json_t *components = json_array();
2223
        if(stream->rtp_component) {
2224
                json_t *c = janus_admin_component_summary(stream->rtp_component);
2225
                if(c)
2226
                        json_array_append_new(components, c);
2227
        }
2228
        if(stream->rtcp_component) {
2229
                json_t *c = janus_admin_component_summary(stream->rtcp_component);
2230
                if(c)
2231
                        json_array_append_new(components, c);
2232
        }
2233
        json_t *rtcp_stats = NULL;
2234
        if(stream->audio_rtcp_ctx != NULL) {
2235
                rtcp_stats = json_object();
2236
                json_t *audio_rtcp_stats = json_object();
2237
                json_object_set_new(audio_rtcp_stats, "base", json_integer(stream->audio_rtcp_ctx->tb));
2238
                json_object_set_new(audio_rtcp_stats, "lsr", json_integer(janus_rtcp_context_get_lsr(stream->audio_rtcp_ctx)));
2239
                json_object_set_new(audio_rtcp_stats, "lost", json_integer(janus_rtcp_context_get_lost_all(stream->audio_rtcp_ctx, FALSE)));
2240
                json_object_set_new(audio_rtcp_stats, "lost-by-remote", json_integer(janus_rtcp_context_get_lost_all(stream->audio_rtcp_ctx, TRUE)));
2241
                json_object_set_new(audio_rtcp_stats, "jitter-local", json_integer(janus_rtcp_context_get_jitter(stream->audio_rtcp_ctx, FALSE)));
2242
                json_object_set_new(audio_rtcp_stats, "jitter-remote", json_integer(janus_rtcp_context_get_jitter(stream->audio_rtcp_ctx, TRUE)));
2243
                json_object_set_new(rtcp_stats, "audio", audio_rtcp_stats);
2244
        }
2245
        if(stream->video_rtcp_ctx != NULL) {
2246
                if(rtcp_stats == NULL)
2247
                        rtcp_stats = json_object();
2248
                json_t *video_rtcp_stats = json_object();
2249
                json_object_set_new(video_rtcp_stats, "base", json_integer(stream->video_rtcp_ctx->tb));
2250
                json_object_set_new(video_rtcp_stats, "lsr", json_integer(janus_rtcp_context_get_lsr(stream->video_rtcp_ctx)));
2251
                json_object_set_new(video_rtcp_stats, "lost", json_integer(janus_rtcp_context_get_lost_all(stream->video_rtcp_ctx, FALSE)));
2252
                json_object_set_new(video_rtcp_stats, "lost-by-remote", json_integer(janus_rtcp_context_get_lost_all(stream->video_rtcp_ctx, TRUE)));
2253
                json_object_set_new(video_rtcp_stats, "jitter-local", json_integer(janus_rtcp_context_get_jitter(stream->video_rtcp_ctx, FALSE)));
2254
                json_object_set_new(video_rtcp_stats, "jitter-remote", json_integer(janus_rtcp_context_get_jitter(stream->video_rtcp_ctx, TRUE)));
2255
                json_object_set_new(rtcp_stats, "video", video_rtcp_stats);
2256
        }
2257
        if(rtcp_stats != NULL)
2258
                json_object_set_new(s, "rtcp_stats", rtcp_stats);
2259
        json_object_set_new(s, "components", components);
2260
        return s;
2261
}
2262

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

    
2370

    
2371
/* Transports */
2372
void janus_transport_close(gpointer key, gpointer value, gpointer user_data) {
2373
        janus_transport *transport = (janus_transport *)value;
2374
        if(!transport)
2375
                return;
2376
        transport->destroy();
2377
}
2378

    
2379
void janus_transportso_close(gpointer key, gpointer value, gpointer user_data) {
2380
        void *transport = (janus_transport *)value;
2381
        if(!transport)
2382
                return;
2383
        //~ dlclose(transport);
2384
}
2385

    
2386
/* Transport callback interface */
2387
void janus_transport_incoming_request(janus_transport *plugin, void *transport, void *request_id, gboolean admin, json_t *message, json_error_t *error) {
2388
        JANUS_LOG(LOG_VERB, "Got %s API request from %s (%p)\n", admin ? "an admin" : "a Janus", plugin->get_package(), transport);
2389
        /* Create a janus_request instance to handle the request */
2390
        janus_request *request = janus_request_new(plugin, transport, request_id, admin, message);
2391
        GError *tperror = NULL;
2392
        g_thread_pool_push(tasks, request, &tperror);
2393
        if(tperror != NULL) {
2394
                /* Something went wrong... */
2395
                JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to push task in thread pool...\n", tperror->code, tperror->message ? tperror->message : "??");
2396
                json_t *transaction = json_object_get(message, "transaction");
2397
                const char *transaction_text = json_is_string(transaction) ? json_string_value(transaction) : NULL;
2398
                janus_process_error(request, 0, transaction_text, JANUS_ERROR_UNKNOWN, "Thread pool error");
2399
                janus_request_destroy(request);
2400
        }
2401
}
2402

    
2403
void janus_transport_gone(janus_transport *plugin, void *transport) {
2404
        /* Get rid of sessions this transport was handling */
2405
        JANUS_LOG(LOG_VERB, "A %s transport instance has gone away (%p)\n", plugin->get_package(), transport);
2406
        janus_mutex_lock(&sessions_mutex);
2407
        if(sessions && g_hash_table_size(sessions) > 0) {
2408
                GHashTableIter iter;
2409
                gpointer value;
2410
                g_hash_table_iter_init(&iter, sessions);
2411
                while(g_hash_table_iter_next(&iter, NULL, &value)) {
2412
                        janus_session *session = (janus_session *) value;
2413
                        if(!session || session->destroy || session->timeout || session->last_activity == 0)
2414
                                continue;
2415
                        if(session->source && session->source->instance == transport) {
2416
                                JANUS_LOG(LOG_VERB, "  -- Marking Session %"SCNu64" as over\n", session->session_id);
2417
                                session->last_activity = 0;        /* This will trigger a timeout */
2418
                        }
2419
                }
2420
        }
2421
        janus_mutex_unlock(&sessions_mutex);
2422
}
2423

    
2424
gboolean janus_transport_is_api_secret_needed(janus_transport *plugin) {
2425
        return api_secret != NULL;
2426
}
2427

    
2428
gboolean janus_transport_is_api_secret_valid(janus_transport *plugin, const char *apisecret) {
2429
        if(api_secret == NULL)
2430
                return TRUE;
2431
        return apisecret && janus_strcmp_const_time(apisecret, api_secret);
2432
}
2433

    
2434
gboolean janus_transport_is_auth_token_needed(janus_transport *plugin) {
2435
        return janus_auth_is_enabled();
2436
}
2437

    
2438
gboolean janus_transport_is_auth_token_valid(janus_transport *plugin, const char *token) {
2439
        if(!janus_auth_is_enabled())
2440
                return TRUE;
2441
        return token && janus_auth_check_token(token);
2442
}
2443

    
2444
void janus_transport_task(gpointer data, gpointer user_data) {
2445
        JANUS_LOG(LOG_VERB, "Transport task pool, serving request\n");
2446
        janus_request *request = (janus_request *)data;
2447
        if(request == NULL) {
2448
                JANUS_LOG(LOG_ERR, "Missing request\n");
2449
                return;
2450
        }
2451
        if(!request->admin)
2452
                janus_process_incoming_request(request);
2453
        else
2454
                janus_process_incoming_admin_request(request);
2455
        /* Done */
2456
        janus_request_destroy(request);
2457
}
2458

    
2459

    
2460
/* Plugins */
2461
void janus_plugin_close(gpointer key, gpointer value, gpointer user_data) {
2462
        janus_plugin *plugin = (janus_plugin *)value;
2463
        if(!plugin)
2464
                return;
2465
        plugin->destroy();
2466
}
2467

    
2468
void janus_pluginso_close(gpointer key, gpointer value, gpointer user_data) {
2469
        void *plugin = (janus_plugin *)value;
2470
        if(!plugin)
2471
                return;
2472
        //~ dlclose(plugin);
2473
}
2474

    
2475
janus_plugin *janus_plugin_find(const gchar *package) {
2476
        if(package != NULL && plugins != NULL)        /* FIXME Do we need to fix the key pointer? */
2477
                return g_hash_table_lookup(plugins, package);
2478
        return NULL;
2479
}
2480

    
2481

    
2482
/* Plugin callback interface */
2483
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) {
2484
        if(!plugin || !message)
2485
                return -1;
2486
        if(!plugin_session || plugin_session < (janus_plugin_session *)0x1000 ||
2487
                        !janus_plugin_session_is_alive(plugin_session) || plugin_session->stopped)
2488
                return -2;
2489
        janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
2490
        if(!ice_handle || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP))
2491
                return JANUS_ERROR_SESSION_NOT_FOUND;
2492
        janus_session *session = ice_handle->session;
2493
        if(!session || session->destroy)
2494
                return JANUS_ERROR_SESSION_NOT_FOUND;
2495
        /* Make sure this is JSON */
2496
        json_error_t error;
2497
        json_t *plugin_event = json_loads(message, 0, &error);
2498
        if(!plugin_event) {
2499
                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (JSON error: on line %d: %s)\n", ice_handle->handle_id, error.line, error.text);
2500
                return JANUS_ERROR_INVALID_JSON;
2501
        }
2502
        if(!json_is_object(plugin_event)) {
2503
                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (JSON error: not an object)\n", ice_handle->handle_id);
2504
                return JANUS_ERROR_INVALID_JSON_OBJECT;
2505
        }
2506
        /* Attach JSEP if possible? */
2507
        json_t *jsep = NULL;
2508
        if(sdp_type != NULL && sdp != NULL) {
2509
                jsep = janus_plugin_handle_sdp(plugin_session, plugin, sdp_type, sdp);
2510
                if(jsep == NULL) {
2511
                        if(ice_handle == NULL || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2512
                                        || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)) {
2513
                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (handle not available anymore or negotiation stopped)\n", ice_handle->handle_id);
2514
                                return JANUS_ERROR_HANDLE_NOT_FOUND;
2515
                        } else {
2516
                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (JSON error: problem with the SDP)\n", ice_handle->handle_id);
2517
                                return JANUS_ERROR_JSEP_INVALID_SDP;
2518
                        }
2519
                }
2520
        }
2521
        /* Prepare JSON event */
2522
        json_t *event = json_object();
2523
        json_object_set_new(event, "janus", json_string("event"));
2524
        json_object_set_new(event, "session_id", json_integer(session->session_id));
2525
        json_object_set_new(event, "sender", json_integer(ice_handle->handle_id));
2526
        if(transaction != NULL)
2527
                json_object_set_new(event, "transaction", json_string(transaction));
2528
        json_t *plugin_data = json_object();
2529
        json_object_set_new(plugin_data, "plugin", json_string(plugin->get_package()));
2530
        json_object_set_new(plugin_data, "data", plugin_event);
2531
        json_object_set_new(event, "plugindata", plugin_data);
2532
        if(jsep != NULL)
2533
                json_object_set_new(event, "jsep", jsep);
2534
        /* Send the event */
2535
        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Sending event to transport...\n", ice_handle->handle_id);
2536
        janus_session_notify_event(session->session_id, event);
2537

    
2538
        return JANUS_OK;
2539
}
2540

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

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

    
2865
        /* Prepare JSON event */
2866
        json_t *jsep = json_object();
2867
        json_object_set_new(jsep, "type", json_string(sdp_type));
2868
        json_object_set_new(jsep, "sdp", json_string(sdp_merged));
2869
        g_free(sdp_stripped);
2870
        ice_handle->local_sdp = sdp_merged;
2871
        return jsep;
2872
}
2873

    
2874
void janus_plugin_relay_rtp(janus_plugin_session *plugin_session, int video, char *buf, int len) {
2875
        if((plugin_session < (janus_plugin_session *)0x1000) || plugin_session->stopped || buf == NULL || len < 1)
2876
                return;
2877
        janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
2878
        if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2879
                        || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
2880
                return;
2881
        janus_ice_relay_rtp(handle, video, buf, len);
2882
}
2883

    
2884
void janus_plugin_relay_rtcp(janus_plugin_session *plugin_session, int video, char *buf, int len) {
2885
        if((plugin_session < (janus_plugin_session *)0x1000) || plugin_session->stopped || buf == NULL || len < 1)
2886
                return;
2887
        janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
2888
        if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2889
                        || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
2890
                return;
2891
        janus_ice_relay_rtcp(handle, video, buf, len);
2892
}
2893

    
2894
void janus_plugin_relay_data(janus_plugin_session *plugin_session, char *buf, int len) {
2895
        if((plugin_session < (janus_plugin_session *)0x1000) || plugin_session->stopped || buf == NULL || len < 1)
2896
                return;
2897
        janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
2898
        if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2899
                        || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
2900
                return;
2901
#ifdef HAVE_SCTP
2902
        janus_ice_relay_data(handle, buf, len);
2903
#else
2904
        JANUS_LOG(LOG_WARN, "Asked to relay data, but Data Channels support has not been compiled...\n");
2905
#endif
2906
}
2907

    
2908
void janus_plugin_close_pc(janus_plugin_session *plugin_session) {
2909
        /* A plugin asked to get rid of a PeerConnection */
2910
        if((plugin_session < (janus_plugin_session *)0x1000) || !janus_plugin_session_is_alive(plugin_session) || plugin_session->stopped)
2911
                return;
2912
        janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
2913
        if(!ice_handle)
2914
                return;
2915
        if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2916
                        || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
2917
                return;
2918
        janus_session *session = (janus_session *)ice_handle->session;
2919
        if(!session)
2920
                return;
2921

    
2922
        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Plugin asked to hangup PeerConnection: sending alert\n", ice_handle->handle_id);
2923
        /* Send an alert on all the DTLS connections */
2924
        janus_ice_webrtc_hangup(ice_handle);
2925
}
2926

    
2927
void janus_plugin_end_session(janus_plugin_session *plugin_session) {
2928
        /* A plugin asked to get rid of a handle */
2929
        if((plugin_session < (janus_plugin_session *)0x1000) || !janus_plugin_session_is_alive(plugin_session) || plugin_session->stopped)
2930
                return;
2931
        janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
2932
        if(!ice_handle)
2933
                return;
2934
        janus_session *session = (janus_session *)ice_handle->session;
2935
        if(!session)
2936
                return;
2937
        /* Destroy the handle */
2938
        janus_ice_handle_destroy(session, ice_handle->handle_id);
2939
        janus_mutex_lock(&session->mutex);
2940
        g_hash_table_remove(session->ice_handles, &ice_handle->handle_id);
2941
        janus_mutex_unlock(&session->mutex);
2942
}
2943

    
2944

    
2945
static void janus_detect_local_ip(gchar *buf, size_t buflen) {
2946
        JANUS_LOG(LOG_VERB, "Autodetecting local IP...\n");
2947
        struct sockaddr_in addr;
2948
        socklen_t len;
2949
        int fd = socket(AF_INET, SOCK_DGRAM, 0);
2950
        if (fd == -1)
2951
                goto error;
2952
        addr.sin_family = AF_INET;
2953
        addr.sin_port = htons(1);
2954
        inet_pton(AF_INET, "1.2.3.4", &addr.sin_addr.s_addr);
2955
        if (connect(fd, (const struct sockaddr*) &addr, sizeof(addr)) < 0)
2956
                goto error;
2957
        len = sizeof(addr);
2958
        if (getsockname(fd, (struct sockaddr*) &addr, &len) < 0)
2959
                goto error;
2960
        if (getnameinfo((const struct sockaddr*) &addr, sizeof(addr),
2961
                        buf, buflen,
2962
                        NULL, 0, NI_NUMERICHOST) != 0)
2963
                goto error;
2964
        close(fd);
2965
        return;
2966
error:
2967
        if (fd != -1)
2968
                close(fd);
2969
        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");
2970
        g_strlcpy(buf, "127.0.0.1", buflen);
2971
}
2972

    
2973

    
2974
/* Main */
2975
gint main(int argc, char *argv[])
2976
{
2977
        /* Core dumps may be disallowed by parent of this process; change that */
2978
        struct rlimit core_limits;
2979
        core_limits.rlim_cur = core_limits.rlim_max = RLIM_INFINITY;
2980
        setrlimit(RLIMIT_CORE, &core_limits);
2981

    
2982
        struct gengetopt_args_info args_info;
2983
        /* Let's call our cmdline parser */
2984
        if(cmdline_parser(argc, argv, &args_info) != 0)
2985
                exit(1);
2986

    
2987
        /* Any configuration to open? */
2988
        if(args_info.config_given) {
2989
                config_file = g_strdup(args_info.config_arg);
2990
        }
2991
        if(args_info.configs_folder_given) {
2992
                configs_folder = g_strdup(args_info.configs_folder_arg);
2993
        } else {
2994
                configs_folder = g_strdup (CONFDIR);
2995
        }
2996
        if(config_file == NULL) {
2997
                char file[255];
2998
                g_snprintf(file, 255, "%s/janus.cfg", configs_folder);
2999
                config_file = g_strdup(file);
3000
        }
3001
        if((config = janus_config_parse(config_file)) == NULL) {
3002
                if(args_info.config_given) {
3003
                        /* We only give up if the configuration file was explicitly provided */
3004
                        g_print("Error reading configuration from %s\n", config_file);
3005
                        exit(1);
3006
                }
3007
                g_print("Error reading/parsing the configuration file, going on with the defaults and the command line arguments\n");
3008
                config = janus_config_create("janus.cfg");
3009
                if(config == NULL) {
3010
                        /* If we can't even create an empty configuration, something's definitely wrong */
3011
                        exit(1);
3012
                }
3013
        }
3014

    
3015
        /* Check if we need to log to console and/or file */
3016
        gboolean use_stdout = TRUE;
3017
        if(args_info.disable_stdout_given) {
3018
                use_stdout = FALSE;
3019
                janus_config_add_item(config, "general", "log_to_stdout", "no");
3020
        } else {
3021
                /* Check if the configuration file is saying anything about this */
3022
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "log_to_stdout");
3023
                if(item && item->value && !janus_is_true(item->value))
3024
                        use_stdout = FALSE;
3025
        }
3026
        const char *logfile = NULL;
3027
        if(args_info.log_file_given) {
3028
                logfile = args_info.log_file_arg;
3029
                janus_config_add_item(config, "general", "log_to_file", "no");
3030
        } else {
3031
                /* Check if the configuration file is saying anything about this */
3032
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "log_to_file");
3033
                if(item && item->value)
3034
                        logfile = item->value;
3035
        }
3036

    
3037
        /* Check if we're going to daemonize Janus */
3038
        if(args_info.daemon_given) {
3039
                daemonize = TRUE;
3040
                janus_config_add_item(config, "general", "daemonize", "yes");
3041
        } else {
3042
                /* Check if the configuration file is saying anything about this */
3043
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "daemonize");
3044
                if(item && item->value && janus_is_true(item->value))
3045
                        daemonize = TRUE;
3046
        }
3047
        /* If we're going to daemonize, make sure logging to stdout is disabled and a log file has been specified */
3048
        if(daemonize && use_stdout) {
3049
                use_stdout = FALSE;
3050
        }
3051
        if(daemonize && logfile == NULL) {
3052
                g_print("Running Janus as a daemon but no log file provided, giving up...\n");
3053
                exit(1);
3054
        }
3055
        /* Daemonize now, if we need to */
3056
        if(daemonize) {
3057
                g_print("Running Janus as a daemon\n");
3058

    
3059
                /* Create a pipe for parent<->child communication during the startup phase */
3060
                if(pipe(pipefd) == -1) {
3061
                        g_print("pipe error!\n");
3062
                        exit(1);
3063
                }
3064

    
3065
                /* Fork off the parent process */
3066
                pid_t pid = fork();
3067
                if(pid < 0) {
3068
                        g_print("Fork error!\n");
3069
                        exit(1);
3070
                }
3071
                if(pid > 0) {
3072
                        /* Ok, we're the parent: let's wait for the child to tell us everything started fine */
3073
                        close(pipefd[1]);
3074
                        int code = -1;
3075
                        struct pollfd pollfds;
3076

    
3077
                        while(code < 0) {
3078
                                pollfds.fd = pipefd[0];
3079
                                pollfds.events = POLLIN;
3080
                                int res = poll(&pollfds, 1, -1);
3081
                                if(res < 0)
3082
                                        break;
3083
                                if(res == 0)
3084
                                        continue;
3085
                                if(pollfds.revents & POLLERR || pollfds.revents & POLLHUP)
3086
                                        break;
3087
                                if(pollfds.revents & POLLIN) {
3088
                                        res = read(pipefd[0], &code, sizeof(int));
3089
                                        break;
3090
                                }
3091
                        }
3092
                        if(code < 0)
3093
                                code = 1;
3094

    
3095
                        /* Leave the parent and return the exit code we received from the child */
3096
                        if(code)
3097
                                g_print("Error launching Janus (error code %d), check the logs for more details\n", code);
3098
                        exit(code);
3099
                }
3100
                /* Change the file mode mask */
3101
                umask(0);
3102

    
3103
                /* Create a new SID for the child process */
3104
                pid_t sid = setsid();
3105
                if(sid < 0) {
3106
                        g_print("Error setting SID!\n");
3107
                        exit(1);
3108
                }
3109
                /* Change the current working directory */
3110
                if((chdir("/")) < 0) {
3111
                        g_print("Error changing the current working directory!\n");
3112
                        exit(1);
3113
                }
3114
                /* We close stdin/stdout/stderr when initializing the logger */
3115
        }
3116

    
3117
        /* Initialize logger */
3118
        if(janus_log_init(daemonize, use_stdout, logfile) < 0)
3119
                exit(1);
3120

    
3121
        JANUS_PRINT("---------------------------------------------------\n");
3122
        JANUS_PRINT("  Starting Meetecho Janus (WebRTC Gateway) v%s\n", JANUS_VERSION_STRING);
3123
        JANUS_PRINT("---------------------------------------------------\n\n");
3124

    
3125
        /* Handle SIGINT (CTRL-C), SIGTERM (from service managers) */
3126
        signal(SIGINT, janus_handle_signal);
3127
        signal(SIGTERM, janus_handle_signal);
3128
        atexit(janus_termination_handler);
3129

    
3130
        /* Setup Glib */
3131
#if !GLIB_CHECK_VERSION(2, 36, 0)
3132
        g_type_init();
3133
#endif
3134

    
3135
        /* Logging level: default is info and no timestamps */
3136
        janus_log_level = LOG_INFO;
3137
        janus_log_timestamps = FALSE;
3138
        janus_log_colors = TRUE;
3139
        if(args_info.debug_level_given) {
3140
                if(args_info.debug_level_arg < LOG_NONE)
3141
                        args_info.debug_level_arg = 0;
3142
                else if(args_info.debug_level_arg > LOG_MAX)
3143
                        args_info.debug_level_arg = LOG_MAX;
3144
                janus_log_level = args_info.debug_level_arg;
3145
        }
3146

    
3147
        /* Any PID we need to create? */
3148
        const char *pidfile = NULL;
3149
        if(args_info.pid_file_given) {
3150
                pidfile = args_info.pid_file_arg;
3151
                janus_config_add_item(config, "general", "pid_file", pidfile);
3152
        } else {
3153
                /* Check if the configuration file is saying anything about this */
3154
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "pid_file");
3155
                if(item && item->value)
3156
                        pidfile = item->value;
3157
        }
3158
        if(janus_pidfile_create(pidfile) < 0)
3159
                exit(1);
3160

    
3161
        /* Proceed with the rest of the configuration */
3162
        janus_config_print(config);
3163
        if(args_info.debug_level_given) {
3164
                char debug[5];
3165
                g_snprintf(debug, 5, "%d", args_info.debug_level_arg);
3166
                janus_config_add_item(config, "general", "debug_level", debug);
3167
        } else {
3168
                /* No command line directive on logging, try the configuration file */
3169
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "debug_level");
3170
                if(item && item->value) {
3171
                        int temp_level = atoi(item->value);
3172
                        if(temp_level == 0 && strcmp(item->value, "0")) {
3173
                                JANUS_PRINT("Invalid debug level %s (configuration), using default (info=4)\n", item->value);
3174
                        } else {
3175
                                janus_log_level = temp_level;
3176
                                if(janus_log_level < LOG_NONE)
3177
                                        janus_log_level = 0;
3178
                                else if(janus_log_level > LOG_MAX)
3179
                                        janus_log_level = LOG_MAX;
3180
                        }
3181
                }
3182
        }
3183
        /* Any command line argument that should overwrite the configuration? */
3184
        JANUS_PRINT("Checking command line arguments...\n");
3185
        if(args_info.debug_timestamps_given) {
3186
                janus_config_add_item(config, "general", "debug_timestamps", "yes");
3187
        }
3188
        if(args_info.disable_colors_given) {
3189
                janus_config_add_item(config, "general", "debug_colors", "no");
3190
        }
3191
        if(args_info.server_name_given) {
3192
                janus_config_add_item(config, "general", "server_name", args_info.server_name_arg);
3193
        }
3194
         if(args_info.interface_given) {
3195
                janus_config_add_item(config, "general", "interface", args_info.interface_arg);
3196
        }
3197
        if(args_info.configs_folder_given) {
3198
                janus_config_add_item(config, "general", "configs_folder", args_info.configs_folder_arg);
3199
        }
3200
        if(args_info.plugins_folder_given) {
3201
                janus_config_add_item(config, "general", "plugins_folder", args_info.plugins_folder_arg);
3202
        }
3203
        if(args_info.apisecret_given) {
3204
                janus_config_add_item(config, "general", "api_secret", args_info.apisecret_arg);
3205
        }
3206
        if(args_info.token_auth_given) {
3207
                janus_config_add_item(config, "general", "token_auth", "yes");
3208
        }
3209
        if(args_info.cert_pem_given) {
3210
                janus_config_add_item(config, "certificates", "cert_pem", args_info.cert_pem_arg);
3211
        }
3212
        if(args_info.cert_key_given) {
3213
                janus_config_add_item(config, "certificates", "cert_key", args_info.cert_key_arg);
3214
        }
3215
        if(args_info.stun_server_given) {
3216
                /* Split in server and port (if port missing, use 3478 as default) */
3217
                char *stunport = strrchr(args_info.stun_server_arg, ':');
3218
                if(stunport != NULL) {
3219
                        *stunport = '\0';
3220
                        stunport++;
3221
                        janus_config_add_item(config, "nat", "stun_server", args_info.stun_server_arg);
3222
                        janus_config_add_item(config, "nat", "stun_port", stunport);
3223
                } else {
3224
                        janus_config_add_item(config, "nat", "stun_server", args_info.stun_server_arg);
3225
                        janus_config_add_item(config, "nat", "stun_port", "3478");
3226
                }
3227
        }
3228
        if(args_info.nat_1_1_given) {
3229
                janus_config_add_item(config, "nat", "nat_1_1_mapping", args_info.nat_1_1_arg);
3230
        }
3231
        if(args_info.ice_enforce_list_given) {
3232
                janus_config_add_item(config, "nat", "ice_enforce_list", args_info.ice_enforce_list_arg);
3233
        }
3234
        if(args_info.ice_ignore_list_given) {
3235
                janus_config_add_item(config, "nat", "ice_ignore_list", args_info.ice_ignore_list_arg);
3236
        }
3237
        if(args_info.libnice_debug_given) {
3238
                janus_config_add_item(config, "nat", "nice_debug", "true");
3239
        }
3240
        if(args_info.ice_lite_given) {
3241
                janus_config_add_item(config, "nat", "ice_lite", "true");
3242
        }
3243
        if(args_info.ice_tcp_given) {
3244
                janus_config_add_item(config, "nat", "ice_tcp", "true");
3245
        }
3246
        if(args_info.ipv6_candidates_given) {
3247
                janus_config_add_item(config, "media", "ipv6", "true");
3248
        }
3249
        if(args_info.force_bundle_given) {
3250
                janus_config_add_item(config, "media", "force-bundle", "true");
3251
        }
3252
        if(args_info.force_rtcp_mux_given) {
3253
                janus_config_add_item(config, "media", "force-rtcp-mux", "true");
3254
        }
3255
        if(args_info.max_nack_queue_given) {
3256
                char mnq[20];
3257
                g_snprintf(mnq, 20, "%d", args_info.max_nack_queue_arg);
3258
                janus_config_add_item(config, "media", "max_nack_queue", mnq);
3259
        }
3260
        if(args_info.rtp_port_range_given) {
3261
                janus_config_add_item(config, "media", "rtp_port_range", args_info.rtp_port_range_arg);
3262
        }
3263
        janus_config_print(config);
3264

    
3265
        /* Logging/debugging */
3266
        JANUS_PRINT("Debug/log level is %d\n", janus_log_level);
3267
        janus_config_item *item = janus_config_get_item_drilldown(config, "general", "debug_timestamps");
3268
        if(item && item->value)
3269
                janus_log_timestamps = janus_is_true(item->value);
3270
        JANUS_PRINT("Debug/log timestamps are %s\n", janus_log_timestamps ? "enabled" : "disabled");
3271
        item = janus_config_get_item_drilldown(config, "general", "debug_colors");
3272
        if(item && item->value)
3273
                janus_log_colors = janus_is_true(item->value);
3274
        JANUS_PRINT("Debug/log colors are %s\n", janus_log_colors ? "enabled" : "disabled");
3275

    
3276
        /* Any IP/interface to enforce/ignore? */
3277
        item = janus_config_get_item_drilldown(config, "nat", "ice_enforce_list");
3278
        if(item && item->value) {
3279
                gchar **list = g_strsplit(item->value, ",", -1);
3280
                gchar *index = list[0];
3281
                if(index != NULL) {
3282
                        int i=0;
3283
                        while(index != NULL) {
3284
                                if(strlen(index) > 0) {
3285
                                        JANUS_LOG(LOG_INFO, "Adding '%s' to the ICE enforce list...\n", index);
3286
                                        janus_ice_enforce_interface(g_strdup(index));
3287
                                }
3288
                                i++;
3289
                                index = list[i];
3290
                        }
3291
                }
3292
                g_strfreev(list);
3293
                list = NULL;
3294
        }
3295
        item = janus_config_get_item_drilldown(config, "nat", "ice_ignore_list");
3296
        if(item && item->value) {
3297
                gchar **list = g_strsplit(item->value, ",", -1);
3298
                gchar *index = list[0];
3299
                if(index != NULL) {
3300
                        int i=0;
3301
                        while(index != NULL) {
3302
                                if(strlen(index) > 0) {
3303
                                        JANUS_LOG(LOG_INFO, "Adding '%s' to the ICE ignore list...\n", index);
3304
                                        janus_ice_ignore_interface(g_strdup(index));
3305
                                }
3306
                                i++;
3307
                                index = list[i];
3308
                        }
3309
                }
3310
                g_strfreev(list);
3311
                list = NULL;
3312
        }
3313
        /* What is the local IP? */
3314
        JANUS_LOG(LOG_VERB, "Selecting local IP address...\n");
3315
        gboolean local_ip_set = FALSE;
3316
        item = janus_config_get_item_drilldown(config, "general", "interface");
3317
        if(item && item->value) {
3318
                JANUS_LOG(LOG_VERB, "  -- Will try to use %s\n", item->value);
3319
                int family;
3320
                if (!janus_is_ip_valid(item->value, &family)) {
3321
                        JANUS_LOG(LOG_WARN, "Invalid local IP specified: %s, guessing the default...\n", item->value);
3322
                } else {
3323
                        /* Verify that we can actually bind to that address */
3324
                        int fd = socket(family, SOCK_DGRAM, 0);
3325
                        if (fd == -1) {
3326
                                JANUS_LOG(LOG_WARN, "Error creating test socket, falling back to detecting IP address...\n");
3327
                        } else {
3328
                                int r;
3329
                                struct sockaddr_storage ss;
3330
                                socklen_t addrlen;
3331
                                memset(&ss, 0, sizeof(ss));
3332
                                if (family == AF_INET) {
3333
                                        struct sockaddr_in *addr4 = (struct sockaddr_in*)&ss;
3334
                                        addr4->sin_family = AF_INET;
3335
                                        addr4->sin_port = 0;
3336
                                        inet_pton(AF_INET, item->value, &(addr4->sin_addr.s_addr));
3337
                                        addrlen = sizeof(struct sockaddr_in);
3338
                                } else {
3339
                                        struct sockaddr_in6 *addr6 = (struct sockaddr_in6*)&ss;
3340
                                        addr6->sin6_family = AF_INET6;
3341
                                        addr6->sin6_port = 0;
3342
                                        inet_pton(AF_INET6, item->value, &(addr6->sin6_addr.s6_addr));
3343
                                        addrlen = sizeof(struct sockaddr_in6);
3344
                                }
3345
                                r = bind(fd, (const struct sockaddr*)&ss, addrlen);
3346
                                close(fd);
3347
                                if (r < 0) {
3348
                                        JANUS_LOG(LOG_WARN, "Error setting local IP address to %s, falling back to detecting IP address...\n", item->value);
3349
                                } else {
3350
                                        g_strlcpy(local_ip, item->value, sizeof(local_ip));
3351
                                        local_ip_set = TRUE;
3352
                                }
3353
                        }
3354
                }
3355
        }
3356
        if (!local_ip_set)
3357
                janus_detect_local_ip(local_ip, sizeof(local_ip));
3358
        JANUS_LOG(LOG_INFO, "Using %s as local IP...\n", local_ip);
3359

    
3360
        /* Was a custom instance name provided? */
3361
        item = janus_config_get_item_drilldown(config, "general", "server_name");
3362
        if(item && item->value) {
3363
                server_name = g_strdup(item->value);
3364
        }
3365

    
3366
        /* Is there any API secret to consider? */
3367
        api_secret = NULL;
3368
        item = janus_config_get_item_drilldown(config, "general", "api_secret");
3369
        if(item && item->value) {
3370
                api_secret = g_strdup(item->value);
3371
        }
3372
        /* Is there any API secret to consider? */
3373
        admin_api_secret = NULL;
3374
        item = janus_config_get_item_drilldown(config, "general", "admin_secret");
3375
        if(item && item->value) {
3376
                admin_api_secret = g_strdup(item->value);
3377
        }
3378
        /* Also check if the token based authentication mechanism needs to be enabled */
3379
        item = janus_config_get_item_drilldown(config, "general", "token_auth");
3380
        janus_auth_init(item && item->value && janus_is_true(item->value));
3381

    
3382
        /* Setup ICE stuff (e.g., checking if the provided STUN server is correct) */
3383
        char *stun_server = NULL, *turn_server = NULL;
3384
        uint16_t stun_port = 0, turn_port = 0;
3385
        char *turn_type = NULL, *turn_user = NULL, *turn_pwd = NULL;
3386
        char *turn_rest_api = NULL, *turn_rest_api_key = NULL;
3387
#ifdef HAVE_LIBCURL
3388
        char *turn_rest_api_method = NULL;
3389
#endif
3390
        const char *nat_1_1_mapping = NULL;
3391
        uint16_t rtp_min_port = 0, rtp_max_port = 0;
3392
        gboolean ice_lite = FALSE, ice_tcp = FALSE, ipv6 = FALSE;
3393
        item = janus_config_get_item_drilldown(config, "media", "ipv6");
3394
        ipv6 = (item && item->value) ? janus_is_true(item->value) : FALSE;
3395
        item = janus_config_get_item_drilldown(config, "media", "rtp_port_range");
3396
        if(item && item->value) {
3397
                /* Split in min and max port */
3398
                char *maxport = strrchr(item->value, '-');
3399
                if(maxport != NULL) {
3400
                        *maxport = '\0';
3401
                        maxport++;
3402
                        rtp_min_port = atoi(item->value);
3403
                        rtp_max_port = atoi(maxport);
3404
                        maxport--;
3405
                        *maxport = '-';
3406
                }
3407
                if(rtp_min_port > rtp_max_port) {
3408
                        int temp_port = rtp_min_port;
3409
                        rtp_min_port = rtp_max_port;
3410
                        rtp_max_port = temp_port;
3411
                }
3412
                if(rtp_max_port == 0)
3413
                        rtp_max_port = 65535;
3414
                JANUS_LOG(LOG_INFO, "RTP port range: %u -- %u\n", rtp_min_port, rtp_max_port);
3415
        }
3416
        /* Check if we need to enable the ICE Lite mode */
3417
        item = janus_config_get_item_drilldown(config, "nat", "ice_lite");
3418
        ice_lite = (item && item->value) ? janus_is_true(item->value) : FALSE;
3419
        /* Check if we need to enable ICE-TCP support (warning: still broken, for debugging only) */
3420
        item = janus_config_get_item_drilldown(config, "nat", "ice_tcp");
3421
        ice_tcp = (item && item->value) ? janus_is_true(item->value) : FALSE;
3422
        /* Any STUN server to use in Janus? */
3423
        item = janus_config_get_item_drilldown(config, "nat", "stun_server");
3424
        if(item && item->value)
3425
                stun_server = (char *)item->value;
3426
        item = janus_config_get_item_drilldown(config, "nat", "stun_port");
3427
        if(item && item->value)
3428
                stun_port = atoi(item->value);
3429
        /* Any 1:1 NAT mapping to take into account? */
3430
        item = janus_config_get_item_drilldown(config, "nat", "nat_1_1_mapping");
3431
        if(item && item->value) {
3432
                JANUS_LOG(LOG_VERB, "Using nat_1_1_mapping for public ip - %s\n", item->value);
3433
                nat_1_1_mapping = item->value;
3434
                janus_set_public_ip(item->value);
3435
                janus_ice_enable_nat_1_1();
3436
        }
3437
        /* Any TURN server to use in Janus? */
3438
        item = janus_config_get_item_drilldown(config, "nat", "turn_server");
3439
        if(item && item->value)
3440
                turn_server = (char *)item->value;
3441
        item = janus_config_get_item_drilldown(config, "nat", "turn_port");
3442
        if(item && item->value)
3443
                turn_port = atoi(item->value);
3444
        item = janus_config_get_item_drilldown(config, "nat", "turn_type");
3445
        if(item && item->value)
3446
                turn_type = (char *)item->value;
3447
        item = janus_config_get_item_drilldown(config, "nat", "turn_user");
3448
        if(item && item->value)
3449
                turn_user = (char *)item->value;
3450
        item = janus_config_get_item_drilldown(config, "nat", "turn_pwd");
3451
        if(item && item->value)
3452
                turn_pwd = (char *)item->value;
3453
        /* Check if there's any TURN REST API backend to use */
3454
        item = janus_config_get_item_drilldown(config, "nat", "turn_rest_api");
3455
        if(item && item->value)
3456
                turn_rest_api = (char *)item->value;
3457
        item = janus_config_get_item_drilldown(config, "nat", "turn_rest_api_key");
3458
        if(item && item->value)
3459
                turn_rest_api_key = (char *)item->value;
3460
#ifdef HAVE_LIBCURL
3461
        item = janus_config_get_item_drilldown(config, "nat", "turn_rest_api_method");
3462
        if(item && item->value)
3463
                turn_rest_api_method = (char *)item->value;
3464
#endif
3465
        /* Initialize the ICE stack now */
3466
        janus_ice_init(ice_lite, ice_tcp, ipv6, rtp_min_port, rtp_max_port);
3467
        if(janus_ice_set_stun_server(stun_server, stun_port) < 0) {
3468
                JANUS_LOG(LOG_FATAL, "Invalid STUN address %s:%u\n", stun_server, stun_port);
3469
                exit(1);
3470
        }
3471
        if(janus_ice_set_turn_server(turn_server, turn_port, turn_type, turn_user, turn_pwd) < 0) {
3472
                JANUS_LOG(LOG_FATAL, "Invalid TURN address %s:%u\n", turn_server, turn_port);
3473
                exit(1);
3474
        }
3475
#ifndef HAVE_LIBCURL
3476
        if(turn_rest_api != NULL || turn_rest_api_key != NULL) {
3477
                JANUS_LOG(LOG_WARN, "A TURN REST API backend specified in the settings, but libcurl support has not been built\n");
3478
        }
3479
#else
3480
        if(janus_ice_set_turn_rest_api(turn_rest_api, turn_rest_api_key, turn_rest_api_method) < 0) {
3481
                JANUS_LOG(LOG_FATAL, "Invalid TURN REST API configuration: %s (%s, %s)\n", turn_rest_api, turn_rest_api_key, turn_rest_api_method);
3482
                exit(1);
3483
        }
3484
#endif
3485
        item = janus_config_get_item_drilldown(config, "nat", "nice_debug");
3486
        if(item && item->value && janus_is_true(item->value)) {
3487
                /* Enable libnice debugging */
3488
                janus_ice_debugging_enable();
3489
        }
3490
        if(stun_server == NULL && turn_server == NULL) {
3491
                /* No STUN and TURN server provided for Janus: make sure it isn't on a private address */
3492
                gboolean private_address = FALSE;
3493
                const char *test_ip = nat_1_1_mapping ? nat_1_1_mapping : local_ip;
3494
                struct sockaddr_in addr;
3495
                if(inet_pton(AF_INET, test_ip, &addr) > 0) {
3496
                        unsigned short int ip[4];
3497
                        sscanf(test_ip, "%hu.%hu.%hu.%hu", &ip[0], &ip[1], &ip[2], &ip[3]);
3498
                        if(ip[0] == 10) {
3499
                                /* Class A private address */
3500
                                private_address = TRUE;
3501
                        } else if(ip[0] == 172 && (ip[1] >= 16 && ip[1] <= 31)) {
3502
                                /* Class B private address */
3503
                                private_address = TRUE;
3504
                        } else if(ip[0] == 192 && ip[1] == 168) {
3505
                                /* Class C private address */
3506
                                private_address = TRUE;
3507
                        }
3508
                }
3509
                if(private_address) {
3510
                        JANUS_LOG(LOG_WARN, "Janus is deployed on a private address (%s) but you didn't specify any STUN server!"
3511
                                            " Expect trouble if this is supposed to work over the internet and not just in a LAN...\n", test_ip);
3512
                }
3513
        }
3514
        /* Are we going to force BUNDLE and/or rtcp-mux? */
3515
        gboolean force_bundle = FALSE, force_rtcpmux = FALSE;
3516
        item = janus_config_get_item_drilldown(config, "media", "force-bundle");
3517
        force_bundle = (item && item->value) ? janus_is_true(item->value) : FALSE;
3518
        janus_ice_force_bundle(force_bundle);
3519
        item = janus_config_get_item_drilldown(config, "media", "force-rtcp-mux");
3520
        force_rtcpmux = (item && item->value) ? janus_is_true(item->value) : FALSE;
3521
        janus_ice_force_rtcpmux(force_rtcpmux);
3522
        /* NACK related stuff */
3523
        item = janus_config_get_item_drilldown(config, "media", "max_nack_queue");
3524
        if(item && item->value) {
3525
                int mnq = atoi(item->value);
3526
                if(mnq < 0) {
3527
                        JANUS_LOG(LOG_WARN, "Ignoring max_nack_queue value as it's not a positive integer\n");
3528
                } else if(mnq > 0 && mnq < 200) {
3529
                        JANUS_LOG(LOG_WARN, "Ignoring max_nack_queue value as it's less than 200\n");
3530
                } else {
3531
                        janus_set_max_nack_queue(mnq);
3532
                }
3533
        }
3534

    
3535
        /* Setup OpenSSL stuff */
3536
        const char* server_pem;
3537
        item = janus_config_get_item_drilldown(config, "certificates", "cert_pem");
3538
        if(!item || !item->value) {
3539
                server_pem = NULL;
3540
        } else {
3541
                server_pem = item->value;
3542
        }
3543

    
3544
        const char* server_key;
3545
        item = janus_config_get_item_drilldown(config, "certificates", "cert_key");
3546
        if(!item || !item->value) {
3547
                server_key = NULL;
3548
        } else {
3549
                server_key = item->value;
3550
        }
3551
        JANUS_LOG(LOG_VERB, "Using certificates:\n\t%s\n\t%s\n", server_pem, server_key);
3552

    
3553
        SSL_library_init();
3554
        SSL_load_error_strings();
3555
        OpenSSL_add_all_algorithms();
3556
        /* ... and DTLS-SRTP in particular */
3557
        if(janus_dtls_srtp_init(server_pem, server_key) < 0) {
3558
                exit(1);
3559
        }
3560
        /* Check if there's any custom value for the starting MTU to use in the BIO filter */
3561
        item = janus_config_get_item_drilldown(config, "media", "dtls_mtu");
3562
        if(item && item->value)
3563
                janus_dtls_bio_filter_set_mtu(atoi(item->value));
3564

    
3565
#ifdef HAVE_SCTP
3566
        /* Initialize SCTP for DataChannels */
3567
        if(janus_sctp_init() < 0) {
3568
                exit(1);
3569
        }
3570
#else
3571
        JANUS_LOG(LOG_WARN, "Data Channels support not compiled\n");
3572
#endif
3573

    
3574
        /* Initialize Sofia-SDP */
3575
        if(janus_sdp_init() < 0) {
3576
                exit(1);
3577
        }
3578

    
3579
        /* Load plugins */
3580
        const char *path = PLUGINDIR;
3581
        item = janus_config_get_item_drilldown(config, "general", "plugins_folder");
3582
        if(item && item->value)
3583
                path = (char *)item->value;
3584
        JANUS_LOG(LOG_INFO, "Plugins folder: %s\n", path);
3585
        DIR *dir = opendir(path);
3586
        if(!dir) {
3587
                JANUS_LOG(LOG_FATAL, "\tCouldn't access plugins folder...\n");
3588
                exit(1);
3589
        }
3590
        /* Any plugin to ignore? */
3591
        gchar **disabled_plugins = NULL;
3592
        item = janus_config_get_item_drilldown(config, "plugins", "disable");
3593
        if(item && item->value)
3594
                disabled_plugins = g_strsplit(item->value, ",", -1);
3595
        /* Open the shared objects */
3596
        struct dirent *pluginent = NULL;
3597
        char pluginpath[1024];
3598
        while((pluginent = readdir(dir))) {
3599
                int len = strlen(pluginent->d_name);
3600
                if (len < 4) {
3601
                        continue;
3602
                }
3603
                if (strcasecmp(pluginent->d_name+len-strlen(SHLIB_EXT), SHLIB_EXT)) {
3604
                        continue;
3605
                }
3606
                /* Check if this plugins has been disabled in the configuration file */
3607
                if(disabled_plugins != NULL) {
3608
                        gchar *index = disabled_plugins[0];
3609
                        if(index != NULL) {
3610
                                int i=0;
3611
                                gboolean skip = FALSE;
3612
                                while(index != NULL) {
3613
                                        while(isspace(*index))
3614
                                                index++;
3615
                                        if(strlen(index) && !strcmp(index, pluginent->d_name)) {
3616
                                                JANUS_LOG(LOG_WARN, "Plugin '%s' has been disabled, skipping...\n", pluginent->d_name);
3617
                                                skip = TRUE;
3618
                                                break;
3619
                                        }
3620
                                        i++;
3621
                                        index = disabled_plugins[i];
3622
                                }
3623
                                if(skip)
3624
                                        continue;
3625
                        }
3626
                }
3627
                JANUS_LOG(LOG_INFO, "Loading plugin '%s'...\n", pluginent->d_name);
3628
                memset(pluginpath, 0, 1024);
3629
                g_snprintf(pluginpath, 1024, "%s/%s", path, pluginent->d_name);
3630
                void *plugin = dlopen(pluginpath, RTLD_LOCAL | RTLD_LAZY);
3631
                if (!plugin) {
3632
                        JANUS_LOG(LOG_ERR, "\tCouldn't load plugin '%s': %s\n", pluginent->d_name, dlerror());
3633
                } else {
3634
                        create_p *create = (create_p*) dlsym(plugin, "create");
3635
                        const char *dlsym_error = dlerror();
3636
                        if (dlsym_error) {
3637
                                JANUS_LOG(LOG_ERR, "\tCouldn't load symbol 'create': %s\n", dlsym_error);
3638
                                continue;
3639
                        }
3640
                        janus_plugin *janus_plugin = create();
3641
                        if(!janus_plugin) {
3642
                                JANUS_LOG(LOG_ERR, "\tCouldn't use function 'create'...\n");
3643
                                continue;
3644
                        }
3645
                        /* Are all the mandatory methods and callbacks implemented? */
3646
                        if(!janus_plugin->init || !janus_plugin->destroy ||
3647
                                        !janus_plugin->get_api_compatibility ||
3648
                                        !janus_plugin->get_version ||
3649
                                        !janus_plugin->get_version_string ||
3650
                                        !janus_plugin->get_description ||
3651
                                        !janus_plugin->get_package ||
3652
                                        !janus_plugin->get_name ||
3653
                                        !janus_plugin->create_session ||
3654
                                        !janus_plugin->query_session ||
3655
                                        !janus_plugin->destroy_session ||
3656
                                        !janus_plugin->handle_message ||
3657
                                        !janus_plugin->setup_media ||
3658
                                        !janus_plugin->hangup_media) {
3659
                                JANUS_LOG(LOG_ERR, "\tMissing some mandatory methods/callbacks, skipping this plugin...\n");
3660
                                continue;
3661
                        }
3662
                        if(janus_plugin->get_api_compatibility() < JANUS_PLUGIN_API_VERSION) {
3663
                                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",
3664
                                        janus_plugin->get_package(), janus_plugin->get_api_compatibility(), JANUS_PLUGIN_API_VERSION);
3665
                                continue;
3666
                        }
3667
                        janus_plugin->init(&janus_handler_plugin, configs_folder);
3668
                        JANUS_LOG(LOG_VERB, "\tVersion: %d (%s)\n", janus_plugin->get_version(), janus_plugin->get_version_string());
3669
                        JANUS_LOG(LOG_VERB, "\t   [%s] %s\n", janus_plugin->get_package(), janus_plugin->get_name());
3670
                        JANUS_LOG(LOG_VERB, "\t   %s\n", janus_plugin->get_description());
3671
                        JANUS_LOG(LOG_VERB, "\t   Plugin API version: %d\n", janus_plugin->get_api_compatibility());
3672
                        if(!janus_plugin->incoming_rtp && !janus_plugin->incoming_rtcp && !janus_plugin->incoming_data) {
3673
                                JANUS_LOG(LOG_WARN, "The '%s' plugin doesn't implement any callback for RTP/RTCP/data... is this on purpose?\n",
3674
                                        janus_plugin->get_package());
3675
                        }
3676
                        if(!janus_plugin->incoming_rtp && !janus_plugin->incoming_rtcp && janus_plugin->incoming_data) {
3677
                                JANUS_LOG(LOG_WARN, "The '%s' plugin will only handle data channels (no RTP/RTCP)... is this on purpose?\n",
3678
                                        janus_plugin->get_package());
3679
                        }
3680
                        if(plugins == NULL)
3681
                                plugins = g_hash_table_new(g_str_hash, g_str_equal);
3682
                        g_hash_table_insert(plugins, (gpointer)janus_plugin->get_package(), janus_plugin);
3683
                        if(plugins_so == NULL)
3684
                                plugins_so = g_hash_table_new(g_str_hash, g_str_equal);
3685
                        g_hash_table_insert(plugins_so, (gpointer)janus_plugin->get_package(), plugin);
3686
                }
3687
        }
3688
        closedir(dir);
3689
        if(disabled_plugins != NULL)
3690
                g_strfreev(disabled_plugins);
3691
        disabled_plugins = NULL;
3692

    
3693
        /* Create a thread pool to handle incoming requests, no matter what the transport */
3694
        GError *error = NULL;
3695
        tasks = g_thread_pool_new(janus_transport_task, NULL, -1, FALSE, &error);
3696
        if(error != NULL) {
3697
                /* Something went wrong... */
3698
                JANUS_LOG(LOG_FATAL, "Got error %d (%s) trying to launch the request pool task thread...\n", error->code, error->message ? error->message : "??");
3699
                exit(1);
3700
        }
3701

    
3702
        /* Load transports */
3703
        gboolean janus_api_enabled = FALSE, admin_api_enabled = FALSE;
3704
        path = TRANSPORTDIR;
3705
        item = janus_config_get_item_drilldown(config, "general", "transports_folder");
3706
        if(item && item->value)
3707
                path = (char *)item->value;
3708
        JANUS_LOG(LOG_INFO, "Transport plugins folder: %s\n", path);
3709
        dir = opendir(path);
3710
        if(!dir) {
3711
                JANUS_LOG(LOG_FATAL, "\tCouldn't access transport plugins folder...\n");
3712
                exit(1);
3713
        }
3714
        /* Any transport to ignore? */
3715
        gchar **disabled_transports = NULL;
3716
        item = janus_config_get_item_drilldown(config, "transports", "disable");
3717
        if(item && item->value)
3718
                disabled_transports = g_strsplit(item->value, ",", -1);
3719
        /* Open the shared objects */
3720
        struct dirent *transportent = NULL;
3721
        char transportpath[1024];
3722
        while((transportent = readdir(dir))) {
3723
                int len = strlen(transportent->d_name);
3724
                if (len < 4) {
3725
                        continue;
3726
                }
3727
                if (strcasecmp(transportent->d_name+len-strlen(SHLIB_EXT), SHLIB_EXT)) {
3728
                        continue;
3729
                }
3730
                /* Check if this transports has been disabled in the configuration file */
3731
                if(disabled_transports != NULL) {
3732
                        gchar *index = disabled_transports[0];
3733
                        if(index != NULL) {
3734
                                int i=0;
3735
                                gboolean skip = FALSE;
3736
                                while(index != NULL) {
3737
                                        while(isspace(*index))
3738
                                                index++;
3739
                                        if(strlen(index) && !strcmp(index, transportent->d_name)) {
3740
                                                JANUS_LOG(LOG_WARN, "Transport plugin '%s' has been disabled, skipping...\n", transportent->d_name);
3741
                                                skip = TRUE;
3742
                                                break;
3743
                                        }
3744
                                        i++;
3745
                                        index = disabled_transports[i];
3746
                                }
3747
                                if(skip)
3748
                                        continue;
3749
                        }
3750
                }
3751
                JANUS_LOG(LOG_INFO, "Loading transport plugin '%s'...\n", transportent->d_name);
3752
                memset(transportpath, 0, 1024);
3753
                g_snprintf(transportpath, 1024, "%s/%s", path, transportent->d_name);
3754
                void *transport = dlopen(transportpath, RTLD_LOCAL | RTLD_LAZY);
3755
                if (!transport) {
3756
                        JANUS_LOG(LOG_ERR, "\tCouldn't load transport plugin '%s': %s\n", transportent->d_name, dlerror());
3757
                } else {
3758
                        create_t *create = (create_t*) dlsym(transport, "create");
3759
                        const char *dlsym_error = dlerror();
3760
                        if (dlsym_error) {
3761
                                JANUS_LOG(LOG_ERR, "\tCouldn't load symbol 'create': %s\n", dlsym_error);
3762
                                continue;
3763
                        }
3764
                        janus_transport *janus_transport = create();
3765
                        if(!janus_transport) {
3766
                                JANUS_LOG(LOG_ERR, "\tCouldn't use function 'create'...\n");
3767
                                continue;
3768
                        }
3769
                        /* Are all the mandatory methods and callbacks implemented? */
3770
                        if(!janus_transport->init || !janus_transport->destroy ||
3771
                                        !janus_transport->get_api_compatibility ||
3772
                                        !janus_transport->get_version ||
3773
                                        !janus_transport->get_version_string ||
3774
                                        !janus_transport->get_description ||
3775
                                        !janus_transport->get_package ||
3776
                                        !janus_transport->get_name ||
3777
                                        !janus_transport->send_message ||
3778
                                        !janus_transport->is_janus_api_enabled ||
3779
                                        !janus_transport->is_admin_api_enabled ||
3780
                                        !janus_transport->session_created ||
3781
                                        !janus_transport->session_over) {
3782
                                JANUS_LOG(LOG_ERR, "\tMissing some mandatory methods/callbacks, skipping this transport plugin...\n");
3783
                                continue;
3784
                        }
3785
                        if(janus_transport->get_api_compatibility() < JANUS_TRANSPORT_API_VERSION) {
3786
                                JANUS_LOG(LOG_ERR, "The '%s' transport plugin was compiled against an older version of the API (%d < %d), skipping it: update it to enable it again\n",
3787
                                        janus_transport->get_package(), janus_transport->get_api_compatibility(), JANUS_TRANSPORT_API_VERSION);
3788
                                continue;
3789
                        }
3790
                        janus_transport->init(&janus_handler_transport, configs_folder);
3791
                        JANUS_LOG(LOG_VERB, "\tVersion: %d (%s)\n", janus_transport->get_version(), janus_transport->get_version_string());
3792
                        JANUS_LOG(LOG_VERB, "\t   [%s] %s\n", janus_transport->get_package(), janus_transport->get_name());
3793
                        JANUS_LOG(LOG_VERB, "\t   %s\n", janus_transport->get_description());
3794
                        JANUS_LOG(LOG_VERB, "\t   Plugin API version: %d\n", janus_transport->get_api_compatibility());
3795
                        JANUS_LOG(LOG_VERB, "\t   Janus API: %s\n", janus_transport->is_janus_api_enabled() ? "enabled" : "disabled");
3796
                        JANUS_LOG(LOG_VERB, "\t   Admin API: %s\n", janus_transport->is_admin_api_enabled() ? "enabled" : "disabled");
3797
                        janus_api_enabled = janus_api_enabled || janus_transport->is_janus_api_enabled();
3798
                        admin_api_enabled = admin_api_enabled || janus_transport->is_admin_api_enabled();
3799
                        if(transports == NULL)
3800
                                transports = g_hash_table_new(g_str_hash, g_str_equal);
3801
                        g_hash_table_insert(transports, (gpointer)janus_transport->get_package(), janus_transport);
3802
                        if(transports_so == NULL)
3803
                                transports_so = g_hash_table_new(g_str_hash, g_str_equal);
3804
                        g_hash_table_insert(transports_so, (gpointer)janus_transport->get_package(), transport);
3805
                }
3806
        }
3807
        closedir(dir);
3808
        if(disabled_transports != NULL)
3809
                g_strfreev(disabled_transports);
3810
        disabled_transports = NULL;
3811
        /* Make sure at least a Janus API transport is available */
3812
        if(!janus_api_enabled) {
3813
                JANUS_LOG(LOG_FATAL, "No Janus API transport is available... enable at least one and restart Janus\n");
3814
                exit(1);        /* FIXME Should we really give up? */
3815
        }
3816
        /* Make sure at least an admin API transport is available, if the auth mechanism is enabled */
3817
        if(!admin_api_enabled && janus_auth_is_enabled()) {
3818
                JANUS_LOG(LOG_FATAL, "No Admin/monitor transport is available, but the token based authentication mechanism is enabled... this will cause all requests to fail, giving up! If you want to use tokens, enable the Admin/monitor API and restart Janus\n");
3819
                exit(1);        /* FIXME Should we really give up? */
3820
        }
3821

    
3822
        /* Sessions */
3823
        sessions = g_hash_table_new_full(g_int64_hash, g_int64_equal, (GDestroyNotify)g_free, NULL);
3824
        old_sessions = g_hash_table_new_full(g_int64_hash, g_int64_equal, (GDestroyNotify)g_free, NULL);
3825
        janus_mutex_init(&sessions_mutex);
3826
        /* Start the sessions watchdog */
3827
        sessions_watchdog_context = g_main_context_new();
3828
        GMainLoop *watchdog_loop = g_main_loop_new(sessions_watchdog_context, FALSE);
3829
        error = NULL;
3830
        GThread *watchdog = g_thread_try_new("watchdog", &janus_sessions_watchdog, watchdog_loop, &error);
3831
        if(error != NULL) {
3832
                JANUS_LOG(LOG_FATAL, "Got error %d (%s) trying to start sessions watchdog...\n", error->code, error->message ? error->message : "??");
3833
                exit(1);
3834
        }
3835

    
3836
        /* Ok, Janus has started! Let the parent now about this if we're daemonizing */
3837
        if(daemonize) {
3838
                int code = 0;
3839
                ssize_t res = 0;
3840
                do {
3841
                        res = write(pipefd[1], &code, sizeof(int));
3842
                } while(res == -1 && errno == EINTR);
3843
        }
3844

    
3845
        while(!g_atomic_int_get(&stop)) {
3846
                /* Loop until we have to stop */
3847
                usleep(250000); /* A signal will cancel usleep() but not g_usleep() */
3848
        }
3849

    
3850
        /* Done */
3851
        JANUS_LOG(LOG_INFO, "Ending watchdog mainloop...\n");
3852
        g_main_loop_quit(watchdog_loop);
3853
        g_thread_join(watchdog);
3854
        watchdog = NULL;
3855
        g_main_loop_unref(watchdog_loop);
3856
        g_main_context_unref(sessions_watchdog_context);
3857

    
3858
        if(config)
3859
                janus_config_destroy(config);
3860

    
3861
        JANUS_LOG(LOG_INFO, "Closing transport plugins:\n");
3862
        if(transports != NULL) {
3863
                g_hash_table_foreach(transports, janus_transport_close, NULL);
3864
                g_hash_table_destroy(transports);
3865
        }
3866
        if(transports_so != NULL) {
3867
                g_hash_table_foreach(transports_so, janus_transportso_close, NULL);
3868
                g_hash_table_destroy(transports_so);
3869
        }
3870
        g_thread_pool_free(tasks, FALSE, FALSE);
3871

    
3872
        JANUS_LOG(LOG_INFO, "Destroying sessions...\n");
3873
        if(sessions != NULL)
3874
                g_hash_table_destroy(sessions);
3875
        if(old_sessions != NULL)
3876
                g_hash_table_destroy(old_sessions);
3877
        JANUS_LOG(LOG_INFO, "Freeing crypto resources...\n");
3878
        janus_dtls_srtp_cleanup();
3879
        EVP_cleanup();
3880
        ERR_free_strings();
3881
        JANUS_LOG(LOG_INFO, "Cleaning SDP structures...\n");
3882
        janus_sdp_deinit();
3883
#ifdef HAVE_SCTP
3884
        JANUS_LOG(LOG_INFO, "De-initializing SCTP...\n");
3885
        janus_sctp_deinit();
3886
#endif
3887
        janus_ice_deinit();
3888
        janus_auth_deinit();
3889

    
3890
        JANUS_LOG(LOG_INFO, "Closing plugins:\n");
3891
        if(plugins != NULL) {
3892
                g_hash_table_foreach(plugins, janus_plugin_close, NULL);
3893
                g_hash_table_destroy(plugins);
3894
        }
3895
        if(plugins_so != NULL) {
3896
                g_hash_table_foreach(plugins_so, janus_pluginso_close, NULL);
3897
                g_hash_table_destroy(plugins_so);
3898
        }
3899

    
3900
        JANUS_PRINT("Bye!\n");
3901

    
3902
        exit(0);
3903
}