Statistics
| Branch: | Revision:

janus-gateway / janus.c @ 887df302

History | View | Annotate | Download (164 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                        10
43
#define JANUS_VERSION_STRING        "0.1.0"
44

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

    
51

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

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

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

    
62

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

    
67

    
68
/* Certificates */
69
static char *server_pem = NULL;
70
gchar *janus_get_server_pem(void) {
71
        return server_pem;
72
}
73
static char *server_key = NULL;
74
gchar *janus_get_server_key(void) {
75
        return server_key;
76
}
77

    
78

    
79
/* API secrets */
80
static char *api_secret = NULL, *admin_api_secret = NULL;
81

    
82
/* JSON parameters */
83
static int janus_process_error_string(janus_request *request, uint64_t session_id, const char *transaction, gint error, gchar *error_string);
84

    
85
static struct janus_json_parameter incoming_request_parameters[] = {
86
        {"transaction", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
87
        {"janus", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
88
        {"id", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE}
89
};
90
static struct janus_json_parameter attach_parameters[] = {
91
        {"plugin", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
92
};
93
static struct janus_json_parameter jsep_parameters[] = {
94
        {"type", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
95
        {"trickle", JANUS_JSON_BOOL, 0},
96
        {"sdp", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
97
};
98

    
99
/* Admin/Monitor helpers */
100
json_t *janus_admin_stream_summary(janus_ice_stream *stream);
101
json_t *janus_admin_component_summary(janus_ice_component *component);
102

    
103

    
104
/* IP addresses */
105
static gchar local_ip[INET6_ADDRSTRLEN];
106
gchar *janus_get_local_ip(void) {
107
        return local_ip;
108
}
109
static gchar *public_ip = NULL;
110
gchar *janus_get_public_ip(void) {
111
        /* Fallback to the local IP, if we have no public one */
112
        return public_ip ? public_ip : local_ip;
113
}
114
void janus_set_public_ip(const char *ip) {
115
        /* once set do not override */
116
        if(ip == NULL || public_ip != NULL)
117
                return;
118
        public_ip = g_strdup(ip);
119
}
120
static volatile gint stop = 0;
121
gint janus_is_stopping(void) {
122
        return g_atomic_int_get(&stop);
123
}
124

    
125

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

    
210

    
211
/* Logging */
212
int janus_log_level = LOG_INFO;
213
gboolean janus_log_timestamps = FALSE;
214
gboolean janus_log_colors = FALSE;
215
int lock_debug = 0;
216

    
217

    
218
/*! \brief Signal handler (just used to intercept CTRL+C and SIGTERM) */
219
static void janus_handle_signal(int signum) {
220
        switch(g_atomic_int_get(&stop)) {
221
                case 0:
222
                        JANUS_PRINT("Stopping gateway, please wait...\n");
223
                        break;
224
                case 1:
225
                        JANUS_PRINT("In a hurry? I'm trying to free resources cleanly, here!\n");
226
                        break;
227
                default:
228
                        JANUS_PRINT("Ok, leaving immediately...\n");
229
                        break;
230
        }
231
        g_atomic_int_inc(&stop);
232
        if(g_atomic_int_get(&stop) > 2)
233
                exit(1);
234
}
235

    
236
/*! \brief Termination handler (atexit) */
237
static void janus_termination_handler(void) {
238
        /* Remove the PID file if we created it */
239
        janus_pidfile_remove();
240
        /* Close the logger */
241
        janus_log_destroy();
242
        /* If we're daemonizing, we send an error code to the parent */
243
        if(daemonize) {
244
                int code = 1;
245
                ssize_t res = 0;
246
                do {
247
                        res = write(pipefd[1], &code, sizeof(int));
248
                } while(res == -1 && errno == EINTR);
249
        }
250
}
251

    
252

    
253
/** @name Transport plugin callback interface
254
 * These are the callbacks implemented by the gateway core, as part of
255
 * the janus_transport_callbacks interface. Everything the transport
256
 * plugins send the gateway is handled here.
257
 */
258
///@{
259
void janus_transport_incoming_request(janus_transport *plugin, void *transport, void *request_id, gboolean admin, json_t *message, json_error_t *error);
260
void janus_transport_gone(janus_transport *plugin, void *transport);
261
gboolean janus_transport_is_api_secret_needed(janus_transport *plugin);
262
gboolean janus_transport_is_api_secret_valid(janus_transport *plugin, const char *apisecret);
263
gboolean janus_transport_is_auth_token_needed(janus_transport *plugin);
264
gboolean janus_transport_is_auth_token_valid(janus_transport *plugin, const char *token);
265

    
266
static janus_transport_callbacks janus_handler_transport =
267
        {
268
                .incoming_request = janus_transport_incoming_request,
269
                .transport_gone = janus_transport_gone,
270
                .is_api_secret_needed = janus_transport_is_api_secret_needed,
271
                .is_api_secret_valid = janus_transport_is_api_secret_valid,
272
                .is_auth_token_needed = janus_transport_is_auth_token_needed,
273
                .is_auth_token_valid = janus_transport_is_auth_token_valid,
274
        }; 
275
GThreadPool *tasks = NULL;
276
void janus_transport_task(gpointer data, gpointer user_data);
277
///@}
278

    
279

    
280
/** @name Plugin callback interface
281
 * These are the callbacks implemented by the gateway core, as part of
282
 * the janus_callbacks interface. Everything the plugins send the
283
 * gateway is handled here.
284
 */
285
///@{
286
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);
287
json_t *janus_plugin_handle_sdp(janus_plugin_session *plugin_session, janus_plugin *plugin, const char *sdp_type, const char *sdp);
288
void janus_plugin_relay_rtp(janus_plugin_session *plugin_session, int video, char *buf, int len);
289
void janus_plugin_relay_rtcp(janus_plugin_session *plugin_session, int video, char *buf, int len);
290
void janus_plugin_relay_data(janus_plugin_session *plugin_session, char *buf, int len);
291
void janus_plugin_close_pc(janus_plugin_session *plugin_session);
292
void janus_plugin_end_session(janus_plugin_session *plugin_session);
293
static janus_callbacks janus_handler_plugin =
294
        {
295
                .push_event = janus_plugin_push_event,
296
                .relay_rtp = janus_plugin_relay_rtp,
297
                .relay_rtcp = janus_plugin_relay_rtcp,
298
                .relay_data = janus_plugin_relay_data,
299
                .close_pc = janus_plugin_close_pc,
300
                .end_session = janus_plugin_end_session,
301
        }; 
302
///@}
303

    
304

    
305
/* Gateway Sessions */
306
static janus_mutex sessions_mutex;
307
static GHashTable *sessions = NULL, *old_sessions = NULL;
308
static GMainContext *sessions_watchdog_context = NULL;
309

    
310

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

    
313
static gboolean janus_cleanup_session(gpointer user_data) {
314
        janus_session *session = (janus_session *) user_data;
315

    
316
        JANUS_LOG(LOG_DBG, "Cleaning up session %"SCNu64"...\n", session->session_id);
317
        janus_session_destroy(session->session_id);
318

    
319
        return G_SOURCE_REMOVE;
320
}
321

    
322
static gboolean janus_check_sessions(gpointer user_data) {
323
        GMainContext *watchdog_context = (GMainContext *) user_data;
324
        janus_mutex_lock(&sessions_mutex);
325
        if(sessions && g_hash_table_size(sessions) > 0) {
326
                GHashTableIter iter;
327
                gpointer value;
328
                g_hash_table_iter_init(&iter, sessions);
329
                while (g_hash_table_iter_next(&iter, NULL, &value)) {
330
                        janus_session *session = (janus_session *) value;
331
                        if (!session || session->destroy) {
332
                                continue;
333
                        }
334
                        gint64 now = janus_get_monotonic_time();
335
                        if (now - session->last_activity >= SESSION_TIMEOUT * G_USEC_PER_SEC && !session->timeout) {
336
                                JANUS_LOG(LOG_INFO, "Timeout expired for session %"SCNu64"...\n", session->session_id);
337

    
338
                                /* Notify the transport */
339
                                if(session->source) {
340
                                        json_t *event = json_object();
341
                                        json_object_set_new(event, "janus", json_string("timeout"));
342
                                        json_object_set_new(event, "session_id", json_integer(session->session_id));
343
                                        /* Send this to the transport client */
344
                                        session->source->transport->send_message(session->source->instance, NULL, FALSE, event);
345
                                        /* Notify the transport plugin about the session timeout */
346
                                        session->source->transport->session_over(session->source->instance, session->session_id, TRUE);
347
                                }
348
                                
349
                                /* Mark the session as over, we'll deal with it later */
350
                                session->timeout = 1;
351
                                /* FIXME Is this safe? apparently it causes hash table errors on the console */
352
                                g_hash_table_iter_remove(&iter);
353
                                g_hash_table_insert(old_sessions, GUINT_TO_POINTER(session->session_id), session);
354

    
355
                                /* Schedule the session for deletion */
356
                                GSource *timeout_source = g_timeout_source_new_seconds(3);
357
                                g_source_set_callback(timeout_source, janus_cleanup_session, session, NULL);
358
                                g_source_attach(timeout_source, watchdog_context);
359
                                g_source_unref(timeout_source);
360
                        }
361
                }
362
        }
363
        janus_mutex_unlock(&sessions_mutex);
364

    
365
        return G_SOURCE_CONTINUE;
366
}
367

    
368
static gpointer janus_sessions_watchdog(gpointer user_data) {
369
        GMainLoop *loop = (GMainLoop *) user_data;
370
        GMainContext *watchdog_context = g_main_loop_get_context(loop);
371
        GSource *timeout_source;
372

    
373
        timeout_source = g_timeout_source_new_seconds(2);
374
        g_source_set_callback(timeout_source, janus_check_sessions, watchdog_context, NULL);
375
        g_source_attach(timeout_source, watchdog_context);
376
        g_source_unref(timeout_source);
377

    
378
        JANUS_LOG(LOG_INFO, "Sessions watchdog started\n");
379

    
380
        g_main_loop_run(loop);
381

    
382
        return NULL;
383
}
384

    
385
janus_session *janus_session_create(guint64 session_id) {
386
        if(session_id == 0) {
387
                while(session_id == 0) {
388
                        session_id = g_random_int();
389
                        if(janus_session_find(session_id) != NULL) {
390
                                /* Session ID already taken, try another one */
391
                                session_id = 0;
392
                        }
393
                }
394
        }
395
        JANUS_LOG(LOG_INFO, "Creating new session: %"SCNu64"\n", session_id);
396
        janus_session *session = (janus_session *)g_malloc0(sizeof(janus_session));
397
        if(session == NULL) {
398
                JANUS_LOG(LOG_FATAL, "Memory error!\n");
399
                return NULL;
400
        }
401
        session->session_id = session_id;
402
        session->source = NULL;
403
        session->destroy = 0;
404
        session->timeout = 0;
405
        session->last_activity = janus_get_monotonic_time();
406
        janus_mutex_init(&session->mutex);
407
        janus_mutex_lock(&sessions_mutex);
408
        g_hash_table_insert(sessions, GUINT_TO_POINTER(session_id), session);
409
        janus_mutex_unlock(&sessions_mutex);
410
        return session;
411
}
412

    
413
janus_session *janus_session_find(guint64 session_id) {
414
        janus_mutex_lock(&sessions_mutex);
415
        janus_session *session = g_hash_table_lookup(sessions, GUINT_TO_POINTER(session_id));
416
        janus_mutex_unlock(&sessions_mutex);
417
        return session;
418
}
419

    
420
janus_session *janus_session_find_destroyed(guint64 session_id) {
421
        janus_mutex_lock(&sessions_mutex);
422
        janus_session *session = g_hash_table_lookup(old_sessions, GUINT_TO_POINTER(session_id));
423
        janus_mutex_unlock(&sessions_mutex);
424
        return session;
425
}
426

    
427
void janus_session_notify_event(guint64 session_id, json_t *event) {
428
        janus_mutex_lock(&sessions_mutex);
429
        janus_session *session = sessions ? g_hash_table_lookup(sessions, GUINT_TO_POINTER(session_id)) : NULL;
430
        if(session != NULL && !session->destroy && session->source != NULL && session->source->transport != NULL) {
431
                janus_mutex_unlock(&sessions_mutex);
432
                /* Send this to the transport client */
433
                JANUS_LOG(LOG_HUGE, "Sending event to %s (%p)\n", session->source->transport->get_package(), session->source->instance);
434
                session->source->transport->send_message(session->source->instance, NULL, FALSE, event);
435
        } else {
436
                janus_mutex_unlock(&sessions_mutex);
437
                /* No transport, free the event */
438
                json_decref(event);
439
        }
440
}
441

    
442

    
443
/* Destroys a session but does not remove it from the sessions hash table. */
444
gint janus_session_destroy(guint64 session_id) {
445
        janus_session *session = janus_session_find_destroyed(session_id);
446
        if(session == NULL) {
447
                JANUS_LOG(LOG_ERR, "Couldn't find session to destroy: %"SCNu64"\n", session_id);
448
                return -1;
449
        }
450
        JANUS_LOG(LOG_VERB, "Destroying session %"SCNu64"\n", session_id);
451
        session->destroy = 1;
452
        if (session->ice_handles != NULL && g_hash_table_size(session->ice_handles) > 0) {
453
                GHashTableIter iter;
454
                gpointer value;
455
                /* Remove all handles */
456
                g_hash_table_iter_init(&iter, session->ice_handles);
457
                while (g_hash_table_iter_next(&iter, NULL, &value)) {
458
                        janus_ice_handle *handle = value;
459
                        if(!handle || g_atomic_int_get(&stop)) {
460
                                continue;
461
                        }
462
                        janus_ice_handle_destroy(session, handle->handle_id);
463
                        g_hash_table_iter_remove(&iter);
464
                }
465
        }
466

    
467
        /* FIXME Actually destroy session */
468
        janus_session_free(session);
469

    
470
        return 0;
471
}
472

    
473
void janus_session_free(janus_session *session) {
474
        if(session == NULL)
475
                return;
476
        janus_mutex_lock(&session->mutex);
477
        if(session->ice_handles != NULL) {
478
                g_hash_table_destroy(session->ice_handles);
479
                session->ice_handles = NULL;
480
        }
481
        if(session->source != NULL) {
482
                janus_request_destroy(session->source);
483
                session->source = NULL;
484
        }
485
        janus_mutex_unlock(&session->mutex);
486
        g_free(session);
487
        session = NULL;
488
}
489

    
490

    
491
/* Requests management */
492
janus_request *janus_request_new(janus_transport *transport, void *instance, void *request_id, gboolean admin, json_t *message) {
493
        janus_request *request = (janus_request *)g_malloc0(sizeof(janus_request));
494
        request->transport = transport;
495
        request->instance = instance;
496
        request->request_id = request_id;
497
        request->admin = admin;
498
        request->message = message;
499
        return request;
500
}
501

    
502
void janus_request_destroy(janus_request *request) {
503
        if(request == NULL)
504
                return;
505
        request->transport = NULL;
506
        request->instance = NULL;
507
        request->request_id = NULL;
508
        if(request->message)
509
                json_decref(request->message);
510
        request->message = NULL;
511
        g_free(request);
512
}
513

    
514
int janus_process_incoming_request(janus_request *request) {
515
        int ret = -1;
516
        if(request == NULL) {
517
                JANUS_LOG(LOG_ERR, "Missing request or payload to process, giving up...\n");
518
                return ret;
519
        }
520
        int error_code = 0;
521
        char error_cause[100];
522
        json_t *root = request->message;
523
        /* Ok, let's start with the ids */
524
        guint64 session_id = 0, handle_id = 0;
525
        json_t *s = json_object_get(root, "session_id");
526
        if(s && json_is_integer(s))
527
                session_id = json_integer_value(s);
528
        json_t *h = json_object_get(root, "handle_id");
529
        if(h && json_is_integer(h))
530
                handle_id = json_integer_value(h);
531

    
532
        /* Get transaction and message request */
533
        JANUS_VALIDATE_JSON_OBJECT(root, incoming_request_parameters,
534
                error_code, error_cause, FALSE,
535
                JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
536
        if(error_code != 0) {
537
                ret = janus_process_error_string(request, session_id, NULL, error_code, error_cause);
538
                goto jsondone;
539
        }
540
        json_t *transaction = json_object_get(root, "transaction");
541
        const gchar *transaction_text = json_string_value(transaction);
542
        json_t *message = json_object_get(root, "janus");
543
        const gchar *message_text = json_string_value(message);
544
        
545
        if(session_id == 0 && handle_id == 0) {
546
                /* Can only be a 'Create new session', a 'Get info' or a 'Ping/Pong' request */
547
                if(!strcasecmp(message_text, "info")) {
548
                        ret = janus_process_success(request, janus_info(transaction_text));
549
                        goto jsondone;
550
                }
551
                if(!strcasecmp(message_text, "ping")) {
552
                        /* Prepare JSON reply */
553
                        json_t *reply = json_object();
554
                        json_object_set_new(reply, "janus", json_string("pong"));
555
                        json_object_set_new(reply, "transaction", json_string(transaction_text));
556
                        ret = janus_process_success(request, reply);
557
                        goto jsondone;
558
                }
559
                if(strcasecmp(message_text, "create")) {
560
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
561
                        goto jsondone;
562
                }
563
                /* Any secret/token to check? */
564
                gboolean secret_authorized = FALSE, token_authorized = FALSE;
565
                if(api_secret == NULL && !janus_auth_is_enabled()) {
566
                        /* Nothing to check */
567
                        secret_authorized = TRUE;
568
                        token_authorized = TRUE;
569
                } else {
570
                        if(api_secret != NULL) {
571
                                /* There's an API secret, check that the client provided it */
572
                                json_t *secret = json_object_get(root, "apisecret");
573
                                if(secret && json_is_string(secret) && janus_strcmp_const_time(json_string_value(secret), api_secret)) {
574
                                        secret_authorized = TRUE;
575
                                }
576
                        }
577
                        if(janus_auth_is_enabled()) {
578
                                /* The token based authentication mechanism is enabled, check that the client provided it */
579
                                json_t *token = json_object_get(root, "token");
580
                                if(token && json_is_string(token) && janus_auth_check_token(json_string_value(token))) {
581
                                        token_authorized = TRUE;
582
                                }
583
                        }
584
                        /* We consider a request authorized if either the proper API secret or a valid token has been provided */
585
                        if(!secret_authorized && !token_authorized) {
586
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED, NULL);
587
                                goto jsondone;
588
                        }
589
                }
590
                session_id = 0;
591
                json_t *id = json_object_get(root, "id");
592
                if(id != NULL) {
593
                        /* The application provided the session ID to use */
594
                        session_id = json_integer_value(id);
595
                        if(session_id > 0 && janus_session_find(session_id) != NULL) {
596
                                /* Session ID already taken */
597
                                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_CONFLICT, "Session ID already in use");
598
                                goto jsondone;
599
                        }
600
                }
601
                /* Handle it */
602
                janus_session *session = janus_session_create(session_id);
603
                if(session == NULL) {
604
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Memory error");
605
                        goto jsondone;
606
                }
607
                session_id = session->session_id;
608
                /* Take note of the request source that originated this session (HTTP, WebSockets, RabbitMQ?) */
609
                session->source = janus_request_new(request->transport, request->instance, NULL, FALSE, NULL);
610
                /* Notify the source that a new session has been created */
611
                request->transport->session_created(request->instance, session->session_id);
612
                /* Prepare JSON reply */
613
                json_t *reply = json_object();
614
                json_object_set_new(reply, "janus", json_string("success"));
615
                json_object_set_new(reply, "transaction", json_string(transaction_text));
616
                json_t *data = json_object();
617
                json_object_set_new(data, "id", json_integer(session_id));
618
                json_object_set_new(reply, "data", data);
619
                /* Send the success reply */
620
                ret = janus_process_success(request, reply);
621
                goto jsondone;
622
        }
623
        if(session_id < 1) {
624
                JANUS_LOG(LOG_ERR, "Invalid session\n");
625
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, NULL);
626
                goto jsondone;
627
        }
628
        if(h && handle_id < 1) {
629
                JANUS_LOG(LOG_ERR, "Invalid handle\n");
630
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, NULL);
631
                goto jsondone;
632
        }
633

    
634
        /* Go on with the processing */
635
        gboolean secret_authorized = FALSE, token_authorized = FALSE;
636
        if(api_secret == NULL && !janus_auth_is_enabled()) {
637
                /* Nothing to check */
638
                secret_authorized = TRUE;
639
                token_authorized = TRUE;
640
        } else {
641
                if(api_secret != NULL) {
642
                        /* There's an API secret, check that the client provided it */
643
                        json_t *secret = json_object_get(root, "apisecret");
644
                        if(secret && json_is_string(secret) && janus_strcmp_const_time(json_string_value(secret), api_secret)) {
645
                                secret_authorized = TRUE;
646
                        }
647
                }
648
                if(janus_auth_is_enabled()) {
649
                        /* The token based authentication mechanism is enabled, check that the client provided it */
650
                        json_t *token = json_object_get(root, "token");
651
                        if(token && json_is_string(token) && janus_auth_check_token(json_string_value(token))) {
652
                                token_authorized = TRUE;
653
                        }
654
                }
655
                /* We consider a request authorized if either the proper API secret or a valid token has been provided */
656
                if(!secret_authorized && !token_authorized) {
657
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED, NULL);
658
                        goto jsondone;
659
                }
660
        }
661

    
662
        /* If we got here, make sure we have a session (and/or a handle) */
663
        janus_session *session = janus_session_find(session_id);
664
        if(!session) {
665
                JANUS_LOG(LOG_ERR, "Couldn't find any session %"SCNu64"...\n", session_id);
666
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, "No such session %"SCNu64"", session_id);
667
                goto jsondone;
668
        }
669
        /* Update the last activity timer */
670
        session->last_activity = janus_get_monotonic_time();
671
        janus_ice_handle *handle = NULL;
672
        if(handle_id > 0) {
673
                handle = janus_ice_handle_find(session, handle_id);
674
                if(!handle) {
675
                        JANUS_LOG(LOG_ERR, "Couldn't find any handle %"SCNu64" in session %"SCNu64"...\n", handle_id, session_id);
676
                        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);
677
                        goto jsondone;
678
                }
679
        }
680

    
681
        /* What is this? */
682
        if(!strcasecmp(message_text, "keepalive")) {
683
                /* Just a keep-alive message, reply with an ack */
684
                JANUS_LOG(LOG_VERB, "Got a keep-alive on session %"SCNu64"\n", session_id);
685
                json_t *reply = json_object();
686
                json_object_set_new(reply, "janus", json_string("ack"));
687
                json_object_set_new(reply, "session_id", json_integer(session_id));
688
                json_object_set_new(reply, "transaction", json_string(transaction_text));
689
                /* Send the success reply */
690
                ret = janus_process_success(request, reply);
691
        } else if(!strcasecmp(message_text, "attach")) {
692
                if(handle != NULL) {
693
                        /* Attach is a session-level command */
694
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
695
                        goto jsondone;
696
                }
697
                JANUS_VALIDATE_JSON_OBJECT(root, attach_parameters,
698
                        error_code, error_cause, FALSE,
699
                        JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
700
                if(error_code != 0) {
701
                        ret = janus_process_error_string(request, session_id, NULL, error_code, error_cause);
702
                        goto jsondone;
703
                }
704
                json_t *plugin = json_object_get(root, "plugin");
705
                const gchar *plugin_text = json_string_value(plugin);
706
                janus_plugin *plugin_t = janus_plugin_find(plugin_text);
707
                if(plugin_t == NULL) {
708
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_NOT_FOUND, "No such plugin '%s'", plugin_text);
709
                        goto jsondone;
710
                }
711
                /* If the auth token mechanism is enabled, we should check if this token can access this plugin */
712
                if(janus_auth_is_enabled()) {
713
                        json_t *token = json_object_get(root, "token");
714
                        if(token != NULL) {
715
                                const char *token_value = json_string_value(token);
716
                                if(token_value && !janus_auth_check_plugin(token_value, plugin_t)) {
717
                                        JANUS_LOG(LOG_ERR, "Token '%s' can't access plugin '%s'\n", token_value, plugin_text);
718
                                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED_PLUGIN, "Provided token can't access plugin '%s'", plugin_text);
719
                                        goto jsondone;
720
                                }
721
                        }
722
                }
723
                /* Create handle */
724
                handle = janus_ice_handle_create(session);
725
                if(handle == NULL) {
726
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Memory error");
727
                        goto jsondone;
728
                }
729
                handle_id = handle->handle_id;
730
                /* Attach to the plugin */
731
                int error = 0;
732
                if((error = janus_ice_handle_attach_plugin(session, handle_id, plugin_t)) != 0) {
733
                        /* TODO Make error struct to pass verbose information */
734
                        janus_ice_handle_destroy(session, handle_id);
735
                        janus_mutex_lock(&session->mutex);
736
                        g_hash_table_remove(session->ice_handles, GUINT_TO_POINTER(handle_id));
737
                        janus_mutex_unlock(&session->mutex);
738

    
739
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_ATTACH, "Couldn't attach to plugin: error '%d'", error);
740
                        goto jsondone;
741
                }
742
                /* Prepare JSON reply */
743
                json_t *reply = json_object();
744
                json_object_set_new(reply, "janus", json_string("success"));
745
                json_object_set_new(reply, "session_id", json_integer(session_id));
746
                json_object_set_new(reply, "transaction", json_string(transaction_text));
747
                json_t *data = json_object();
748
                json_object_set_new(data, "id", json_integer(handle_id));
749
                json_object_set_new(reply, "data", data);
750
                /* Send the success reply */
751
                ret = janus_process_success(request, reply);
752
        } else if(!strcasecmp(message_text, "destroy")) {
753
                if(handle != NULL) {
754
                        /* Query is a session-level command */
755
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
756
                        goto jsondone;
757
                }
758
                /* Schedule the session for deletion */
759
                session->destroy = 1;
760
                janus_mutex_lock(&sessions_mutex);
761
                g_hash_table_remove(sessions, GUINT_TO_POINTER(session->session_id));
762
                g_hash_table_insert(old_sessions, GUINT_TO_POINTER(session->session_id), session);
763
                GSource *timeout_source = g_timeout_source_new_seconds(3);
764
                g_source_set_callback(timeout_source, janus_cleanup_session, session, NULL);
765
                g_source_attach(timeout_source, sessions_watchdog_context);
766
                g_source_unref(timeout_source);
767
                janus_mutex_unlock(&sessions_mutex);
768
                /* Notify the source that the session has been destroyed */
769
                if(session->source && session->source->transport)
770
                        session->source->transport->session_over(session->source->instance, session->session_id, FALSE);
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
                /* Send the success reply */
778
                ret = janus_process_success(request, reply);
779
        } else if(!strcasecmp(message_text, "detach")) {
780
                if(handle == NULL) {
781
                        /* Query is an handle-level command */
782
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
783
                        goto jsondone;
784
                }
785
                if(handle->app == NULL || handle->app_handle == NULL) {
786
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_DETACH, "No plugin to detach from");
787
                        goto jsondone;
788
                }
789
                int error = janus_ice_handle_destroy(session, handle_id);
790
                janus_mutex_lock(&session->mutex);
791
                g_hash_table_remove(session->ice_handles, GUINT_TO_POINTER(handle_id));
792
                janus_mutex_unlock(&session->mutex);
793

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

    
1199
                /* Make sure the app handle is still valid */
1200
                if(handle->app == NULL || handle->app_handle == NULL || !janus_plugin_session_is_alive(handle->app_handle)) {
1201
                        ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "No plugin to handle this message");
1202
                        if(jsep_type)
1203
                                g_free(jsep_type);
1204
                        if(jsep_sdp_stripped)
1205
                                g_free(jsep_sdp_stripped);
1206
                        janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1207
                        goto jsondone;
1208
                }
1209

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

    
1352
trickledone:
1353
                janus_mutex_unlock(&handle->mutex);
1354
                /* We reply right away, not to block the web server... */
1355
                json_t *reply = json_object();
1356
                json_object_set_new(reply, "janus", json_string("ack"));
1357
                json_object_set_new(reply, "session_id", json_integer(session_id));
1358
                json_object_set_new(reply, "transaction", json_string(transaction_text));
1359
                /* Send the success reply */
1360
                ret = janus_process_success(request, reply);
1361
        } else {
1362
                ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN_REQUEST, "Unknown request '%s'", message_text);
1363
        }
1364

    
1365
jsondone:
1366
        /* Done processing */
1367
        return ret;
1368
}
1369

    
1370
/* Admin/monitor WebServer requests handler */
1371
int janus_process_incoming_admin_request(janus_request *request) {
1372
        int ret = -1;
1373
        if(request == NULL) {
1374
                JANUS_LOG(LOG_ERR, "Missing request or payload to process, giving up...\n");
1375
                return ret;
1376
        }
1377
        json_t *root = request->message;
1378
        /* Ok, let's start with the ids */
1379
        guint64 session_id = 0, handle_id = 0;
1380
        json_t *s = json_object_get(root, "session_id");
1381
        if(s && json_is_integer(s))
1382
                session_id = json_integer_value(s);
1383
        json_t *h = json_object_get(root, "handle_id");
1384
        if(h && json_is_integer(h))
1385
                handle_id = json_integer_value(h);
1386

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

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

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

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

    
2141
jsondone:
2142
        /* Done processing */
2143
        return ret;
2144
}
2145

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

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

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

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

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

    
2349

    
2350
/* Transports */
2351
void janus_transport_close(gpointer key, gpointer value, gpointer user_data) {
2352
        janus_transport *transport = (janus_transport *)value;
2353
        if(!transport)
2354
                return;
2355
        transport->destroy();
2356
}
2357

    
2358
void janus_transportso_close(gpointer key, gpointer value, gpointer user_data) {
2359
        void *transport = (janus_transport *)value;
2360
        if(!transport)
2361
                return;
2362
        //~ dlclose(transport);
2363
}
2364

    
2365
/* Transport callback interface */
2366
void janus_transport_incoming_request(janus_transport *plugin, void *transport, void *request_id, gboolean admin, json_t *message, json_error_t *error) {
2367
        JANUS_LOG(LOG_VERB, "Got %s API request from %s (%p)\n", admin ? "an admin" : "a Janus", plugin->get_package(), transport);
2368
        /* Create a janus_request instance to handle the request */
2369
        janus_request *request = janus_request_new(plugin, transport, request_id, admin, message);
2370
        GError *tperror = NULL;
2371
        g_thread_pool_push(tasks, request, &tperror);
2372
        if(tperror != NULL) {
2373
                /* Something went wrong... */
2374
                JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to push task in thread pool...\n", tperror->code, tperror->message ? tperror->message : "??");
2375
                json_t *transaction = json_object_get(message, "transaction");
2376
                const char *transaction_text = json_is_string(transaction) ? json_string_value(transaction) : NULL;
2377
                janus_process_error(request, 0, transaction_text, JANUS_ERROR_UNKNOWN, "Thread pool error");
2378
                janus_request_destroy(request);
2379
        }
2380
}
2381

    
2382
void janus_transport_gone(janus_transport *plugin, void *transport) {
2383
        /* Get rid of sessions this transport was handling */
2384
        JANUS_LOG(LOG_VERB, "A %s transport instance has gone away (%p)\n", plugin->get_package(), transport);
2385
        janus_mutex_lock(&sessions_mutex);
2386
        if(sessions && g_hash_table_size(sessions) > 0) {
2387
                GHashTableIter iter;
2388
                gpointer value;
2389
                g_hash_table_iter_init(&iter, sessions);
2390
                while(g_hash_table_iter_next(&iter, NULL, &value)) {
2391
                        janus_session *session = (janus_session *) value;
2392
                        if(!session || session->destroy || session->timeout || session->last_activity == 0)
2393
                                continue;
2394
                        if(session->source && session->source->instance == transport) {
2395
                                JANUS_LOG(LOG_VERB, "  -- Marking Session %"SCNu64" as over\n", session->session_id);
2396
                                session->last_activity = 0;        /* This will trigger a timeout */
2397
                        }
2398
                }
2399
        }
2400
        janus_mutex_unlock(&sessions_mutex);
2401
}
2402

    
2403
gboolean janus_transport_is_api_secret_needed(janus_transport *plugin) {
2404
        return api_secret != NULL;
2405
}
2406

    
2407
gboolean janus_transport_is_api_secret_valid(janus_transport *plugin, const char *apisecret) {
2408
        if(api_secret == NULL)
2409
                return TRUE;
2410
        return apisecret && janus_strcmp_const_time(apisecret, api_secret);
2411
}
2412

    
2413
gboolean janus_transport_is_auth_token_needed(janus_transport *plugin) {
2414
        return janus_auth_is_enabled();
2415
}
2416

    
2417
gboolean janus_transport_is_auth_token_valid(janus_transport *plugin, const char *token) {
2418
        if(!janus_auth_is_enabled())
2419
                return TRUE;
2420
        return token && janus_auth_check_token(token);
2421
}
2422

    
2423
void janus_transport_task(gpointer data, gpointer user_data) {
2424
        JANUS_LOG(LOG_VERB, "Transport task pool, serving request\n");
2425
        janus_request *request = (janus_request *)data;
2426
        if(request == NULL) {
2427
                JANUS_LOG(LOG_ERR, "Missing request\n");
2428
                return;
2429
        }
2430
        if(!request->admin)
2431
                janus_process_incoming_request(request);
2432
        else
2433
                janus_process_incoming_admin_request(request);
2434
        /* Done */
2435
        janus_request_destroy(request);
2436
}
2437

    
2438

    
2439
/* Plugins */
2440
void janus_plugin_close(gpointer key, gpointer value, gpointer user_data) {
2441
        janus_plugin *plugin = (janus_plugin *)value;
2442
        if(!plugin)
2443
                return;
2444
        plugin->destroy();
2445
}
2446

    
2447
void janus_pluginso_close(gpointer key, gpointer value, gpointer user_data) {
2448
        void *plugin = (janus_plugin *)value;
2449
        if(!plugin)
2450
                return;
2451
        //~ dlclose(plugin);
2452
}
2453

    
2454
janus_plugin *janus_plugin_find(const gchar *package) {
2455
        if(package != NULL && plugins != NULL)        /* FIXME Do we need to fix the key pointer? */
2456
                return g_hash_table_lookup(plugins, package);
2457
        return NULL;
2458
}
2459

    
2460

    
2461
/* Plugin callback interface */
2462
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) {
2463
        if(!plugin || !message)
2464
                return -1;
2465
        if(!plugin_session || plugin_session < (janus_plugin_session *)0x1000 ||
2466
                        !janus_plugin_session_is_alive(plugin_session) || plugin_session->stopped)
2467
                return -2;
2468
        janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
2469
        if(!ice_handle || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP))
2470
                return JANUS_ERROR_SESSION_NOT_FOUND;
2471
        janus_session *session = ice_handle->session;
2472
        if(!session || session->destroy)
2473
                return JANUS_ERROR_SESSION_NOT_FOUND;
2474
        /* Make sure this is JSON */
2475
        json_error_t error;
2476
        json_t *plugin_event = json_loads(message, 0, &error);
2477
        if(!plugin_event) {
2478
                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (JSON error: on line %d: %s)\n", ice_handle->handle_id, error.line, error.text);
2479
                return JANUS_ERROR_INVALID_JSON;
2480
        }
2481
        if(!json_is_object(plugin_event)) {
2482
                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (JSON error: not an object)\n", ice_handle->handle_id);
2483
                return JANUS_ERROR_INVALID_JSON_OBJECT;
2484
        }
2485
        /* Attach JSEP if possible? */
2486
        json_t *jsep = NULL;
2487
        if(sdp_type != NULL && sdp != NULL) {
2488
                jsep = janus_plugin_handle_sdp(plugin_session, plugin, sdp_type, sdp);
2489
                if(jsep == NULL) {
2490
                        if(ice_handle == NULL || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2491
                                        || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)) {
2492
                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (handle not available anymore or negotiation stopped)\n", ice_handle->handle_id);
2493
                                return JANUS_ERROR_HANDLE_NOT_FOUND;
2494
                        } else {
2495
                                JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (JSON error: problem with the SDP)\n", ice_handle->handle_id);
2496
                                return JANUS_ERROR_JSEP_INVALID_SDP;
2497
                        }
2498
                }
2499
        }
2500
        /* Prepare JSON event */
2501
        json_t *event = json_object();
2502
        json_object_set_new(event, "janus", json_string("event"));
2503
        json_object_set_new(event, "session_id", json_integer(session->session_id));
2504
        json_object_set_new(event, "sender", json_integer(ice_handle->handle_id));
2505
        if(transaction != NULL)
2506
                json_object_set_new(event, "transaction", json_string(transaction));
2507
        json_t *plugin_data = json_object();
2508
        json_object_set_new(plugin_data, "plugin", json_string(plugin->get_package()));
2509
        json_object_set_new(plugin_data, "data", plugin_event);
2510
        json_object_set_new(event, "plugindata", plugin_data);
2511
        if(jsep != NULL)
2512
                json_object_set_new(event, "jsep", jsep);
2513
        /* Send the event */
2514
        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Sending event to transport...\n", ice_handle->handle_id);
2515
        janus_session_notify_event(session->session_id, event);
2516
        
2517
        return JANUS_OK;
2518
}
2519

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

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

    
2848
void janus_plugin_relay_rtp(janus_plugin_session *plugin_session, int video, char *buf, int len) {
2849
        if((plugin_session < (janus_plugin_session *)0x1000) || plugin_session->stopped || buf == NULL || len < 1)
2850
                return;
2851
        janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
2852
        if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2853
                        || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
2854
                return;
2855
        janus_ice_relay_rtp(handle, video, buf, len);
2856
}
2857

    
2858
void janus_plugin_relay_rtcp(janus_plugin_session *plugin_session, int video, char *buf, int len) {
2859
        if((plugin_session < (janus_plugin_session *)0x1000) || plugin_session->stopped || buf == NULL || len < 1)
2860
                return;
2861
        janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
2862
        if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2863
                        || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
2864
                return;
2865
        janus_ice_relay_rtcp(handle, video, buf, len);
2866
}
2867

    
2868
void janus_plugin_relay_data(janus_plugin_session *plugin_session, char *buf, int len) {
2869
        if((plugin_session < (janus_plugin_session *)0x1000) || plugin_session->stopped || buf == NULL || len < 1)
2870
                return;
2871
        janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
2872
        if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2873
                        || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
2874
                return;
2875
#ifdef HAVE_SCTP
2876
        janus_ice_relay_data(handle, buf, len);
2877
#else
2878
        JANUS_LOG(LOG_WARN, "Asked to relay data, but Data Channels support has not been compiled...\n");
2879
#endif
2880
}
2881

    
2882
void janus_plugin_close_pc(janus_plugin_session *plugin_session) {
2883
        /* A plugin asked to get rid of a PeerConnection */
2884
        if((plugin_session < (janus_plugin_session *)0x1000) || !janus_plugin_session_is_alive(plugin_session) || plugin_session->stopped)
2885
                return;
2886
        janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
2887
        if(!ice_handle)
2888
                return;
2889
        if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
2890
                        || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
2891
                return;
2892
        janus_session *session = (janus_session *)ice_handle->session;
2893
        if(!session)
2894
                return;
2895
                
2896
        JANUS_LOG(LOG_VERB, "[%"SCNu64"] Plugin asked to hangup PeerConnection: sending alert\n", ice_handle->handle_id);
2897
        /* Send an alert on all the DTLS connections */
2898
        janus_ice_webrtc_hangup(ice_handle);
2899
}
2900

    
2901
void janus_plugin_end_session(janus_plugin_session *plugin_session) {
2902
        /* A plugin asked to get rid of a handle */
2903
        if((plugin_session < (janus_plugin_session *)0x1000) || !janus_plugin_session_is_alive(plugin_session) || plugin_session->stopped)
2904
                return;
2905
        janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
2906
        if(!ice_handle)
2907
                return;
2908
        janus_session *session = (janus_session *)ice_handle->session;
2909
        if(!session)
2910
                return;
2911
        /* Destroy the handle */
2912
        janus_ice_handle_destroy(session, ice_handle->handle_id);
2913
        janus_mutex_lock(&session->mutex);
2914
        g_hash_table_remove(session->ice_handles, GUINT_TO_POINTER(ice_handle->handle_id));
2915
        janus_mutex_unlock(&session->mutex);
2916
}
2917

    
2918

    
2919
static void janus_detect_local_ip(gchar *buf, size_t buflen) {
2920
        JANUS_LOG(LOG_VERB, "Autodetecting local IP...\n");
2921
        struct sockaddr_in addr;
2922
        socklen_t len;
2923
        int fd = socket(AF_INET, SOCK_DGRAM, 0);
2924
        if (fd == -1)
2925
                goto error;
2926
        addr.sin_family = AF_INET;
2927
        addr.sin_port = htons(1);
2928
        inet_pton(AF_INET, "1.2.3.4", &addr.sin_addr.s_addr);
2929
        if (connect(fd, (const struct sockaddr*) &addr, sizeof(addr)) < 0)
2930
                goto error;
2931
        len = sizeof(addr);
2932
        if (getsockname(fd, (struct sockaddr*) &addr, &len) < 0)
2933
                goto error;
2934
        if (getnameinfo((const struct sockaddr*) &addr, sizeof(addr),
2935
                        buf, buflen,
2936
                        NULL, 0, NI_NUMERICHOST) != 0)
2937
                goto error;
2938
        close(fd);
2939
        return;
2940
error:
2941
        if (fd != -1)
2942
                close(fd);
2943
        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");
2944
        g_strlcpy(buf, "127.0.0.1", buflen);
2945
}
2946

    
2947

    
2948
/* Main */
2949
gint main(int argc, char *argv[])
2950
{
2951
        /* Core dumps may be disallowed by parent of this process; change that */
2952
        struct rlimit core_limits;
2953
        core_limits.rlim_cur = core_limits.rlim_max = RLIM_INFINITY;
2954
        setrlimit(RLIMIT_CORE, &core_limits);
2955

    
2956
        struct gengetopt_args_info args_info;
2957
        /* Let's call our cmdline parser */
2958
        if(cmdline_parser(argc, argv, &args_info) != 0)
2959
                exit(1);
2960
        
2961
        /* Any configuration to open? */
2962
        if(args_info.config_given) {
2963
                config_file = g_strdup(args_info.config_arg);
2964
        }
2965
        if(args_info.configs_folder_given) {
2966
                configs_folder = g_strdup(args_info.configs_folder_arg);
2967
        } else {
2968
                configs_folder = g_strdup (CONFDIR);
2969
        }
2970
        if(config_file == NULL) {
2971
                char file[255];
2972
                g_snprintf(file, 255, "%s/janus.cfg", configs_folder);
2973
                config_file = g_strdup(file);
2974
        }
2975
        if((config = janus_config_parse(config_file)) == NULL) {
2976
                if(args_info.config_given) {
2977
                        /* We only give up if the configuration file was explicitly provided */
2978
                        g_print("Error reading configuration from %s\n", config_file);
2979
                        exit(1);
2980
                }
2981
                g_print("Error reading/parsing the configuration file, going on with the defaults and the command line arguments\n");
2982
                config = janus_config_create("janus.cfg");
2983
                if(config == NULL) {
2984
                        /* If we can't even create an empty configuration, something's definitely wrong */
2985
                        exit(1);
2986
                }
2987
        }
2988

    
2989
        /* Check if we need to log to console and/or file */
2990
        gboolean use_stdout = TRUE;
2991
        if(args_info.disable_stdout_given) {
2992
                use_stdout = FALSE;
2993
                janus_config_add_item(config, "general", "log_to_stdout", "no");
2994
        } else {
2995
                /* Check if the configuration file is saying anything about this */
2996
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "log_to_stdout");
2997
                if(item && item->value && !janus_is_true(item->value))
2998
                        use_stdout = FALSE;
2999
        }
3000
        const char *logfile = NULL;
3001
        if(args_info.log_file_given) {
3002
                logfile = args_info.log_file_arg;
3003
                janus_config_add_item(config, "general", "log_to_file", "no");
3004
        } else {
3005
                /* Check if the configuration file is saying anything about this */
3006
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "log_to_file");
3007
                if(item && item->value)
3008
                        logfile = item->value;
3009
        }
3010

    
3011
        /* Check if we're going to daemonize Janus */
3012
        if(args_info.daemon_given) {
3013
                daemonize = TRUE;
3014
                janus_config_add_item(config, "general", "daemonize", "yes");
3015
        } else {
3016
                /* Check if the configuration file is saying anything about this */
3017
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "daemonize");
3018
                if(item && item->value && janus_is_true(item->value))
3019
                        daemonize = TRUE;
3020
        }
3021
        /* If we're going to daemonize, make sure logging to stdout is disabled and a log file has been specified */
3022
        if(daemonize && use_stdout) {
3023
                use_stdout = FALSE;
3024
        }
3025
        if(daemonize && logfile == NULL) {
3026
                g_print("Running Janus as a daemon but no log file provided, giving up...\n");
3027
                exit(1);
3028
        }
3029
        /* Daemonize now, if we need to */
3030
        if(daemonize) {
3031
                g_print("Running Janus as a daemon\n");
3032

    
3033
                /* Create a pipe for parent<->child communication during the startup phase */
3034
                if(pipe(pipefd) == -1) {
3035
                        g_print("pipe error!\n");
3036
                        exit(1);
3037
                }
3038

    
3039
                /* Fork off the parent process */
3040
                pid_t pid = fork();
3041
                if(pid < 0) {
3042
                        g_print("Fork error!\n");
3043
                        exit(1);
3044
                }
3045
                if(pid > 0) {
3046
                        /* Ok, we're the parent: let's wait for the child to tell us everything started fine */
3047
                        close(pipefd[1]);
3048
                        int code = -1;
3049
                        struct pollfd pollfds;
3050

    
3051
                        while(code < 0) {
3052
                                pollfds.fd = pipefd[0];
3053
                                pollfds.events = POLLIN;
3054
                                int res = poll(&pollfds, 1, -1);
3055
                                if(res < 0)
3056
                                        break;
3057
                                if(res == 0)
3058
                                        continue;
3059
                                if(pollfds.revents & POLLERR || pollfds.revents & POLLHUP)
3060
                                        break;
3061
                                if(pollfds.revents & POLLIN) {
3062
                                        read(pipefd[0], &code, sizeof(int));
3063
                                        break;
3064
                                }
3065
                        }
3066
                        if(code < 0)
3067
                                code = 1;
3068

    
3069
                        /* Leave the parent and return the exit code we received from the child */
3070
                        if(code)
3071
                                g_print("Error launching Janus (error code %d), check the logs for more details\n", code);
3072
                        exit(code);
3073
                }
3074
                /* Change the file mode mask */
3075
                umask(0);
3076

    
3077
                /* Create a new SID for the child process */
3078
                pid_t sid = setsid();
3079
                if(sid < 0) {
3080
                        g_print("Error setting SID!\n");
3081
                        exit(1);
3082
                }
3083
                /* Change the current working directory */
3084
                if((chdir("/")) < 0) {
3085
                        g_print("Error changing the current working directory!\n");
3086
                        exit(1);
3087
                }
3088
                /* We close stdin/stdout/stderr when initializing the logger */
3089
        }
3090

    
3091
        /* Initialize logger */
3092
        if(janus_log_init(daemonize, use_stdout, logfile) < 0)
3093
                exit(1);
3094

    
3095
        JANUS_PRINT("---------------------------------------------------\n");
3096
        JANUS_PRINT("  Starting Meetecho Janus (WebRTC Gateway) v%s\n", JANUS_VERSION_STRING);
3097
        JANUS_PRINT("---------------------------------------------------\n\n");
3098

    
3099
        /* Handle SIGINT (CTRL-C), SIGTERM (from service managers) */
3100
        signal(SIGINT, janus_handle_signal);
3101
        signal(SIGTERM, janus_handle_signal);
3102
        atexit(janus_termination_handler);
3103

    
3104
        /* Setup Glib */
3105
#if !GLIB_CHECK_VERSION(2, 36, 0)
3106
        g_type_init();
3107
#endif
3108

    
3109
        /* Logging level: default is info and no timestamps */
3110
        janus_log_level = LOG_INFO;
3111
        janus_log_timestamps = FALSE;
3112
        janus_log_colors = TRUE;
3113
        if(args_info.debug_level_given) {
3114
                if(args_info.debug_level_arg < LOG_NONE)
3115
                        args_info.debug_level_arg = 0;
3116
                else if(args_info.debug_level_arg > LOG_MAX)
3117
                        args_info.debug_level_arg = LOG_MAX;
3118
                janus_log_level = args_info.debug_level_arg;
3119
        }
3120

    
3121
        /* Any PID we need to create? */
3122
        const char *pidfile = NULL;
3123
        if(args_info.pid_file_given) {
3124
                pidfile = args_info.pid_file_arg;
3125
                janus_config_add_item(config, "general", "pid_file", pidfile);
3126
        } else {
3127
                /* Check if the configuration file is saying anything about this */
3128
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "pid_file");
3129
                if(item && item->value)
3130
                        pidfile = item->value;
3131
        }
3132
        if(janus_pidfile_create(pidfile) < 0)
3133
                exit(1);
3134

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

    
3236
        /* Logging/debugging */
3237
        JANUS_PRINT("Debug/log level is %d\n", janus_log_level);
3238
        janus_config_item *item = janus_config_get_item_drilldown(config, "general", "debug_timestamps");
3239
        if(item && item->value)
3240
                janus_log_timestamps = janus_is_true(item->value);
3241
        JANUS_PRINT("Debug/log timestamps are %s\n", janus_log_timestamps ? "enabled" : "disabled");
3242
        item = janus_config_get_item_drilldown(config, "general", "debug_colors");
3243
        if(item && item->value)
3244
                janus_log_colors = janus_is_true(item->value);
3245
        JANUS_PRINT("Debug/log colors are %s\n", janus_log_colors ? "enabled" : "disabled");
3246

    
3247
        /* Any IP/interface to enforce/ignore? */
3248
        item = janus_config_get_item_drilldown(config, "nat", "ice_enforce_list");
3249
        if(item && item->value) {
3250
                gchar **list = g_strsplit(item->value, ",", -1);
3251
                gchar *index = list[0];
3252
                if(index != NULL) {
3253
                        int i=0;
3254
                        while(index != NULL) {
3255
                                if(strlen(index) > 0) {
3256
                                        JANUS_LOG(LOG_INFO, "Adding '%s' to the ICE enforce list...\n", index);
3257
                                        janus_ice_enforce_interface(g_strdup(index));
3258
                                }
3259
                                i++;
3260
                                index = list[i];
3261
                        }
3262
                }
3263
                g_strfreev(list);
3264
                list = NULL;
3265
        }
3266
        item = janus_config_get_item_drilldown(config, "nat", "ice_ignore_list");
3267
        if(item && item->value) {
3268
                gchar **list = g_strsplit(item->value, ",", -1);
3269
                gchar *index = list[0];
3270
                if(index != NULL) {
3271
                        int i=0;
3272
                        while(index != NULL) {
3273
                                if(strlen(index) > 0) {
3274
                                        JANUS_LOG(LOG_INFO, "Adding '%s' to the ICE ignore list...\n", index);
3275
                                        janus_ice_ignore_interface(g_strdup(index));
3276
                                }
3277
                                i++;
3278
                                index = list[i];
3279
                        }
3280
                }
3281
                g_strfreev(list);
3282
                list = NULL;
3283
        }
3284
        /* What is the local IP? */
3285
        JANUS_LOG(LOG_VERB, "Selecting local IP address...\n");
3286
        gboolean local_ip_set = FALSE;
3287
        item = janus_config_get_item_drilldown(config, "general", "interface");
3288
        if(item && item->value) {
3289
                JANUS_LOG(LOG_VERB, "  -- Will try to use %s\n", item->value);
3290
                int family;
3291
                if (!janus_is_ip_valid(item->value, &family)) {
3292
                        JANUS_LOG(LOG_WARN, "Invalid local IP specified: %s, guessing the default...\n", item->value);
3293
                } else {
3294
                        /* Verify that we can actually bind to that address */
3295
                        int fd = socket(family, SOCK_DGRAM, 0);
3296
                        if (fd == -1) {
3297
                                JANUS_LOG(LOG_WARN, "Error creating test socket, falling back to detecting IP address...\n");
3298
                        } else {
3299
                                int r;
3300
                                struct sockaddr_storage ss;
3301
                                socklen_t addrlen;
3302
                                memset(&ss, 0, sizeof(ss));
3303
                                if (family == AF_INET) {
3304
                                        struct sockaddr_in *addr4 = (struct sockaddr_in*)&ss;
3305
                                        addr4->sin_family = AF_INET;
3306
                                        addr4->sin_port = 0;
3307
                                        inet_pton(AF_INET, item->value, &(addr4->sin_addr.s_addr));
3308
                                        addrlen = sizeof(struct sockaddr_in);
3309
                                } else {
3310
                                        struct sockaddr_in6 *addr6 = (struct sockaddr_in6*)&ss;
3311
                                        addr6->sin6_family = AF_INET6;
3312
                                        addr6->sin6_port = 0;
3313
                                        inet_pton(AF_INET6, item->value, &(addr6->sin6_addr.s6_addr));
3314
                                        addrlen = sizeof(struct sockaddr_in6);
3315
                                }
3316
                                r = bind(fd, (const struct sockaddr*)&ss, addrlen);
3317
                                close(fd);
3318
                                if (r < 0) {
3319
                                        JANUS_LOG(LOG_WARN, "Error setting local IP address to %s, falling back to detecting IP address...\n", item->value);
3320
                                } else {
3321
                                        g_strlcpy(local_ip, item->value, sizeof(local_ip));
3322
                                        local_ip_set = TRUE;
3323
                                }
3324
                        }
3325
                }
3326
        }
3327
        if (!local_ip_set)
3328
                janus_detect_local_ip(local_ip, sizeof(local_ip));
3329
        JANUS_LOG(LOG_INFO, "Using %s as local IP...\n", local_ip);
3330

    
3331
        /* Is there any API secret to consider? */
3332
        api_secret = NULL;
3333
        item = janus_config_get_item_drilldown(config, "general", "api_secret");
3334
        if(item && item->value) {
3335
                api_secret = g_strdup(item->value);
3336
        }
3337
        /* Is there any API secret to consider? */
3338
        admin_api_secret = NULL;
3339
        item = janus_config_get_item_drilldown(config, "general", "admin_secret");
3340
        if(item && item->value) {
3341
                admin_api_secret = g_strdup(item->value);
3342
        }
3343
        /* Also check if the token based authentication mechanism needs to be enabled */
3344
        item = janus_config_get_item_drilldown(config, "general", "token_auth");
3345
        janus_auth_init(item && item->value && janus_is_true(item->value));
3346

    
3347
        /* Setup ICE stuff (e.g., checking if the provided STUN server is correct) */
3348
        char *stun_server = NULL, *turn_server = NULL;
3349
        uint16_t stun_port = 0, turn_port = 0;
3350
        char *turn_type = NULL, *turn_user = NULL, *turn_pwd = NULL;
3351
        char *turn_rest_api = NULL, *turn_rest_api_key = NULL;
3352
        const char *nat_1_1_mapping = NULL;
3353
        uint16_t rtp_min_port = 0, rtp_max_port = 0;
3354
        gboolean ice_lite = FALSE, ice_tcp = FALSE, ipv6 = FALSE;
3355
        item = janus_config_get_item_drilldown(config, "media", "ipv6");
3356
        ipv6 = (item && item->value) ? janus_is_true(item->value) : FALSE;
3357
        item = janus_config_get_item_drilldown(config, "media", "rtp_port_range");
3358
        if(item && item->value) {
3359
                /* Split in min and max port */
3360
                char *maxport = strrchr(item->value, '-');
3361
                if(maxport != NULL) {
3362
                        *maxport = '\0';
3363
                        maxport++;
3364
                        rtp_min_port = atoi(item->value);
3365
                        rtp_max_port = atoi(maxport);
3366
                        maxport--;
3367
                        *maxport = '-';
3368
                }
3369
                if(rtp_min_port > rtp_max_port) {
3370
                        int temp_port = rtp_min_port;
3371
                        rtp_min_port = rtp_max_port;
3372
                        rtp_max_port = temp_port;
3373
                }
3374
                if(rtp_max_port == 0)
3375
                        rtp_max_port = 65535;
3376
                JANUS_LOG(LOG_INFO, "RTP port range: %u -- %u\n", rtp_min_port, rtp_max_port);
3377
        }
3378
        /* Check if we need to enable the ICE Lite mode */
3379
        item = janus_config_get_item_drilldown(config, "nat", "ice_lite");
3380
        ice_lite = (item && item->value) ? janus_is_true(item->value) : FALSE;
3381
        /* Check if we need to enable ICE-TCP support (warning: still broken, for debugging only) */
3382
        item = janus_config_get_item_drilldown(config, "nat", "ice_tcp");
3383
        ice_tcp = (item && item->value) ? janus_is_true(item->value) : FALSE;
3384
        /* Any STUN server to use in Janus? */
3385
        item = janus_config_get_item_drilldown(config, "nat", "stun_server");
3386
        if(item && item->value)
3387
                stun_server = (char *)item->value;
3388
        item = janus_config_get_item_drilldown(config, "nat", "stun_port");
3389
        if(item && item->value)
3390
                stun_port = atoi(item->value);
3391
        /* Any 1:1 NAT mapping to take into account? */
3392
        item = janus_config_get_item_drilldown(config, "nat", "nat_1_1_mapping");
3393
        if(item && item->value) {
3394
                JANUS_LOG(LOG_VERB, "Using nat_1_1_mapping for public ip - %s\n", item->value);
3395
                nat_1_1_mapping = item->value;
3396
                janus_set_public_ip(item->value);
3397
                janus_ice_enable_nat_1_1();
3398
        }
3399
        /* Any TURN server to use in Janus? */
3400
        item = janus_config_get_item_drilldown(config, "nat", "turn_server");
3401
        if(item && item->value)
3402
                turn_server = (char *)item->value;
3403
        item = janus_config_get_item_drilldown(config, "nat", "turn_port");
3404
        if(item && item->value)
3405
                turn_port = atoi(item->value);
3406
        item = janus_config_get_item_drilldown(config, "nat", "turn_type");
3407
        if(item && item->value)
3408
                turn_type = (char *)item->value;
3409
        item = janus_config_get_item_drilldown(config, "nat", "turn_user");
3410
        if(item && item->value)
3411
                turn_user = (char *)item->value;
3412
        item = janus_config_get_item_drilldown(config, "nat", "turn_pwd");
3413
        if(item && item->value)
3414
                turn_pwd = (char *)item->value;
3415
        /* Check if there's any TURN REST API backend to use */
3416
        item = janus_config_get_item_drilldown(config, "nat", "turn_rest_api");
3417
        if(item && item->value)
3418
                turn_rest_api = (char *)item->value;
3419
        item = janus_config_get_item_drilldown(config, "nat", "turn_rest_api_key");
3420
        if(item && item->value)
3421
                turn_rest_api_key = (char *)item->value;
3422
        /* Initialize the ICE stack now */
3423
        janus_ice_init(ice_lite, ice_tcp, ipv6, rtp_min_port, rtp_max_port);
3424
        if(janus_ice_set_stun_server(stun_server, stun_port) < 0) {
3425
                JANUS_LOG(LOG_FATAL, "Invalid STUN address %s:%u\n", stun_server, stun_port);
3426
                exit(1);
3427
        }
3428
        if(janus_ice_set_turn_server(turn_server, turn_port, turn_type, turn_user, turn_pwd) < 0) {
3429
                JANUS_LOG(LOG_FATAL, "Invalid TURN address %s:%u\n", turn_server, turn_port);
3430
                exit(1);
3431
        }
3432
#ifndef HAVE_LIBCURL
3433
        if(turn_rest_api != NULL || turn_rest_api_key != NULL) {
3434
                JANUS_LOG(LOG_WARN, "A TURN REST API backend specified in the settings, but libcurl support has not been built\n");
3435
        }
3436
#else
3437
        if(janus_ice_set_turn_rest_api(turn_rest_api, turn_rest_api_key) < 0) {
3438
                JANUS_LOG(LOG_FATAL, "Invalid TURN REST API configuration: %s (%s)\n", turn_rest_api, turn_rest_api_key);
3439
                exit(1);
3440
        }
3441
#endif
3442
        item = janus_config_get_item_drilldown(config, "nat", "nice_debug");
3443
        if(item && item->value && janus_is_true(item->value)) {
3444
                /* Enable libnice debugging */
3445
                janus_ice_debugging_enable();
3446
        }
3447
        if(stun_server == NULL && turn_server == NULL) {
3448
                /* No STUN and TURN server provided for Janus: make sure it isn't on a private address */
3449
                gboolean private_address = FALSE;
3450
                const char *test_ip = nat_1_1_mapping ? nat_1_1_mapping : local_ip;
3451
                struct sockaddr_in addr;
3452
                if(inet_pton(AF_INET, test_ip, &addr) > 0) {
3453
                        unsigned short int ip[4];
3454
                        sscanf(test_ip, "%hu.%hu.%hu.%hu", &ip[0], &ip[1], &ip[2], &ip[3]);
3455
                        if(ip[0] == 10) {
3456
                                /* Class A private address */
3457
                                private_address = TRUE;
3458
                        } else if(ip[0] == 172 && (ip[1] >= 16 && ip[1] <= 31)) {
3459
                                /* Class B private address */
3460
                                private_address = TRUE;
3461
                        } else if(ip[0] == 192 && ip[1] == 168) {
3462
                                /* Class C private address */
3463
                                private_address = TRUE;
3464
                        }
3465
                }
3466
                if(private_address) {
3467
                        JANUS_LOG(LOG_WARN, "Janus is deployed on a private address (%s) but you didn't specify any STUN server!"
3468
                                            " Expect trouble if this is supposed to work over the internet and not just in a LAN...\n", test_ip);
3469
                }
3470
        }
3471
        /* Are we going to force BUNDLE and/or rtcp-mux? */
3472
        gboolean force_bundle = FALSE, force_rtcpmux = FALSE;
3473
        item = janus_config_get_item_drilldown(config, "media", "force-bundle");
3474
        force_bundle = (item && item->value) ? janus_is_true(item->value) : FALSE;
3475
        janus_ice_force_bundle(force_bundle);
3476
        item = janus_config_get_item_drilldown(config, "media", "force-rtcp-mux");
3477
        force_rtcpmux = (item && item->value) ? janus_is_true(item->value) : FALSE;
3478
        janus_ice_force_rtcpmux(force_rtcpmux);
3479
        /* NACK related stuff */
3480
        item = janus_config_get_item_drilldown(config, "media", "max_nack_queue");
3481
        if(item && item->value) {
3482
                int mnq = atoi(item->value);
3483
                if(mnq < 0) {
3484
                        JANUS_LOG(LOG_WARN, "Ignoring max_nack_queue value as it's not a positive integer\n");
3485
                } else {
3486
                        janus_set_max_nack_queue(mnq);
3487
                }
3488
        }
3489

    
3490
        /* Setup OpenSSL stuff */
3491
        item = janus_config_get_item_drilldown(config, "certificates", "cert_pem");
3492
        if(!item || !item->value) {
3493
                JANUS_LOG(LOG_FATAL, "Missing certificate/key path, use the command line or the configuration to provide one\n");
3494
                exit(1);
3495
        }
3496
        server_pem = (char *)item->value;
3497
        item = janus_config_get_item_drilldown(config, "certificates", "cert_key");
3498
        if(!item || !item->value) {
3499
                JANUS_LOG(LOG_FATAL, "Missing certificate/key path, use the command line or the configuration to provide one\n");
3500
                exit(1);
3501
        }
3502
        server_key = (char *)item->value;
3503
        JANUS_LOG(LOG_VERB, "Using certificates:\n\t%s\n\t%s\n", server_pem, server_key);
3504
        SSL_library_init();
3505
        SSL_load_error_strings();
3506
        OpenSSL_add_all_algorithms();
3507
        /* ... and DTLS-SRTP in particular */
3508
        if(janus_dtls_srtp_init(server_pem, server_key) < 0) {
3509
                exit(1);
3510
        }
3511
        /* Check if there's any custom value for the starting MTU to use in the BIO filter */
3512
        item = janus_config_get_item_drilldown(config, "media", "dtls_mtu");
3513
        if(item && item->value)
3514
                janus_dtls_bio_filter_set_mtu(atoi(item->value));
3515

    
3516
#ifdef HAVE_SCTP
3517
        /* Initialize SCTP for DataChannels */
3518
        if(janus_sctp_init() < 0) {
3519
                exit(1);
3520
        }
3521
#else
3522
        JANUS_LOG(LOG_WARN, "Data Channels support not compiled\n");
3523
#endif
3524

    
3525
        /* Initialize Sofia-SDP */
3526
        if(janus_sdp_init() < 0) {
3527
                exit(1);
3528
        }
3529

    
3530
        /* Load plugins */
3531
        const char *path = PLUGINDIR;
3532
        item = janus_config_get_item_drilldown(config, "general", "plugins_folder");
3533
        if(item && item->value)
3534
                path = (char *)item->value;
3535
        JANUS_LOG(LOG_INFO, "Plugins folder: %s\n", path);
3536
        DIR *dir = opendir(path);
3537
        if(!dir) {
3538
                JANUS_LOG(LOG_FATAL, "\tCouldn't access plugins folder...\n");
3539
                exit(1);
3540
        }
3541
        /* Any plugin to ignore? */
3542
        gchar **disabled_plugins = NULL;
3543
        item = janus_config_get_item_drilldown(config, "plugins", "disable");
3544
        if(item && item->value)
3545
                disabled_plugins = g_strsplit(item->value, ",", -1);
3546
        /* Open the shared objects */
3547
        struct dirent *pluginent = NULL;
3548
        char pluginpath[1024];
3549
        while((pluginent = readdir(dir))) {
3550
                int len = strlen(pluginent->d_name);
3551
                if (len < 4) {
3552
                        continue;
3553
                }
3554
                if (strcasecmp(pluginent->d_name+len-strlen(SHLIB_EXT), SHLIB_EXT)) {
3555
                        continue;
3556
                }
3557
                /* Check if this plugins has been disabled in the configuration file */
3558
                if(disabled_plugins != NULL) {
3559
                        gchar *index = disabled_plugins[0];
3560
                        if(index != NULL) {
3561
                                int i=0;
3562
                                gboolean skip = FALSE;
3563
                                while(index != NULL) {
3564
                                        while(isspace(*index))
3565
                                                index++;
3566
                                        if(strlen(index) && !strcmp(index, pluginent->d_name)) {
3567
                                                JANUS_LOG(LOG_WARN, "Plugin '%s' has been disabled, skipping...\n", pluginent->d_name);
3568
                                                skip = TRUE;
3569
                                                break;
3570
                                        }
3571
                                        i++;
3572
                                        index = disabled_plugins[i];
3573
                                }
3574
                                if(skip)
3575
                                        continue;
3576
                        }
3577
                }
3578
                JANUS_LOG(LOG_INFO, "Loading plugin '%s'...\n", pluginent->d_name);
3579
                memset(pluginpath, 0, 1024);
3580
                g_snprintf(pluginpath, 1024, "%s/%s", path, pluginent->d_name);
3581
                void *plugin = dlopen(pluginpath, RTLD_LOCAL | RTLD_LAZY);
3582
                if (!plugin) {
3583
                        JANUS_LOG(LOG_ERR, "\tCouldn't load plugin '%s': %s\n", pluginent->d_name, dlerror());
3584
                } else {
3585
                        create_p *create = (create_p*) dlsym(plugin, "create");
3586
                        const char *dlsym_error = dlerror();
3587
                        if (dlsym_error) {
3588
                                JANUS_LOG(LOG_ERR, "\tCouldn't load symbol 'create': %s\n", dlsym_error);
3589
                                continue;
3590
                        }
3591
                        janus_plugin *janus_plugin = create();
3592
                        if(!janus_plugin) {
3593
                                JANUS_LOG(LOG_ERR, "\tCouldn't use function 'create'...\n");
3594
                                continue;
3595
                        }
3596
                        /* Are all the mandatory methods and callbacks implemented? */
3597
                        if(!janus_plugin->init || !janus_plugin->destroy ||
3598
                                        !janus_plugin->get_api_compatibility ||
3599
                                        !janus_plugin->get_version ||
3600
                                        !janus_plugin->get_version_string ||
3601
                                        !janus_plugin->get_description ||
3602
                                        !janus_plugin->get_package ||
3603
                                        !janus_plugin->get_name ||
3604
                                        !janus_plugin->create_session ||
3605
                                        !janus_plugin->query_session ||
3606
                                        !janus_plugin->destroy_session ||
3607
                                        !janus_plugin->handle_message ||
3608
                                        !janus_plugin->setup_media ||
3609
                                        !janus_plugin->hangup_media) {
3610
                                JANUS_LOG(LOG_ERR, "\tMissing some mandatory methods/callbacks, skipping this plugin...\n");
3611
                                continue;
3612
                        }
3613
                        if(janus_plugin->get_api_compatibility() < JANUS_PLUGIN_API_VERSION) {
3614
                                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",
3615
                                        janus_plugin->get_package(), janus_plugin->get_api_compatibility(), JANUS_PLUGIN_API_VERSION);
3616
                                continue;
3617
                        }
3618
                        janus_plugin->init(&janus_handler_plugin, configs_folder);
3619
                        JANUS_LOG(LOG_VERB, "\tVersion: %d (%s)\n", janus_plugin->get_version(), janus_plugin->get_version_string());
3620
                        JANUS_LOG(LOG_VERB, "\t   [%s] %s\n", janus_plugin->get_package(), janus_plugin->get_name());
3621
                        JANUS_LOG(LOG_VERB, "\t   %s\n", janus_plugin->get_description());
3622
                        JANUS_LOG(LOG_VERB, "\t   Plugin API version: %d\n", janus_plugin->get_api_compatibility());
3623
                        if(!janus_plugin->incoming_rtp && !janus_plugin->incoming_rtcp && !janus_plugin->incoming_data) {
3624
                                JANUS_LOG(LOG_WARN, "The '%s' plugin doesn't implement any callback for RTP/RTCP/data... is this on purpose?\n",
3625
                                        janus_plugin->get_package());
3626
                        }
3627
                        if(!janus_plugin->incoming_rtp && !janus_plugin->incoming_rtcp && janus_plugin->incoming_data) {
3628
                                JANUS_LOG(LOG_WARN, "The '%s' plugin will only handle data channels (no RTP/RTCP)... is this on purpose?\n",
3629
                                        janus_plugin->get_package());
3630
                        }
3631
                        if(plugins == NULL)
3632
                                plugins = g_hash_table_new(g_str_hash, g_str_equal);
3633
                        g_hash_table_insert(plugins, (gpointer)janus_plugin->get_package(), janus_plugin);
3634
                        if(plugins_so == NULL)
3635
                                plugins_so = g_hash_table_new(g_str_hash, g_str_equal);
3636
                        g_hash_table_insert(plugins_so, (gpointer)janus_plugin->get_package(), plugin);
3637
                }
3638
        }
3639
        closedir(dir);
3640
        if(disabled_plugins != NULL)
3641
                g_strfreev(disabled_plugins);
3642
        disabled_plugins = NULL;
3643

    
3644
        /* Create a thread pool to handle incoming requests, no matter what the transport */
3645
        GError *error = NULL;
3646
        tasks = g_thread_pool_new(janus_transport_task, NULL, -1, FALSE, &error);
3647
        if(error != NULL) {
3648
                /* Something went wrong... */
3649
                JANUS_LOG(LOG_FATAL, "Got error %d (%s) trying to launch the request pool task thread...\n", error->code, error->message ? error->message : "??");
3650
                exit(1);
3651
        }
3652

    
3653
        /* Load transports */
3654
        gboolean janus_api_enabled = FALSE, admin_api_enabled = FALSE;
3655
        path = TRANSPORTDIR;
3656
        item = janus_config_get_item_drilldown(config, "general", "transports_folder");
3657
        if(item && item->value)
3658
                path = (char *)item->value;
3659
        JANUS_LOG(LOG_INFO, "Transport plugins folder: %s\n", path);
3660
        dir = opendir(path);
3661
        if(!dir) {
3662
                JANUS_LOG(LOG_FATAL, "\tCouldn't access transport plugins folder...\n");
3663
                exit(1);
3664
        }
3665
        /* Any transport to ignore? */
3666
        gchar **disabled_transports = NULL;
3667
        item = janus_config_get_item_drilldown(config, "transports", "disable");
3668
        if(item && item->value)
3669
                disabled_transports = g_strsplit(item->value, ",", -1);
3670
        /* Open the shared objects */
3671
        struct dirent *transportent = NULL;
3672
        char transportpath[1024];
3673
        while((transportent = readdir(dir))) {
3674
                int len = strlen(transportent->d_name);
3675
                if (len < 4) {
3676
                        continue;
3677
                }
3678
                if (strcasecmp(transportent->d_name+len-strlen(SHLIB_EXT), SHLIB_EXT)) {
3679
                        continue;
3680
                }
3681
                /* Check if this transports has been disabled in the configuration file */
3682
                if(disabled_transports != NULL) {
3683
                        gchar *index = disabled_transports[0];
3684
                        if(index != NULL) {
3685
                                int i=0;
3686
                                gboolean skip = FALSE;
3687
                                while(index != NULL) {
3688
                                        while(isspace(*index))
3689
                                                index++;
3690
                                        if(strlen(index) && !strcmp(index, transportent->d_name)) {
3691
                                                JANUS_LOG(LOG_WARN, "Transport plugin '%s' has been disabled, skipping...\n", transportent->d_name);
3692
                                                skip = TRUE;
3693
                                                break;
3694
                                        }
3695
                                        i++;
3696
                                        index = disabled_transports[i];
3697
                                }
3698
                                if(skip)
3699
                                        continue;
3700
                        }
3701
                }
3702
                JANUS_LOG(LOG_INFO, "Loading transport plugin '%s'...\n", transportent->d_name);
3703
                memset(transportpath, 0, 1024);
3704
                g_snprintf(transportpath, 1024, "%s/%s", path, transportent->d_name);
3705
                void *transport = dlopen(transportpath, RTLD_LOCAL | RTLD_LAZY);
3706
                if (!transport) {
3707
                        JANUS_LOG(LOG_ERR, "\tCouldn't load transport plugin '%s': %s\n", transportent->d_name, dlerror());
3708
                } else {
3709
                        create_t *create = (create_t*) dlsym(transport, "create");
3710
                        const char *dlsym_error = dlerror();
3711
                        if (dlsym_error) {
3712
                                JANUS_LOG(LOG_ERR, "\tCouldn't load symbol 'create': %s\n", dlsym_error);
3713
                                continue;
3714
                        }
3715
                        janus_transport *janus_transport = create();
3716
                        if(!janus_transport) {
3717
                                JANUS_LOG(LOG_ERR, "\tCouldn't use function 'create'...\n");
3718
                                continue;
3719
                        }
3720
                        /* Are all the mandatory methods and callbacks implemented? */
3721
                        if(!janus_transport->init || !janus_transport->destroy ||
3722
                                        !janus_transport->get_api_compatibility ||
3723
                                        !janus_transport->get_version ||
3724
                                        !janus_transport->get_version_string ||
3725
                                        !janus_transport->get_description ||
3726
                                        !janus_transport->get_package ||
3727
                                        !janus_transport->get_name ||
3728
                                        !janus_transport->send_message ||
3729
                                        !janus_transport->is_janus_api_enabled ||
3730
                                        !janus_transport->is_admin_api_enabled ||
3731
                                        !janus_transport->session_created ||
3732
                                        !janus_transport->session_over) {
3733
                                JANUS_LOG(LOG_ERR, "\tMissing some mandatory methods/callbacks, skipping this transport plugin...\n");
3734
                                continue;
3735
                        }
3736
                        if(janus_transport->get_api_compatibility() < JANUS_TRANSPORT_API_VERSION) {
3737
                                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",
3738
                                        janus_transport->get_package(), janus_transport->get_api_compatibility(), JANUS_TRANSPORT_API_VERSION);
3739
                                continue;
3740
                        }
3741
                        janus_transport->init(&janus_handler_transport, configs_folder);
3742
                        JANUS_LOG(LOG_VERB, "\tVersion: %d (%s)\n", janus_transport->get_version(), janus_transport->get_version_string());
3743
                        JANUS_LOG(LOG_VERB, "\t   [%s] %s\n", janus_transport->get_package(), janus_transport->get_name());
3744
                        JANUS_LOG(LOG_VERB, "\t   %s\n", janus_transport->get_description());
3745
                        JANUS_LOG(LOG_VERB, "\t   Plugin API version: %d\n", janus_transport->get_api_compatibility());
3746
                        JANUS_LOG(LOG_VERB, "\t   Janus API: %s\n", janus_transport->is_janus_api_enabled() ? "enabled" : "disabled");
3747
                        JANUS_LOG(LOG_VERB, "\t   Admin API: %s\n", janus_transport->is_admin_api_enabled() ? "enabled" : "disabled");
3748
                        janus_api_enabled = janus_api_enabled || janus_transport->is_janus_api_enabled();
3749
                        admin_api_enabled = admin_api_enabled || janus_transport->is_admin_api_enabled();
3750
                        if(transports == NULL)
3751
                                transports = g_hash_table_new(g_str_hash, g_str_equal);
3752
                        g_hash_table_insert(transports, (gpointer)janus_transport->get_package(), janus_transport);
3753
                        if(transports_so == NULL)
3754
                                transports_so = g_hash_table_new(g_str_hash, g_str_equal);
3755
                        g_hash_table_insert(transports_so, (gpointer)janus_transport->get_package(), transport);
3756
                }
3757
        }
3758
        closedir(dir);
3759
        if(disabled_transports != NULL)
3760
                g_strfreev(disabled_transports);
3761
        disabled_transports = NULL;
3762
        /* Make sure at least a Janus API transport is available */
3763
        if(!janus_api_enabled) {
3764
                JANUS_LOG(LOG_FATAL, "No Janus API transport is available... enable at least one and restart Janus\n");
3765
                exit(1);        /* FIXME Should we really give up? */
3766
        }
3767
        /* Make sure at least an admin API transport is available, if the auth mechanism is enabled */
3768
        if(!admin_api_enabled && janus_auth_is_enabled()) {
3769
                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");
3770
                exit(1);        /* FIXME Should we really give up? */
3771
        }
3772

    
3773
        /* Sessions */
3774
        sessions = g_hash_table_new(NULL, NULL);
3775
        old_sessions = g_hash_table_new(NULL, NULL);
3776
        janus_mutex_init(&sessions_mutex);
3777
        /* Start the sessions watchdog */
3778
        sessions_watchdog_context = g_main_context_new();
3779
        GMainLoop *watchdog_loop = g_main_loop_new(sessions_watchdog_context, FALSE);
3780
        error = NULL;
3781
        GThread *watchdog = g_thread_try_new("watchdog", &janus_sessions_watchdog, watchdog_loop, &error);
3782
        if(error != NULL) {
3783
                JANUS_LOG(LOG_FATAL, "Got error %d (%s) trying to start sessions watchdog...\n", error->code, error->message ? error->message : "??");
3784
                exit(1);
3785
        }
3786

    
3787
        /* Ok, Janus has started! Let the parent now about this if we're daemonizing */
3788
        if(daemonize) {
3789
                int code = 0;
3790
                ssize_t res = 0;
3791
                do {
3792
                        res = write(pipefd[1], &code, sizeof(int));
3793
                } while(res == -1 && errno == EINTR);
3794
        }
3795

    
3796
        while(!g_atomic_int_get(&stop)) {
3797
                /* Loop until we have to stop */
3798
                usleep(250000); /* A signal will cancel usleep() but not g_usleep() */
3799
        }
3800

    
3801
        /* Done */
3802
        JANUS_LOG(LOG_INFO, "Ending watchdog mainloop...\n");
3803
        g_main_loop_quit(watchdog_loop);
3804
        g_thread_join(watchdog);
3805
        watchdog = NULL;
3806
        g_main_loop_unref(watchdog_loop);
3807
        g_main_context_unref(sessions_watchdog_context);
3808

    
3809
        if(config)
3810
                janus_config_destroy(config);
3811

    
3812
        JANUS_LOG(LOG_INFO, "Closing transport plugins:\n");
3813
        if(transports != NULL) {
3814
                g_hash_table_foreach(transports, janus_transport_close, NULL);
3815
                g_hash_table_destroy(transports);
3816
        }
3817
        if(transports_so != NULL) {
3818
                g_hash_table_foreach(transports_so, janus_transportso_close, NULL);
3819
                g_hash_table_destroy(transports_so);
3820
        }
3821
        g_thread_pool_free(tasks, FALSE, FALSE);
3822

    
3823
        JANUS_LOG(LOG_INFO, "Destroying sessions...\n");
3824
        if(sessions != NULL)
3825
                g_hash_table_destroy(sessions);
3826
        if(old_sessions != NULL)
3827
                g_hash_table_destroy(old_sessions);
3828
        JANUS_LOG(LOG_INFO, "Freeing crypto resources...\n");
3829
        SSL_CTX_free(janus_dtls_get_ssl_ctx());
3830
        EVP_cleanup();
3831
        ERR_free_strings();
3832
        JANUS_LOG(LOG_INFO, "Cleaning SDP structures...\n");
3833
        janus_sdp_deinit();
3834
#ifdef HAVE_SCTP
3835
        JANUS_LOG(LOG_INFO, "De-initializing SCTP...\n");
3836
        janus_sctp_deinit();
3837
#endif
3838
        janus_ice_deinit();
3839
        janus_auth_deinit();
3840

    
3841
        JANUS_LOG(LOG_INFO, "Closing plugins:\n");
3842
        if(plugins != NULL) {
3843
                g_hash_table_foreach(plugins, janus_plugin_close, NULL);
3844
                g_hash_table_destroy(plugins);
3845
        }
3846
        if(plugins_so != NULL) {
3847
                g_hash_table_foreach(plugins_so, janus_pluginso_close, NULL);
3848
                g_hash_table_destroy(plugins_so);
3849
        }
3850

    
3851
        JANUS_PRINT("Bye!\n");
3852

    
3853
        exit(0);
3854
}