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 Janus core. This code takes care of
6  * the server 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 <fcntl.h>
27 #include <poll.h>
28 
29 #include <openssl/rand.h>
30 #ifdef HAVE_TURNRESTAPI
31 #include <curl/curl.h>
32 #endif
33 
34 #include "janus.h"
35 #include "version.h"
36 #include "cmdline.h"
37 #include "config.h"
38 #include "apierror.h"
39 #include "debug.h"
40 #include "ip-utils.h"
41 #include "rtcp.h"
42 #include "auth.h"
43 #include "record.h"
44 #include "events.h"
45 
46 
47 #define JANUS_NAME				"Janus WebRTC Server"
48 #define JANUS_AUTHOR			"Meetecho s.r.l."
49 #define JANUS_SERVER_NAME		"MyJanusInstance"
50 
51 #ifdef __MACH__
52 #define SHLIB_EXT "0.dylib"
53 #else
54 #define SHLIB_EXT ".so"
55 #endif
56 
57 
58 static janus_config *config = NULL;
59 static char *config_file = NULL;
60 static char *configs_folder = NULL;
61 
62 static GHashTable *transports = NULL;
63 static GHashTable *transports_so = NULL;
64 
65 static GHashTable *eventhandlers = NULL;
66 static GHashTable *eventhandlers_so = NULL;
67 
68 static GHashTable *loggers = NULL;
69 static GHashTable *loggers_so = NULL;
70 
71 static GHashTable *plugins = NULL;
72 static GHashTable *plugins_so = NULL;
73 
74 
75 /* Daemonization */
76 static gboolean daemonize = FALSE;
77 static int pipefd[2];
78 
79 
80 #ifdef REFCOUNT_DEBUG
81 /* Reference counters debugging */
82 GHashTable *counters = NULL;
83 janus_mutex counters_mutex;
84 #endif
85 
86 
87 /* API secrets */
88 static char *api_secret = NULL, *admin_api_secret = NULL;
89 
90 /* JSON parameters */
91 static int janus_process_error_string(janus_request *request, uint64_t session_id, const char *transaction, gint error, gchar *error_string);
92 
93 static struct janus_json_parameter incoming_request_parameters[] = {
94 	{"transaction", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
95 	{"janus", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
96 	{"id", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE}
97 };
98 static struct janus_json_parameter attach_parameters[] = {
99 	{"plugin", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
100 	{"opaque_id", JSON_STRING, 0},
101 	{"loop_index", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
102 };
103 static struct janus_json_parameter body_parameters[] = {
104 	{"body", JSON_OBJECT, JANUS_JSON_PARAM_REQUIRED}
105 };
106 static struct janus_json_parameter jsep_parameters[] = {
107 	{"type", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
108 	{"sdp", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
109 	{"trickle", JANUS_JSON_BOOL, 0},
110 	{"rid_order", JSON_STRING, 0},
111 	{"force_relay", JANUS_JSON_BOOL, 0},
112 	{"e2ee", JANUS_JSON_BOOL, 0}
113 };
114 static struct janus_json_parameter add_token_parameters[] = {
115 	{"token", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
116 	{"plugins", JSON_ARRAY, 0}
117 };
118 static struct janus_json_parameter token_parameters[] = {
119 	{"token", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
120 };
121 static struct janus_json_parameter admin_parameters[] = {
122 	{"transaction", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
123 	{"janus", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
124 };
125 static struct janus_json_parameter debug_parameters[] = {
126 	{"debug", JANUS_JSON_BOOL, JANUS_JSON_PARAM_REQUIRED}
127 };
128 static struct janus_json_parameter timeout_parameters[] = {
129 	{"timeout", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
130 };
131 static struct janus_json_parameter session_timeout_parameters[] = {
132 	{"timeout", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED}
133 };
134 static struct janus_json_parameter level_parameters[] = {
135 	{"level", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
136 };
137 static struct janus_json_parameter timestamps_parameters[] = {
138 	{"timestamps", JANUS_JSON_BOOL, JANUS_JSON_PARAM_REQUIRED}
139 };
140 static struct janus_json_parameter colors_parameters[] = {
141 	{"colors", JANUS_JSON_BOOL, JANUS_JSON_PARAM_REQUIRED}
142 };
143 static struct janus_json_parameter mnq_parameters[] = {
144 	{"min_nack_queue", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
145 };
146 static struct janus_json_parameter nopt_parameters[] = {
147 	{"nack_optimizations", JANUS_JSON_BOOL, JANUS_JSON_PARAM_REQUIRED}
148 };
149 static struct janus_json_parameter nmt_parameters[] = {
150 	{"no_media_timer", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
151 };
152 static struct janus_json_parameter st_parameters[] = {
153 	{"slowlink_threshold", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
154 };
155 static struct janus_json_parameter ans_parameters[] = {
156 	{"accept", JANUS_JSON_BOOL, JANUS_JSON_PARAM_REQUIRED}
157 };
158 static struct janus_json_parameter querytransport_parameters[] = {
159 	{"transport", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
160 	{"request", JSON_OBJECT, 0}
161 };
162 static struct janus_json_parameter queryhandler_parameters[] = {
163 	{"handler", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
164 	{"request", JSON_OBJECT, 0}
165 };
166 static struct janus_json_parameter querylogger_parameters[] = {
167 	{"logger", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
168 	{"request", JSON_OBJECT, 0}
169 };
170 static struct janus_json_parameter messageplugin_parameters[] = {
171 	{"plugin", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
172 	{"request", JSON_OBJECT, 0}
173 };
174 static struct janus_json_parameter customevent_parameters[] = {
175 	{"schema", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
176 	{"data", JSON_OBJECT, JANUS_JSON_PARAM_REQUIRED}
177 };
178 static struct janus_json_parameter customlogline_parameters[] = {
179 	{"line", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
180 	{"level", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE}
181 };
182 static struct janus_json_parameter text2pcap_parameters[] = {
183 	{"folder", JSON_STRING, 0},
184 	{"filename", JSON_STRING, 0},
185 	{"truncate", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE}
186 };
187 static struct janus_json_parameter handleinfo_parameters[] = {
188 	{"plugin_only", JANUS_JSON_BOOL, 0}
189 };
190 static struct janus_json_parameter resaddr_parameters[] = {
191 	{"address", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
192 };
193 static struct janus_json_parameter teststun_parameters[] = {
194 	{"address", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
195 	{"port", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE},
196 	{"localport", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE}
197 };
198 
199 /* Admin/Monitor helpers */
200 json_t *janus_admin_stream_summary(janus_ice_stream *stream);
201 json_t *janus_admin_component_summary(janus_ice_component *component);
202 
203 
204 /* IP addresses */
205 static gchar *local_ip = NULL;
janus_get_local_ip(void)206 gchar *janus_get_local_ip(void) {
207 	return local_ip;
208 }
209 static GHashTable *public_ips_table = NULL;
210 static GList *public_ips = NULL;
211 gboolean public_ips_ipv4 = FALSE, public_ips_ipv6 = FALSE;
janus_get_public_ip_count(void)212 guint janus_get_public_ip_count(void) {
213 	return public_ips_table ? g_hash_table_size(public_ips_table) : 0;
214 }
janus_get_public_ip(guint index)215 gchar *janus_get_public_ip(guint index) {
216 	if (!janus_get_public_ip_count()) {
217 		/* Fallback to the local IP, if we have no public one */
218 		return local_ip;
219 	}
220 	if (index >= g_hash_table_size(public_ips_table)) {
221 		index = g_hash_table_size(public_ips_table) - 1;
222 	}
223 	return (char *)g_list_nth(public_ips, index)->data;
224 }
janus_add_public_ip(const gchar * ip)225 void janus_add_public_ip(const gchar *ip) {
226 	if(ip == NULL) {
227 		return;
228 	}
229 
230 	if(!public_ips_table) {
231 		public_ips_table = g_hash_table_new_full(g_str_hash, g_str_equal, (GDestroyNotify)g_free, NULL);
232 	}
233 	if (g_hash_table_insert(public_ips_table, g_strdup(ip), NULL)) {
234 		g_list_free(public_ips);
235 		public_ips = g_hash_table_get_keys(public_ips_table);
236 	}
237 	/* Take note of whether we received at least one IPv4 and/or IPv6 address */
238 	if(strchr(ip, ':')) {
239 		public_ips_ipv6 = TRUE;
240 	} else {
241 		public_ips_ipv4 = TRUE;
242 	}
243 }
janus_has_public_ipv4_ip(void)244 gboolean janus_has_public_ipv4_ip(void) {
245 	return public_ips_ipv4;
246 }
janus_has_public_ipv6_ip(void)247 gboolean janus_has_public_ipv6_ip(void) {
248 	return public_ips_ipv6;
249 }
250 
251 static volatile gint stop = 0;
252 static gint stop_signal = 0;
janus_is_stopping(void)253 gint janus_is_stopping(void) {
254 	return g_atomic_int_get(&stop);
255 }
256 static GMainLoop *mainloop = NULL;
257 
258 
259 /* Public instance name */
260 static gchar *server_name = NULL;
261 
janus_create_message(const char * status,uint64_t session_id,const char * transaction)262 static json_t *janus_create_message(const char *status, uint64_t session_id, const char *transaction) {
263 	json_t *msg = json_object();
264 	json_object_set_new(msg, "janus", json_string(status));
265 	if(session_id > 0)
266 		json_object_set_new(msg, "session_id", json_integer(session_id));
267 	if(transaction != NULL)
268 		json_object_set_new(msg, "transaction", json_string(transaction));
269 	return msg;
270 }
271 
272 /* The default timeout for sessions is 60 seconds: this means that, if
273  * we don't get any activity (i.e., no request) on this session for more
274  * than 60 seconds, then it's considered expired and we destroy it. That's
275  * why we have a keep-alive method in the API. This can be overridden in
276  * either janus.cfg/.jcfg or from the command line. Setting this to 0 will
277  * disable the timeout mechanism, which is NOT suggested as it may risk
278  * having orphaned sessions (sessions not controlled by any transport
279  * and never freed). Besides, notice that if you make this shorter than
280  * 30s, you'll have to update the timers in janus.js when the long
281  * polling mechanism is used and shorten them as well, or you'll risk
282  * incurring in unexpected timeouts (when HTTP is used in janus.js, the
283  * long poll is used as a keepalive mechanism). */
284 #define DEFAULT_SESSION_TIMEOUT		60
285 static uint global_session_timeout = DEFAULT_SESSION_TIMEOUT;
286 
287 #define DEFAULT_RECLAIM_SESSION_TIMEOUT		0
288 static uint reclaim_session_timeout = DEFAULT_RECLAIM_SESSION_TIMEOUT;
289 
290 /* We can programmatically change whether we want to accept new sessions
291  * or not: the default is of course TRUE, but we may want to temporarily
292  * change that in some cases, e.g., if we don't want the load on this
293  * server to grow too much, or because we're draining the server. */
294 static gboolean accept_new_sessions = TRUE;
295 
296 /* We don't hold (trickle) candidates indefinitely either: by default, we
297  * only store them for 45 seconds. After that, they're discarded, in order
298  * to avoid leaks or orphaned media details. This means that, if for instance
299  * you're trying to set up a call with someone, and that someone only answers
300  * a minute later, the candidates you sent initially will be discarded and
301  * the call will fail. You can modify the default value in janus.jcfg */
302 #define DEFAULT_CANDIDATES_TIMEOUT		45
303 static uint candidates_timeout = DEFAULT_CANDIDATES_TIMEOUT;
304 
305 /* By default we list dependencies details, but some may prefer not to */
306 static gboolean hide_dependencies = FALSE;
307 
308 /* By default we do not exit if a shared library cannot be loaded or is missing an expected symbol */
309 static gboolean exit_on_dl_error = FALSE;
310 
311 /* WebRTC encryption is obviously enabled by default. In the rare cases
312  * you want to disable it for debugging purposes, though, you can do
313  * that either via command line (-w) or in the main configuration file */
314 static gboolean webrtc_encryption = TRUE;
janus_is_webrtc_encryption_enabled(void)315 gboolean janus_is_webrtc_encryption_enabled(void) {
316 	return webrtc_encryption;
317 }
318 
319 /* Information */
janus_info(const char * transaction)320 static json_t *janus_info(const char *transaction) {
321 	/* Prepare a summary on the Janus instance */
322 	json_t *info = janus_create_message("server_info", 0, transaction);
323 	json_object_set_new(info, "name", json_string(JANUS_NAME));
324 	json_object_set_new(info, "version", json_integer(janus_version));
325 	json_object_set_new(info, "version_string", json_string(janus_version_string));
326 	json_object_set_new(info, "author", json_string(JANUS_AUTHOR));
327 	json_object_set_new(info, "commit-hash", json_string(janus_build_git_sha));
328 	json_object_set_new(info, "compile-time", json_string(janus_build_git_time));
329 	json_object_set_new(info, "log-to-stdout", janus_log_is_stdout_enabled() ? json_true() : json_false());
330 	json_object_set_new(info, "log-to-file", janus_log_is_logfile_enabled() ? json_true() : json_false());
331 	if(janus_log_is_logfile_enabled())
332 		json_object_set_new(info, "log-path", json_string(janus_log_get_logfile_path()));
333 #ifdef HAVE_SCTP
334 	json_object_set_new(info, "data_channels", json_true());
335 #else
336 	json_object_set_new(info, "data_channels", json_false());
337 #endif
338 	json_object_set_new(info, "accepting-new-sessions", accept_new_sessions ? json_true() : json_false());
339 	json_object_set_new(info, "session-timeout", json_integer(global_session_timeout));
340 	json_object_set_new(info, "reclaim-session-timeout", json_integer(reclaim_session_timeout));
341 	json_object_set_new(info, "candidates-timeout", json_integer(candidates_timeout));
342 	json_object_set_new(info, "server-name", json_string(server_name ? server_name : JANUS_SERVER_NAME));
343 	json_object_set_new(info, "local-ip", json_string(local_ip));
344 	guint public_ip_count = janus_get_public_ip_count();
345 	if(public_ip_count > 0) {
346 		json_object_set_new(info, "public-ip", json_string(janus_get_public_ip(0)));
347 	}
348 	if(public_ip_count > 1) {
349 		guint i;
350 		json_t *ips = json_array();
351 		for (i = 0; i < public_ip_count; i++) {
352 			json_array_append_new(ips, json_string(janus_get_public_ip(i)));
353 		}
354 		json_object_set_new(info, "public-ips", ips);
355 	}
356 	json_object_set_new(info, "ipv6", janus_ice_is_ipv6_enabled() ? json_true() : json_false());
357 	if(janus_ice_is_ipv6_enabled())
358 		json_object_set_new(info, "ipv6-link-local", janus_ice_is_ipv6_linklocal_enabled() ? json_true() : json_false());
359 	json_object_set_new(info, "ice-lite", janus_ice_is_ice_lite_enabled() ? json_true() : json_false());
360 	json_object_set_new(info, "ice-tcp", janus_ice_is_ice_tcp_enabled() ? json_true() : json_false());
361 #ifdef HAVE_ICE_NOMINATION
362 	json_object_set_new(info, "ice-nomination", json_string(janus_ice_get_nomination_mode()));
363 #endif
364 	json_object_set_new(info, "ice-keepalive-conncheck", janus_ice_is_keepalive_conncheck_enabled() ? json_true() : json_false());
365 	json_object_set_new(info, "full-trickle", janus_ice_is_full_trickle_enabled() ? json_true() : json_false());
366 	json_object_set_new(info, "mdns-enabled", janus_ice_is_mdns_enabled() ? json_true() : json_false());
367 	json_object_set_new(info, "min-nack-queue", json_integer(janus_get_min_nack_queue()));
368 	json_object_set_new(info, "nack-optimizations", janus_is_nack_optimizations_enabled() ? json_true() : json_false());
369 	json_object_set_new(info, "twcc-period", json_integer(janus_get_twcc_period()));
370 	if(janus_get_dscp() > 0)
371 		json_object_set_new(info, "dscp", json_integer(janus_get_dscp()));
372 	json_object_set_new(info, "dtls-mtu", json_integer(janus_dtls_bio_agent_get_mtu()));
373 	if(janus_ice_get_stun_server() != NULL) {
374 		char server[255];
375 		g_snprintf(server, 255, "%s:%"SCNu16, janus_ice_get_stun_server(), janus_ice_get_stun_port());
376 		json_object_set_new(info, "stun-server", json_string(server));
377 	}
378 	if(janus_ice_get_turn_server() != NULL) {
379 		char server[255];
380 		g_snprintf(server, 255, "%s:%"SCNu16, janus_ice_get_turn_server(), janus_ice_get_turn_port());
381 		json_object_set_new(info, "turn-server", json_string(server));
382 	}
383 	if(janus_ice_is_force_relay_allowed())
384 		json_object_set_new(info, "allow-force-relay", json_true());
385 	json_object_set_new(info, "static-event-loops", json_integer(janus_ice_get_static_event_loops()));
386 	if(janus_ice_get_static_event_loops())
387 		json_object_set_new(info, "loop-indication", janus_ice_is_loop_indication_allowed() ? json_true() : json_false());
388 	json_object_set_new(info, "api_secret", api_secret ? json_true() : json_false());
389 	json_object_set_new(info, "auth_token", janus_auth_is_enabled() ? json_true() : json_false());
390 	json_object_set_new(info, "event_handlers", janus_events_is_enabled() ? json_true() : json_false());
391 	json_object_set_new(info, "opaqueid_in_api", janus_is_opaqueid_in_api_enabled() ? json_true() : json_false());
392 	if(!webrtc_encryption)
393 		json_object_set_new(info, "webrtc_encryption", json_false());
394 	/* Dependencies */
395 	if(!hide_dependencies) {
396 		json_t *deps = json_object();
397 		char glib2_version[20];
398 		g_snprintf(glib2_version, sizeof(glib2_version), "%d.%d.%d", glib_major_version, glib_minor_version, glib_micro_version);
399 		json_object_set_new(deps, "glib2", json_string(glib2_version));
400 		json_object_set_new(deps, "jansson", json_string(JANSSON_VERSION));
401 		json_object_set_new(deps, "libnice", json_string(libnice_version_string));
402 		json_object_set_new(deps, "libsrtp", json_string(srtp_get_version_string()));
403 	#ifdef HAVE_TURNRESTAPI
404 		curl_version_info_data *curl_version = curl_version_info(CURLVERSION_NOW);
405 		if(curl_version && curl_version->version)
406 			json_object_set_new(deps, "libcurl", json_string(curl_version->version));
407 	#endif
408 		json_object_set_new(deps, "crypto", json_string(janus_get_ssl_version()));
409 		json_object_set_new(info, "dependencies", deps);
410 	}
411 	/* Available transports */
412 	json_t *t_data = json_object();
413 	if(transports && g_hash_table_size(transports) > 0) {
414 		GHashTableIter iter;
415 		gpointer value;
416 		g_hash_table_iter_init(&iter, transports);
417 		while (g_hash_table_iter_next(&iter, NULL, &value)) {
418 			janus_transport *t = value;
419 			if(t == NULL) {
420 				continue;
421 			}
422 			json_t *transport = json_object();
423 			json_object_set_new(transport, "name", json_string(t->get_name()));
424 			json_object_set_new(transport, "author", json_string(t->get_author()));
425 			json_object_set_new(transport, "description", json_string(t->get_description()));
426 			json_object_set_new(transport, "version_string", json_string(t->get_version_string()));
427 			json_object_set_new(transport, "version", json_integer(t->get_version()));
428 			json_object_set_new(t_data, t->get_package(), transport);
429 		}
430 	}
431 	json_object_set_new(info, "transports", t_data);
432 	/* Available event handlers */
433 	json_t *e_data = json_object();
434 	if(eventhandlers && g_hash_table_size(eventhandlers) > 0) {
435 		GHashTableIter iter;
436 		gpointer value;
437 		g_hash_table_iter_init(&iter, eventhandlers);
438 		while (g_hash_table_iter_next(&iter, NULL, &value)) {
439 			janus_eventhandler *e = value;
440 			if(e == NULL) {
441 				continue;
442 			}
443 			json_t *eventhandler = json_object();
444 			json_object_set_new(eventhandler, "name", json_string(e->get_name()));
445 			json_object_set_new(eventhandler, "author", json_string(e->get_author()));
446 			json_object_set_new(eventhandler, "description", json_string(e->get_description()));
447 			json_object_set_new(eventhandler, "version_string", json_string(e->get_version_string()));
448 			json_object_set_new(eventhandler, "version", json_integer(e->get_version()));
449 			json_object_set_new(e_data, e->get_package(), eventhandler);
450 		}
451 	}
452 	json_object_set_new(info, "events", e_data);
453 	/* Available external loggers */
454 	json_t *l_data = json_object();
455 	if(loggers && g_hash_table_size(loggers) > 0) {
456 		GHashTableIter iter;
457 		gpointer value;
458 		g_hash_table_iter_init(&iter, loggers);
459 		while (g_hash_table_iter_next(&iter, NULL, &value)) {
460 			janus_logger *l = value;
461 			if(l == NULL) {
462 				continue;
463 			}
464 			json_t *logger = json_object();
465 			json_object_set_new(logger, "name", json_string(l->get_name()));
466 			json_object_set_new(logger, "author", json_string(l->get_author()));
467 			json_object_set_new(logger, "description", json_string(l->get_description()));
468 			json_object_set_new(logger, "version_string", json_string(l->get_version_string()));
469 			json_object_set_new(logger, "version", json_integer(l->get_version()));
470 			json_object_set_new(l_data, l->get_package(), logger);
471 		}
472 	}
473 	json_object_set_new(info, "loggers", l_data);
474 	/* Available plugins */
475 	json_t *p_data = json_object();
476 	if(plugins && g_hash_table_size(plugins) > 0) {
477 		GHashTableIter iter;
478 		gpointer value;
479 		g_hash_table_iter_init(&iter, plugins);
480 		while (g_hash_table_iter_next(&iter, NULL, &value)) {
481 			janus_plugin *p = value;
482 			if(p == NULL) {
483 				continue;
484 			}
485 			json_t *plugin = json_object();
486 			json_object_set_new(plugin, "name", json_string(p->get_name()));
487 			json_object_set_new(plugin, "author", json_string(p->get_author()));
488 			json_object_set_new(plugin, "description", json_string(p->get_description()));
489 			json_object_set_new(plugin, "version_string", json_string(p->get_version_string()));
490 			json_object_set_new(plugin, "version", json_integer(p->get_version()));
491 			json_object_set_new(p_data, p->get_package(), plugin);
492 		}
493 	}
494 	json_object_set_new(info, "plugins", p_data);
495 
496 	return info;
497 }
498 
499 
500 /* Logging */
501 int janus_log_level = LOG_INFO;
502 gboolean janus_log_timestamps = FALSE;
503 gboolean janus_log_colors = FALSE;
504 char *janus_log_global_prefix = NULL;
505 int lock_debug = 0;
506 #ifdef REFCOUNT_DEBUG
507 int refcount_debug = 1;
508 #else
509 int refcount_debug = 0;
510 #endif
511 
512 
513 /*! \brief Signal handler (just used to intercept CTRL+C and SIGTERM) */
janus_handle_signal(int signum)514 static void janus_handle_signal(int signum) {
515 	stop_signal = signum;
516 	switch(g_atomic_int_get(&stop)) {
517 		case 0:
518 			JANUS_PRINT("Stopping server, please wait...\n");
519 			break;
520 		case 1:
521 			JANUS_PRINT("In a hurry? I'm trying to free resources cleanly, here!\n");
522 			break;
523 		default:
524 			JANUS_PRINT("Ok, leaving immediately...\n");
525 			break;
526 	}
527 	g_atomic_int_inc(&stop);
528 	if(g_atomic_int_get(&stop) > 2)
529 		exit(1);
530 	if(mainloop && g_main_loop_is_running(mainloop))
531 		g_main_loop_quit(mainloop);
532 }
533 
534 /*! \brief Termination handler (atexit) */
janus_termination_handler(void)535 static void janus_termination_handler(void) {
536 	/* Free the instance name, if provided */
537 	g_free(server_name);
538 	/* Remove the PID file if we created it */
539 	janus_pidfile_remove();
540 	/* Close the logger */
541 	janus_log_destroy();
542 	/* Get rid of external loggers too, if any */
543 	if(loggers != NULL && g_hash_table_size(loggers) > 0) {
544 		g_hash_table_foreach(loggers, janus_logger_close, NULL);
545 		g_hash_table_destroy(loggers);
546 	}
547 	if(loggers_so != NULL && g_hash_table_size(loggers_so) > 0) {
548 		g_hash_table_foreach(loggers_so, janus_loggerso_close, NULL);
549 		g_hash_table_destroy(loggers_so);
550 	}
551 	/* If we're daemonizing, we send an error code to the parent */
552 	if(daemonize) {
553 		int code = 1;
554 		ssize_t res = 0;
555 		do {
556 			res = write(pipefd[1], &code, sizeof(int));
557 		} while(res == -1 && errno == EINTR);
558 	}
559 }
560 
561 
562 /** @name Transport plugin callback interface
563  * These are the callbacks implemented by the Janus core, as part of
564  * the janus_transport_callbacks interface. Everything the transport
565  * plugins send the core is handled here.
566  */
567 ///@{
568 void janus_transport_incoming_request(janus_transport *plugin, janus_transport_session *transport, void *request_id, gboolean admin, json_t *message, json_error_t *error);
569 void janus_transport_gone(janus_transport *plugin, janus_transport_session *transport);
570 gboolean janus_transport_is_api_secret_needed(janus_transport *plugin);
571 gboolean janus_transport_is_api_secret_valid(janus_transport *plugin, const char *apisecret);
572 gboolean janus_transport_is_auth_token_needed(janus_transport *plugin);
573 gboolean janus_transport_is_auth_token_valid(janus_transport *plugin, const char *token);
574 void janus_transport_notify_event(janus_transport *plugin, void *transport, json_t *event);
575 
576 static janus_transport_callbacks janus_handler_transport =
577 	{
578 		.incoming_request = janus_transport_incoming_request,
579 		.transport_gone = janus_transport_gone,
580 		.is_api_secret_needed = janus_transport_is_api_secret_needed,
581 		.is_api_secret_valid = janus_transport_is_api_secret_valid,
582 		.is_auth_token_needed = janus_transport_is_auth_token_needed,
583 		.is_auth_token_valid = janus_transport_is_auth_token_valid,
584 		.events_is_enabled = janus_events_is_enabled,
585 		.notify_event = janus_transport_notify_event,
586 	};
587 static GAsyncQueue *requests = NULL;
588 static janus_request exit_message;
589 static GThreadPool *tasks = NULL;
590 void janus_transport_task(gpointer data, gpointer user_data);
591 ///@}
592 
593 
594 /** @name Plugin callback interface
595  * These are the callbacks implemented by the Janus core, as part of
596  * the janus_callbacks interface. Everything the plugins send the
597  * core is handled here.
598  */
599 ///@{
600 int janus_plugin_push_event(janus_plugin_session *plugin_session, janus_plugin *plugin, const char *transaction, json_t *message, json_t *jsep);
601 json_t *janus_plugin_handle_sdp(janus_plugin_session *plugin_session, janus_plugin *plugin, const char *sdp_type, const char *sdp, gboolean restart);
602 void janus_plugin_relay_rtp(janus_plugin_session *plugin_session, janus_plugin_rtp *packet);
603 void janus_plugin_relay_rtcp(janus_plugin_session *plugin_session, janus_plugin_rtcp *packet);
604 void janus_plugin_relay_data(janus_plugin_session *plugin_session, janus_plugin_data *message);
605 void janus_plugin_send_pli(janus_plugin_session *plugin_session);
606 void janus_plugin_send_remb(janus_plugin_session *plugin_session, uint32_t bitrate);
607 void janus_plugin_close_pc(janus_plugin_session *plugin_session);
608 void janus_plugin_end_session(janus_plugin_session *plugin_session);
609 void janus_plugin_notify_event(janus_plugin *plugin, janus_plugin_session *plugin_session, json_t *event);
610 gboolean janus_plugin_auth_is_signed(void);
611 gboolean janus_plugin_auth_is_signature_valid(janus_plugin *plugin, const char *token);
612 gboolean janus_plugin_auth_signature_contains(janus_plugin *plugin, const char *token, const char *desc);
613 static janus_callbacks janus_handler_plugin =
614 	{
615 		.push_event = janus_plugin_push_event,
616 		.relay_rtp = janus_plugin_relay_rtp,
617 		.relay_rtcp = janus_plugin_relay_rtcp,
618 		.relay_data = janus_plugin_relay_data,
619 		.send_pli = janus_plugin_send_pli,
620 		.send_remb = janus_plugin_send_remb,
621 		.close_pc = janus_plugin_close_pc,
622 		.end_session = janus_plugin_end_session,
623 		.events_is_enabled = janus_events_is_enabled,
624 		.notify_event = janus_plugin_notify_event,
625 		.auth_is_signed = janus_plugin_auth_is_signed,
626 		.auth_is_signature_valid = janus_plugin_auth_is_signature_valid,
627 		.auth_signature_contains = janus_plugin_auth_signature_contains,
628 	};
629 ///@}
630 
631 
632 /* Core Sessions */
633 static janus_mutex sessions_mutex;
634 static GHashTable *sessions = NULL;
635 static GMainContext *sessions_watchdog_context = NULL;
636 
637 
janus_ice_handle_dereference(janus_ice_handle * handle)638 static void janus_ice_handle_dereference(janus_ice_handle *handle) {
639 	if(handle)
640 		janus_refcount_decrease(&handle->ref);
641 }
642 
janus_session_free(const janus_refcount * session_ref)643 static void janus_session_free(const janus_refcount *session_ref) {
644 	janus_session *session = janus_refcount_containerof(session_ref, janus_session, ref);
645 	/* This session can be destroyed, free all the resources */
646 	if(session->ice_handles != NULL) {
647 		g_hash_table_destroy(session->ice_handles);
648 		session->ice_handles = NULL;
649 	}
650 	if(session->source != NULL) {
651 		janus_request_destroy(session->source);
652 		session->source = NULL;
653 	}
654 	g_free(session);
655 }
656 
janus_session_get_request(janus_session * session)657 static janus_request *janus_session_get_request(janus_session *session) {
658 	if(session == NULL)
659 		return NULL;
660 	janus_mutex_lock(&session->mutex);
661 	janus_request *source = session->source;
662 	if(source != NULL && !g_atomic_int_get(&source->destroyed)) {
663 		janus_refcount_increase(&source->ref);
664 	} else {
665 		source = NULL;
666 	}
667 	janus_mutex_unlock(&session->mutex);
668 	return source;
669 }
janus_request_unref(janus_request * request)670 static void janus_request_unref(janus_request *request) {
671 	if(request)
672 		janus_refcount_decrease(&request->ref);
673 }
674 
janus_check_sessions(gpointer user_data)675 static gboolean janus_check_sessions(gpointer user_data) {
676 	janus_mutex_lock(&sessions_mutex);
677 	if(sessions && g_hash_table_size(sessions) > 0) {
678 		GHashTableIter iter;
679 		gpointer value;
680 		g_hash_table_iter_init(&iter, sessions);
681 		while (g_hash_table_iter_next(&iter, NULL, &value)) {
682 			janus_session *session = (janus_session *) value;
683 			if (!session || g_atomic_int_get(&session->destroyed)) {
684 				continue;
685 			}
686 			gint64 now = janus_get_monotonic_time();
687 
688 			/* Use either session-specific timeout or global. */
689 			gint64 timeout = (gint64)session->timeout;
690 			if (timeout == -1) timeout = (gint64)global_session_timeout;
691 
692 			if ((timeout > 0 && (now - session->last_activity >= timeout * G_USEC_PER_SEC) &&
693 					!g_atomic_int_compare_and_exchange(&session->timedout, 0, 1)) ||
694 					((g_atomic_int_get(&session->transport_gone) && now - session->last_activity >= (gint64)reclaim_session_timeout * G_USEC_PER_SEC) &&
695 							!g_atomic_int_compare_and_exchange(&session->timedout, 0, 1))) {
696 				JANUS_LOG(LOG_INFO, "Timeout expired for session %"SCNu64"...\n", session->session_id);
697 				/* Mark the session as over, we'll deal with it later */
698 				janus_session_handles_clear(session);
699 				/* Notify the transport */
700 				janus_request *source = janus_session_get_request(session);
701 				if(source) {
702 					json_t *event = janus_create_message("timeout", session->session_id, NULL);
703 					/* Send this to the transport client and notify the session's over */
704 					source->transport->send_message(source->instance, NULL, FALSE, event);
705 					source->transport->session_over(source->instance, session->session_id, TRUE, FALSE);
706 				}
707 				janus_request_unref(source);
708 				/* Notify event handlers as well */
709 				if(janus_events_is_enabled())
710 					janus_events_notify_handlers(JANUS_EVENT_TYPE_SESSION, JANUS_EVENT_SUBTYPE_NONE,
711 						session->session_id, "timeout", NULL);
712 
713 				/* FIXME Is this safe? apparently it causes hash table errors on the console */
714 				g_hash_table_iter_remove(&iter);
715 
716 				janus_session_destroy(session);
717 			}
718 		}
719 	}
720 	janus_mutex_unlock(&sessions_mutex);
721 
722 	return G_SOURCE_CONTINUE;
723 }
724 
janus_sessions_watchdog(gpointer user_data)725 static gpointer janus_sessions_watchdog(gpointer user_data) {
726 	GMainLoop *loop = (GMainLoop *) user_data;
727 	GMainContext *watchdog_context = g_main_loop_get_context(loop);
728 	GSource *timeout_source;
729 
730 	timeout_source = g_timeout_source_new_seconds(2);
731 	g_source_set_callback(timeout_source, janus_check_sessions, watchdog_context, NULL);
732 	g_source_attach(timeout_source, watchdog_context);
733 	g_source_unref(timeout_source);
734 
735 	JANUS_LOG(LOG_INFO, "Sessions watchdog started\n");
736 
737 	g_main_loop_run(loop);
738 
739 	JANUS_LOG(LOG_INFO, "Sessions watchdog stopped\n");
740 
741 	return NULL;
742 }
743 
744 
janus_session_create(guint64 session_id)745 janus_session *janus_session_create(guint64 session_id) {
746 	janus_session *session = NULL;
747 	if(session_id == 0) {
748 		while(session_id == 0) {
749 			session_id = janus_random_uint64();
750 			session = janus_session_find(session_id);
751 			if(session != NULL) {
752 				/* Session ID already taken, try another one */
753 				janus_refcount_decrease(&session->ref);
754 				session_id = 0;
755 			}
756 		}
757 	}
758 	session = (janus_session *)g_malloc(sizeof(janus_session));
759 	JANUS_LOG(LOG_INFO, "Creating new session: %"SCNu64"; %p\n", session_id, session);
760 	session->session_id = session_id;
761 	janus_refcount_init(&session->ref, janus_session_free);
762 	session->source = NULL;
763 	session->timeout = -1; /* Negative means rely on global timeout */
764 	g_atomic_int_set(&session->destroyed, 0);
765 	g_atomic_int_set(&session->timedout, 0);
766 	g_atomic_int_set(&session->transport_gone, 0);
767 	session->last_activity = janus_get_monotonic_time();
768 	session->ice_handles = NULL;
769 	janus_mutex_init(&session->mutex);
770 	janus_mutex_lock(&sessions_mutex);
771 	g_hash_table_insert(sessions, janus_uint64_dup(session->session_id), session);
772 	janus_mutex_unlock(&sessions_mutex);
773 	return session;
774 }
775 
janus_session_find(guint64 session_id)776 janus_session *janus_session_find(guint64 session_id) {
777 	janus_mutex_lock(&sessions_mutex);
778 	janus_session *session = g_hash_table_lookup(sessions, &session_id);
779 	if(session != NULL) {
780 		/* A successful find automatically increases the reference counter:
781 		 * it's up to the caller to decrease it again when done */
782 		janus_refcount_increase(&session->ref);
783 	}
784 	janus_mutex_unlock(&sessions_mutex);
785 	return session;
786 }
787 
janus_session_notify_event(janus_session * session,json_t * event)788 void janus_session_notify_event(janus_session *session, json_t *event) {
789 	if(session != NULL && !g_atomic_int_get(&session->destroyed)) {
790 		janus_request *source = janus_session_get_request(session);
791 		if(source != NULL && source->transport != NULL) {
792 			/* Send this to the transport client */
793 			JANUS_LOG(LOG_HUGE, "Sending event to %s (%p)\n", source->transport->get_package(), source->instance);
794 			source->transport->send_message(source->instance, NULL, FALSE, event);
795 		} else {
796 			/* No transport, free the event */
797 			json_decref(event);
798 		}
799 		janus_request_unref(source);
800 	} else {
801 		/* No session, free the event */
802 		json_decref(event);
803 	}
804 }
805 
806 
807 /* Destroys a session but does not remove it from the sessions hash table. */
janus_session_destroy(janus_session * session)808 gint janus_session_destroy(janus_session *session) {
809 	guint64 session_id = session->session_id;
810 	JANUS_LOG(LOG_INFO, "Destroying session %"SCNu64"; %p\n", session_id, session);
811 	if(!g_atomic_int_compare_and_exchange(&session->destroyed, 0, 1))
812 		return 0;
813 	janus_session_handles_clear(session);
814 	/* The session will actually be destroyed when the counter gets to 0 */
815 	janus_refcount_decrease(&session->ref);
816 
817 	return 0;
818 }
819 
janus_session_handles_find(janus_session * session,guint64 handle_id)820 janus_ice_handle *janus_session_handles_find(janus_session *session, guint64 handle_id) {
821 	if(session == NULL)
822 		return NULL;
823 	janus_mutex_lock(&session->mutex);
824 	janus_ice_handle *handle = session->ice_handles ? g_hash_table_lookup(session->ice_handles, &handle_id) : NULL;
825 	if(handle != NULL) {
826 		/* A successful find automatically increases the reference counter:
827 		 * it's up to the caller to decrease it again when done */
828 		janus_refcount_increase(&handle->ref);
829 	}
830 	janus_mutex_unlock(&session->mutex);
831 	return handle;
832 }
833 
janus_session_handles_insert(janus_session * session,janus_ice_handle * handle)834 void janus_session_handles_insert(janus_session *session, janus_ice_handle *handle) {
835 	janus_mutex_lock(&session->mutex);
836 	if(session->ice_handles == NULL)
837 		session->ice_handles = g_hash_table_new_full(g_int64_hash, g_int64_equal, (GDestroyNotify)g_free, (GDestroyNotify)janus_ice_handle_dereference);
838 	janus_refcount_increase(&handle->ref);
839 	g_hash_table_insert(session->ice_handles, janus_uint64_dup(handle->handle_id), handle);
840 	janus_mutex_unlock(&session->mutex);
841 }
842 
janus_session_handles_remove(janus_session * session,janus_ice_handle * handle)843 gint janus_session_handles_remove(janus_session *session, janus_ice_handle *handle) {
844 	janus_mutex_lock(&session->mutex);
845 	gint error = janus_ice_handle_destroy(session, handle);
846 	g_hash_table_remove(session->ice_handles, &handle->handle_id);
847 	janus_mutex_unlock(&session->mutex);
848 	return error;
849 }
850 
janus_session_handles_clear(janus_session * session)851 void janus_session_handles_clear(janus_session *session) {
852 	janus_mutex_lock(&session->mutex);
853 	if(session->ice_handles != NULL && g_hash_table_size(session->ice_handles) > 0) {
854 		GHashTableIter iter;
855 		gpointer value;
856 		/* Remove all handles */
857 		g_hash_table_iter_init(&iter, session->ice_handles);
858 		while (g_hash_table_iter_next(&iter, NULL, &value)) {
859 			janus_ice_handle *handle = value;
860 			if(!handle)
861 				continue;
862 			janus_ice_handle_destroy(session, handle);
863 			g_hash_table_iter_remove(&iter);
864 		}
865 	}
866 	janus_mutex_unlock(&session->mutex);
867 }
868 
janus_session_handles_list_json(janus_session * session)869 json_t *janus_session_handles_list_json(janus_session *session) {
870 	json_t *list = json_array();
871 	janus_mutex_lock(&session->mutex);
872 	if(session->ice_handles != NULL && g_hash_table_size(session->ice_handles) > 0) {
873 		GHashTableIter iter;
874 		gpointer value;
875 		g_hash_table_iter_init(&iter, session->ice_handles);
876 		while (g_hash_table_iter_next(&iter, NULL, &value)) {
877 			janus_ice_handle *handle = value;
878 			if(!handle)
879 				continue;
880 			json_array_append_new(list, json_integer(handle->handle_id));
881 		}
882 	}
883 	janus_mutex_unlock(&session->mutex);
884 	return list;
885 }
886 
887 /* Requests management */
janus_request_free(const janus_refcount * request_ref)888 static void janus_request_free(const janus_refcount *request_ref) {
889 	janus_request *request = janus_refcount_containerof(request_ref, janus_request, ref);
890 	/* This request can be destroyed, free all the resources */
891 	request->transport = NULL;
892 	if(request->instance)
893 		janus_refcount_decrease(&request->instance->ref);
894 	request->instance = NULL;
895 	request->request_id = NULL;
896 	if(request->message)
897 		json_decref(request->message);
898 	request->message = NULL;
899 	g_free(request);
900 }
901 
janus_request_new(janus_transport * transport,janus_transport_session * instance,void * request_id,gboolean admin,json_t * message)902 janus_request *janus_request_new(janus_transport *transport, janus_transport_session *instance, void *request_id, gboolean admin, json_t *message) {
903 	janus_request *request = g_malloc(sizeof(janus_request));
904 	request->transport = transport;
905 	request->instance = instance;
906 	janus_refcount_increase(&instance->ref);
907 	request->request_id = request_id;
908 	request->admin = admin;
909 	request->message = message;
910 	g_atomic_int_set(&request->destroyed, 0);
911 	janus_refcount_init(&request->ref, janus_request_free);
912 	return request;
913 }
914 
janus_request_destroy(janus_request * request)915 void janus_request_destroy(janus_request *request) {
916 	if(request == NULL || request == &exit_message || !g_atomic_int_compare_and_exchange(&request->destroyed, 0, 1))
917 		return;
918 	janus_refcount_decrease(&request->ref);
919 }
920 
janus_request_check_secret(janus_request * request,guint64 session_id,const gchar * transaction_text)921 static int janus_request_check_secret(janus_request *request, guint64 session_id, const gchar *transaction_text) {
922 	gboolean secret_authorized = FALSE, token_authorized = FALSE;
923 	if(api_secret == NULL && !janus_auth_is_enabled()) {
924 		/* Nothing to check */
925 		secret_authorized = TRUE;
926 		token_authorized = TRUE;
927 	} else {
928 		json_t *root = request->message;
929 		if(api_secret != NULL) {
930 			/* There's an API secret, check that the client provided it */
931 			json_t *secret = json_object_get(root, "apisecret");
932 			if(secret && json_is_string(secret) && janus_strcmp_const_time(json_string_value(secret), api_secret)) {
933 				secret_authorized = TRUE;
934 			}
935 		}
936 		if(janus_auth_is_enabled()) {
937 			/* The token based authentication mechanism is enabled, check that the client provided it */
938 			json_t *token = json_object_get(root, "token");
939 			if(token && json_is_string(token) && janus_auth_check_token(json_string_value(token))) {
940 				token_authorized = TRUE;
941 			}
942 		}
943 		/* We consider a request authorized if either the proper API secret or a valid token has been provided */
944 		if(!(api_secret != NULL && secret_authorized) && !(janus_auth_is_enabled() && token_authorized))
945 			return JANUS_ERROR_UNAUTHORIZED;
946 	}
947 	return 0;
948 }
949 
janus_request_ice_handle_answer(janus_ice_handle * handle,int audio,int video,int data,char * jsep_sdp)950 static void janus_request_ice_handle_answer(janus_ice_handle *handle, int audio, int video, int data, char *jsep_sdp) {
951 	/* We got our answer */
952 	janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
953 	/* Any pending trickles? */
954 	if(handle->pending_trickles) {
955 		JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Processing %d pending trickle candidates\n", handle->handle_id, g_list_length(handle->pending_trickles));
956 		GList *temp = NULL;
957 		while(handle->pending_trickles) {
958 			temp = g_list_first(handle->pending_trickles);
959 			handle->pending_trickles = g_list_remove_link(handle->pending_trickles, temp);
960 			janus_ice_trickle *trickle = (janus_ice_trickle *)temp->data;
961 			g_list_free(temp);
962 			if(trickle == NULL)
963 				continue;
964 			if((janus_get_monotonic_time() - trickle->received) > candidates_timeout*G_USEC_PER_SEC) {
965 				/* FIXME Candidate is too old, discard it */
966 				JANUS_LOG(LOG_WARN, "[%"SCNu64"] Discarding candidate (too old)\n", handle->handle_id);
967 				janus_ice_trickle_destroy(trickle);
968 				/* FIXME We should report that */
969 				continue;
970 			}
971 			json_t *candidate = trickle->candidate;
972 			if(candidate == NULL) {
973 				janus_ice_trickle_destroy(trickle);
974 				continue;
975 			}
976 			if(json_is_object(candidate)) {
977 				/* We got a single candidate */
978 				int error = 0;
979 				const char *error_string = NULL;
980 				if((error = janus_ice_trickle_parse(handle, candidate, &error_string)) != 0) {
981 					/* FIXME We should report the error parsing the trickle candidate */
982 				}
983 			} else if(json_is_array(candidate)) {
984 				/* We got multiple candidates in an array */
985 				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Got multiple candidates (%zu)\n", handle->handle_id, json_array_size(candidate));
986 				if(json_array_size(candidate) > 0) {
987 					/* Handle remote candidates */
988 					size_t i = 0;
989 					for(i=0; i<json_array_size(candidate); i++) {
990 						json_t *c = json_array_get(candidate, i);
991 						/* FIXME We don't care if any trickle fails to parse */
992 						janus_ice_trickle_parse(handle, c, NULL);
993 					}
994 				}
995 			}
996 			/* Done, free candidate */
997 			janus_ice_trickle_destroy(trickle);
998 		}
999 	}
1000 
1001 	gboolean candidates_found = (handle->stream && handle->stream->component && g_slist_length(handle->stream->component->candidates) > 0);
1002 	/* This was an answer, check if it's time to start ICE */
1003 	if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE) && !candidates_found) {
1004 		JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- ICE Trickling is supported by the browser, waiting for remote candidates...\n", handle->handle_id);
1005 		janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_START);
1006 	} else {
1007 		JANUS_LOG(LOG_VERB, "[%"SCNu64"] Done! Sending connectivity checks...\n", handle->handle_id);
1008 		janus_ice_setup_remote_candidates(handle, handle->stream_id, 1);
1009 	}
1010 }
1011 
janus_process_incoming_request(janus_request * request)1012 int janus_process_incoming_request(janus_request *request) {
1013 	int ret = -1;
1014 	if(request == NULL) {
1015 		JANUS_LOG(LOG_ERR, "Missing request or payload to process, giving up...\n");
1016 		return ret;
1017 	}
1018 	int error_code = 0;
1019 	char error_cause[100];
1020 	json_t *root = request->message;
1021 	/* Ok, let's start with the ids */
1022 	guint64 session_id = 0, handle_id = 0;
1023 	json_t *s = json_object_get(root, "session_id");
1024 	if(json_is_null(s))
1025 		s = NULL;
1026 	if(s && json_is_integer(s))
1027 		session_id = json_integer_value(s);
1028 	json_t *h = json_object_get(root, "handle_id");
1029 	if(json_is_null(h))
1030 		h = NULL;
1031 	if(h && json_is_integer(h))
1032 		handle_id = json_integer_value(h);
1033 
1034 	janus_session *session = NULL;
1035 	janus_ice_handle *handle = NULL;
1036 
1037 	/* Get transaction and message request */
1038 	JANUS_VALIDATE_JSON_OBJECT(root, incoming_request_parameters,
1039 		error_code, error_cause, FALSE,
1040 		JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1041 	if(error_code != 0) {
1042 		ret = janus_process_error_string(request, session_id, NULL, error_code, error_cause);
1043 		goto jsondone;
1044 	}
1045 	json_t *transaction = json_object_get(root, "transaction");
1046 	const gchar *transaction_text = json_string_value(transaction);
1047 	json_t *message = json_object_get(root, "janus");
1048 	const gchar *message_text = json_string_value(message);
1049 
1050 	if(session_id == 0 && handle_id == 0) {
1051 		/* Can only be a 'Create new session', a 'Get info' or a 'Ping/Pong' request */
1052 		if(!strcasecmp(message_text, "info")) {
1053 			ret = janus_process_success(request, janus_info(transaction_text));
1054 			goto jsondone;
1055 		}
1056 		if(!strcasecmp(message_text, "ping")) {
1057 			/* Prepare JSON reply */
1058 			json_t *reply = janus_create_message("pong", 0, transaction_text);
1059 			ret = janus_process_success(request, reply);
1060 			goto jsondone;
1061 		}
1062 		if(strcasecmp(message_text, "create")) {
1063 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
1064 			goto jsondone;
1065 		}
1066 		/* Make sure we're accepting new sessions */
1067 		if(!accept_new_sessions) {
1068 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_NOT_ACCEPTING_SESSIONS, NULL);
1069 			goto jsondone;
1070 		}
1071 		/* Any secret/token to check? */
1072 		ret = janus_request_check_secret(request, session_id, transaction_text);
1073 		if(ret != 0) {
1074 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED, NULL);
1075 			goto jsondone;
1076 		}
1077 		session_id = 0;
1078 		json_t *id = json_object_get(root, "id");
1079 		if(id != NULL) {
1080 			/* The application provided the session ID to use */
1081 			session_id = json_integer_value(id);
1082 			if(session_id > 0 && (session = janus_session_find(session_id)) != NULL) {
1083 				/* Session ID already taken */
1084 				ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_CONFLICT, "Session ID already in use");
1085 				goto jsondone;
1086 			}
1087 		}
1088 
1089 		/* Handle it */
1090 		session = janus_session_create(session_id);
1091 		if(session == NULL) {
1092 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Memory error");
1093 			goto jsondone;
1094 		}
1095 		session_id = session->session_id;
1096 		/* We increase the counter as this request is using the session */
1097 		janus_refcount_increase(&session->ref);
1098 		/* Take note of the request source that originated this session (HTTP, WebSockets, RabbitMQ?) */
1099 		session->source = janus_request_new(request->transport, request->instance, NULL, FALSE, NULL);
1100 		/* Notify the source that a new session has been created */
1101 		request->transport->session_created(request->instance, session->session_id);
1102 		/* Notify event handlers */
1103 		if(janus_events_is_enabled()) {
1104 			/* Session created, add info on the transport that originated it */
1105 			json_t *transport = json_object();
1106 			json_object_set_new(transport, "transport", json_string(session->source->transport->get_package()));
1107 			char id[32];
1108 			memset(id, 0, sizeof(id));
1109 			/* To avoid sending a stringified version of the transport pointer
1110 			 * around, we convert it to a number and hash it instead */
1111 			uint64_t p = janus_uint64_hash(GPOINTER_TO_UINT(session->source->instance));
1112 			g_snprintf(id, sizeof(id), "%"SCNu64, p);
1113 			json_object_set_new(transport, "id", json_string(id));
1114 			janus_events_notify_handlers(JANUS_EVENT_TYPE_SESSION, JANUS_EVENT_SUBTYPE_NONE,
1115 				session_id, "created", transport);
1116 		}
1117 		/* Prepare JSON reply */
1118 		json_t *reply = janus_create_message("success", 0, transaction_text);
1119 		json_t *data = json_object();
1120 		json_object_set_new(data, "id", json_integer(session_id));
1121 		json_object_set_new(reply, "data", data);
1122 		/* Send the success reply */
1123 		ret = janus_process_success(request, reply);
1124 		goto jsondone;
1125 	}
1126 	if(session_id < 1) {
1127 		JANUS_LOG(LOG_ERR, "Invalid session\n");
1128 		ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, NULL);
1129 		goto jsondone;
1130 	}
1131 	if(h && handle_id < 1) {
1132 		JANUS_LOG(LOG_ERR, "Invalid handle\n");
1133 		ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_HANDLE_NOT_FOUND, NULL);
1134 		goto jsondone;
1135 	}
1136 
1137 	/* Go on with the processing */
1138 	ret = janus_request_check_secret(request, session_id, transaction_text);
1139 	if(ret != 0) {
1140 		ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED, NULL);
1141 		goto jsondone;
1142 	}
1143 
1144 	/* If we got here, make sure we have a session (and/or a handle) */
1145 	session = janus_session_find(session_id);
1146 	if(!session) {
1147 		JANUS_LOG(LOG_ERR, "Couldn't find any session %"SCNu64"...\n", session_id);
1148 		ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, "No such session %"SCNu64"", session_id);
1149 		goto jsondone;
1150 	}
1151 	/* Update the last activity timer */
1152 	session->last_activity = janus_get_monotonic_time();
1153 	handle = NULL;
1154 	if(handle_id > 0) {
1155 		handle = janus_session_handles_find(session, handle_id);
1156 		if(!handle) {
1157 			JANUS_LOG(LOG_ERR, "Couldn't find any handle %"SCNu64" in session %"SCNu64"...\n", handle_id, session_id);
1158 			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);
1159 			goto jsondone;
1160 		}
1161 	}
1162 
1163 	/* What is this? */
1164 	if(!strcasecmp(message_text, "keepalive")) {
1165 		/* Just a keep-alive message, reply with an ack */
1166 		JANUS_LOG(LOG_VERB, "Got a keep-alive on session %"SCNu64"\n", session_id);
1167 		json_t *reply = janus_create_message("ack", session_id, transaction_text);
1168 		/* Send the success reply */
1169 		ret = janus_process_success(request, reply);
1170 	} else if(!strcasecmp(message_text, "attach")) {
1171 		if(handle != NULL) {
1172 			/* Attach is a session-level command */
1173 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
1174 			goto jsondone;
1175 		}
1176 		JANUS_VALIDATE_JSON_OBJECT(root, attach_parameters,
1177 			error_code, error_cause, FALSE,
1178 			JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1179 		if(error_code != 0) {
1180 			ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1181 			goto jsondone;
1182 		}
1183 		json_t *plugin = json_object_get(root, "plugin");
1184 		const gchar *plugin_text = json_string_value(plugin);
1185 		janus_plugin *plugin_t = janus_plugin_find(plugin_text);
1186 		if(plugin_t == NULL) {
1187 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_NOT_FOUND, "No such plugin '%s'", plugin_text);
1188 			goto jsondone;
1189 		}
1190 		/* If the auth token mechanism is enabled, we should check if this token can access this plugin */
1191 		const char *token_value = NULL;
1192 		if(janus_auth_is_enabled()) {
1193 			json_t *token = json_object_get(root, "token");
1194 			if(token != NULL) {
1195 				token_value = json_string_value(token);
1196 				if(token_value && !janus_auth_check_plugin(token_value, plugin_t)) {
1197 					JANUS_LOG(LOG_ERR, "Token '%s' can't access plugin '%s'\n", token_value, plugin_text);
1198 					ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED_PLUGIN, "Provided token can't access plugin '%s'", plugin_text);
1199 					goto jsondone;
1200 				}
1201 			}
1202 		}
1203 		json_t *opaque = json_object_get(root, "opaque_id");
1204 		const char *opaque_id = opaque ? json_string_value(opaque) : NULL;
1205 		json_t *loop = json_object_get(root, "loop_index");
1206 		int loop_index = loop ? json_integer_value(loop) : -1;
1207 		/* Create handle */
1208 		handle = janus_ice_handle_create(session, opaque_id, token_value);
1209 		if(handle == NULL) {
1210 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Memory error");
1211 			goto jsondone;
1212 		}
1213 		handle_id = handle->handle_id;
1214 		/* We increase the counter as this request is using the handle */
1215 		janus_refcount_increase(&handle->ref);
1216 		/* Attach to the plugin */
1217 		int error = 0;
1218 		if((error = janus_ice_handle_attach_plugin(session, handle, plugin_t, loop_index)) != 0) {
1219 			/* TODO Make error struct to pass verbose information */
1220 			janus_session_handles_remove(session, handle);
1221 			JANUS_LOG(LOG_ERR, "Couldn't attach to plugin '%s', error '%d'\n", plugin_text, error);
1222 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_ATTACH, "Couldn't attach to plugin: error '%d'", error);
1223 			goto jsondone;
1224 		}
1225 		/* Prepare JSON reply */
1226 		json_t *reply = janus_create_message("success", session_id, transaction_text);
1227 		json_t *data = json_object();
1228 		json_object_set_new(data, "id", json_integer(handle_id));
1229 		json_object_set_new(reply, "data", data);
1230 		/* Send the success reply */
1231 		ret = janus_process_success(request, reply);
1232 	} else if(!strcasecmp(message_text, "destroy")) {
1233 		if(handle != NULL) {
1234 			/* Query is a session-level command */
1235 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
1236 			goto jsondone;
1237 		}
1238 		janus_mutex_lock(&sessions_mutex);
1239 		g_hash_table_remove(sessions, &session->session_id);
1240 		janus_mutex_unlock(&sessions_mutex);
1241 		/* Notify the source that the session has been destroyed */
1242 		janus_request *source = janus_session_get_request(session);
1243 		if(source && source->transport)
1244 			source->transport->session_over(source->instance, session->session_id, FALSE, FALSE);
1245 		janus_request_unref(source);
1246 
1247 		/* Schedule the session for deletion */
1248 		janus_session_destroy(session);
1249 
1250 		/* Prepare JSON reply */
1251 		json_t *reply = janus_create_message("success", session_id, transaction_text);
1252 		/* Send the success reply */
1253 		ret = janus_process_success(request, reply);
1254 		/* Notify event handlers as well */
1255 		if(janus_events_is_enabled())
1256 			janus_events_notify_handlers(JANUS_EVENT_TYPE_SESSION, JANUS_EVENT_SUBTYPE_NONE,
1257 				session_id, "destroyed", NULL);
1258 	} else if(!strcasecmp(message_text, "detach")) {
1259 		if(handle == NULL) {
1260 			/* Query is an handle-level command */
1261 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
1262 			goto jsondone;
1263 		}
1264 		if(handle->app == NULL || handle->app_handle == NULL) {
1265 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_DETACH, "No plugin to detach from");
1266 			goto jsondone;
1267 		}
1268 		int error = janus_session_handles_remove(session, handle);
1269 		if(error != 0) {
1270 			/* TODO Make error struct to pass verbose information */
1271 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_DETACH, "Couldn't detach from plugin: error '%d'", error);
1272 			/* TODO Delete handle instance */
1273 			goto jsondone;
1274 		}
1275 		/* Prepare JSON reply */
1276 		json_t *reply = janus_create_message("success", session_id, transaction_text);
1277 		/* Send the success reply */
1278 		ret = janus_process_success(request, reply);
1279 	} else if(!strcasecmp(message_text, "hangup")) {
1280 		if(handle == NULL) {
1281 			/* Query is an handle-level command */
1282 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
1283 			goto jsondone;
1284 		}
1285 		if(handle->app == NULL || handle->app_handle == NULL) {
1286 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_DETACH, "No plugin attached");
1287 			goto jsondone;
1288 		}
1289 		janus_ice_webrtc_hangup(handle, "Janus API");
1290 		/* Prepare JSON reply */
1291 		json_t *reply = janus_create_message("success", session_id, transaction_text);
1292 		/* Send the success reply */
1293 		ret = janus_process_success(request, reply);
1294 	} else if(!strcasecmp(message_text, "claim")) {
1295 		janus_mutex_lock(&session->mutex);
1296 		if(session->source != NULL) {
1297 			/* If we're claiming from the same transport, ignore */
1298 			if(session->source->instance == request->instance) {
1299 				janus_mutex_unlock(&session->mutex);
1300 				/* Prepare JSON reply */
1301 				json_t *reply = json_object();
1302 				json_object_set_new(reply, "janus", json_string("success"));
1303 				json_object_set_new(reply, "session_id", json_integer(session_id));
1304 				json_object_set_new(reply, "transaction", json_string(transaction_text));
1305 				/* Send the success reply */
1306 				ret = janus_process_success(request, reply);
1307 				goto jsondone;
1308 			}
1309 			/* Notify the old transport that this session is over for them, but has been reclaimed */
1310 			session->source->transport->session_over(session->source->instance, session->session_id, FALSE, TRUE);
1311 			janus_request_destroy(session->source);
1312 			session->source = NULL;
1313 		}
1314 		session->source = janus_request_new(request->transport, request->instance, NULL, FALSE, NULL);
1315 		/* Notify the new transport that it has claimed a session */
1316 		session->source->transport->session_claimed(session->source->instance, session->session_id);
1317 		/* Previous transport may be gone, clear flag */
1318 		g_atomic_int_set(&session->transport_gone, 0);
1319 		janus_mutex_unlock(&session->mutex);
1320 		/* Prepare JSON reply */
1321 		json_t *reply = json_object();
1322 		json_object_set_new(reply, "janus", json_string("success"));
1323 		json_object_set_new(reply, "session_id", json_integer(session_id));
1324 		json_object_set_new(reply, "transaction", json_string(transaction_text));
1325 		/* Send the success reply */
1326 		ret = janus_process_success(request, reply);
1327 	} else if(!strcasecmp(message_text, "message")) {
1328 		if(handle == NULL) {
1329 			/* Query is an handle-level command */
1330 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
1331 			goto jsondone;
1332 		}
1333 		if(handle->app == NULL || handle->app_handle == NULL) {
1334 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "No plugin to handle this message");
1335 			goto jsondone;
1336 		}
1337 		janus_plugin *plugin_t = (janus_plugin *)handle->app;
1338 		JANUS_LOG(LOG_VERB, "[%"SCNu64"] There's a message for %s\n", handle->handle_id, plugin_t->get_name());
1339 		JANUS_VALIDATE_JSON_OBJECT(root, body_parameters,
1340 			error_code, error_cause, FALSE,
1341 			JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1342 		if(error_code != 0) {
1343 			ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1344 			goto jsondone;
1345 		}
1346 		json_t *body = json_object_get(root, "body");
1347 		/* Is there an SDP attached? */
1348 		json_t *jsep = json_object_get(root, "jsep");
1349 		char *jsep_type = NULL;
1350 		char *jsep_sdp = NULL, *jsep_sdp_stripped = NULL;
1351 		gboolean renegotiation = FALSE;
1352 		if(jsep != NULL) {
1353 			if(!json_is_object(jsep)) {
1354 				ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_JSON_OBJECT, "Invalid jsep object");
1355 				goto jsondone;
1356 			}
1357 			JANUS_VALIDATE_JSON_OBJECT_FORMAT("JSEP error: missing mandatory element (%s)",
1358 				"JSEP error: invalid element type (%s should be %s)",
1359 				jsep, jsep_parameters, error_code, error_cause, FALSE,
1360 				JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1361 			if(error_code != 0) {
1362 				ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1363 				goto jsondone;
1364 			}
1365 			json_t *type = json_object_get(jsep, "type");
1366 			jsep_type = g_strdup(json_string_value(type));
1367 			type = NULL;
1368 			json_t *jsep_trickle = json_object_get(jsep, "trickle");
1369 			gboolean do_trickle = jsep_trickle ? json_is_true(jsep_trickle) : TRUE;
1370 			json_t *jsep_rids = json_object_get(jsep, "rid_order");
1371 			gboolean rids_hml = TRUE;
1372 			if(jsep_rids != NULL) {
1373 				const char *jsep_rids_value = json_string_value(jsep_rids);
1374 				if(jsep_rids_value != NULL) {
1375 					if(!strcasecmp(jsep_rids_value, "hml")) {
1376 						rids_hml = TRUE;
1377 					} else if(!strcasecmp(jsep_rids_value, "lmh")) {
1378 						rids_hml = FALSE;
1379 					} else {
1380 						JANUS_LOG(LOG_WARN, "[%"SCNu64"] Invalid 'rid_order' value, falling back to 'hml'\n", handle->handle_id);
1381 					}
1382 				}
1383 				json_object_del(jsep, "rid_order");
1384 			}
1385 			json_t *jsep_e2ee = json_object_get(jsep, "e2ee");
1386 			gboolean e2ee = jsep_e2ee ? json_is_true(jsep_e2ee) : FALSE;
1387 			/* Are we still cleaning up from a previous media session? */
1388 			if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING)) {
1389 				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Still cleaning up from a previous media session, let's wait a bit...\n", handle->handle_id);
1390 				gint64 waited = 0;
1391 				while(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING)) {
1392 					g_usleep(100000);
1393 					waited += 100000;
1394 					if(waited >= 3*G_USEC_PER_SEC) {
1395 						JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Waited 3 seconds, that's enough!\n", handle->handle_id);
1396 						ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_WEBRTC_STATE, "Still cleaning a previous session");
1397 						goto jsondone;
1398 					}
1399 				}
1400 			}
1401 			/* Check if we're renegotiating (if we have an answer, we did an offer/answer round already) */
1402 			renegotiation = janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_NEGOTIATED);
1403 			/* Check the JSEP type */
1404 			janus_mutex_lock(&handle->mutex);
1405 			int offer = 0;
1406 			if(!strcasecmp(jsep_type, "offer")) {
1407 				offer = 1;
1408 				janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1409 				janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER);
1410 				janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER);
1411 			} else if(!strcasecmp(jsep_type, "answer")) {
1412 				janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER);
1413 				if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER))
1414 					janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_NEGOTIATED);
1415 				offer = 0;
1416 			} else {
1417 				/* TODO Handle other message types as well */
1418 				ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_JSEP_UNKNOWN_TYPE, "JSEP error: unknown message type '%s'", jsep_type);
1419 				g_free(jsep_type);
1420 				janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1421 				janus_mutex_unlock(&handle->mutex);
1422 				goto jsondone;
1423 			}
1424 			json_t *sdp = json_object_get(jsep, "sdp");
1425 			jsep_sdp = (char *)json_string_value(sdp);
1426 			JANUS_LOG(LOG_VERB, "[%"SCNu64"] Remote SDP:\n%s", handle->handle_id, jsep_sdp);
1427 			/* Is this valid SDP? */
1428 			char error_str[512];
1429 			error_str[0] = '\0';
1430 			int audio = 0, video = 0, data = 0;
1431 			janus_sdp *parsed_sdp = janus_sdp_preparse(handle, jsep_sdp, error_str, sizeof(error_str), &audio, &video, &data);
1432 			if(parsed_sdp == NULL) {
1433 				/* Invalid SDP */
1434 				ret = janus_process_error_string(request, session_id, transaction_text, JANUS_ERROR_JSEP_INVALID_SDP, error_str);
1435 				g_free(jsep_type);
1436 				janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1437 				janus_mutex_unlock(&handle->mutex);
1438 				goto jsondone;
1439 			}
1440 			/* Notify event handlers */
1441 			if(janus_events_is_enabled()) {
1442 				janus_events_notify_handlers(JANUS_EVENT_TYPE_JSEP, JANUS_EVENT_SUBTYPE_NONE,
1443 					session_id, handle_id, handle->opaque_id, "remote", jsep_type, jsep_sdp);
1444 			}
1445 			/* FIXME We're only handling single audio/video lines for now... */
1446 			JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio %s been negotiated, Video %s been negotiated, SCTP/DataChannels %s been negotiated\n",
1447 			                    handle->handle_id,
1448 			                    audio ? "has" : "has NOT",
1449 			                    video ? "has" : "has NOT",
1450 			                    data ? "have" : "have NOT");
1451 			if(audio > 1) {
1452 				JANUS_LOG(LOG_WARN, "[%"SCNu64"] More than one audio line? only going to negotiate one...\n", handle->handle_id);
1453 			}
1454 			if(video > 1) {
1455 				JANUS_LOG(LOG_WARN, "[%"SCNu64"] More than one video line? only going to negotiate one...\n", handle->handle_id);
1456 			}
1457 			if(data > 1) {
1458 				JANUS_LOG(LOG_WARN, "[%"SCNu64"] More than one data line? only going to negotiate one...\n", handle->handle_id);
1459 			}
1460 #ifndef HAVE_SCTP
1461 			if(data) {
1462 				JANUS_LOG(LOG_WARN, "[%"SCNu64"]   -- DataChannels have been negotiated, but support for them has not been compiled...\n", handle->handle_id);
1463 			}
1464 #endif
1465 			/* We behave differently if it's a new session or an update... */
1466 			if(!renegotiation) {
1467 				/* New session */
1468 				if(offer) {
1469 					/* Setup ICE locally (we received an offer) */
1470 					if(janus_ice_setup_local(handle, offer, audio, video, data, do_trickle) < 0) {
1471 						JANUS_LOG(LOG_ERR, "Error setting ICE locally\n");
1472 						janus_sdp_destroy(parsed_sdp);
1473 						g_free(jsep_type);
1474 						janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1475 						ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Error setting ICE locally");
1476 						janus_mutex_unlock(&handle->mutex);
1477 						goto jsondone;
1478 					}
1479 				} else {
1480 					/* Make sure we're waiting for an ANSWER in the first place */
1481 					if(!handle->agent) {
1482 						JANUS_LOG(LOG_ERR, "Unexpected ANSWER (did we offer?)\n");
1483 						janus_sdp_destroy(parsed_sdp);
1484 						g_free(jsep_type);
1485 						janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1486 						ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNEXPECTED_ANSWER, "Unexpected ANSWER (did we offer?)");
1487 						janus_mutex_unlock(&handle->mutex);
1488 						goto jsondone;
1489 					}
1490 				}
1491 				/* If for some reason the user is asking us to force using a relay, do that. Notice
1492 				 * that this only works with libnice >= 0.1.14, and will cause the PeerConnection
1493 				 * to fail if Janus itself is not configured to use a TURN server. Don't use this
1494 				 * feature if you don't know what you're doing! You will almost always NOT want
1495 				 * Janus itself to use TURN: https://janus.conf.meetecho.com/docs/FAQ.html#turn */
1496 				if(json_is_true(json_object_get(jsep, "force_relay"))) {
1497 					if(!janus_ice_is_force_relay_allowed()) {
1498 						JANUS_LOG(LOG_WARN, "[%"SCNu64"] Forcing Janus to use a TURN server is not allowed\n", handle->handle_id);
1499 					} else {
1500 						JANUS_LOG(LOG_VERB, "[%"SCNu64"] Forcing Janus to use a TURN server\n", handle->handle_id);
1501 						g_object_set(G_OBJECT(handle->agent), "force-relay", TRUE, NULL);
1502 					}
1503 				}
1504 				/* Process the remote SDP */
1505 				if(janus_sdp_process(handle, parsed_sdp, rids_hml, FALSE) < 0) {
1506 					JANUS_LOG(LOG_ERR, "Error processing SDP\n");
1507 					janus_sdp_destroy(parsed_sdp);
1508 					g_free(jsep_type);
1509 					janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1510 					ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_JSEP_INVALID_SDP, "Error processing SDP");
1511 					janus_mutex_unlock(&handle->mutex);
1512 					goto jsondone;
1513 				}
1514 				if(!offer) {
1515 					/* Set remote candidates now (we received an answer) */
1516 					if(do_trickle) {
1517 						janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE);
1518 					} else {
1519 						janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE);
1520 					}
1521 					janus_request_ice_handle_answer(handle, audio, video, data, jsep_sdp);
1522 					/* Check if the answer does contain the mid/abs-send-time/twcc extmaps */
1523 					gboolean do_mid = FALSE, do_twcc = FALSE, do_abs_send_time = FALSE;
1524 					GList *temp = parsed_sdp->m_lines;
1525 					while(temp) {
1526 						janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
1527 						GList *tempA = m->attributes;
1528 						while(tempA) {
1529 							janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
1530 							if(a->name && a->value && !strcasecmp(a->name, "extmap")) {
1531 								if(strstr(a->value, JANUS_RTP_EXTMAP_MID))
1532 									do_mid = TRUE;
1533 								else if(strstr(a->value, JANUS_RTP_EXTMAP_TRANSPORT_WIDE_CC))
1534 									do_twcc = TRUE;
1535 								else if(strstr(a->value, JANUS_RTP_EXTMAP_ABS_SEND_TIME))
1536 									do_abs_send_time = TRUE;
1537 							}
1538 							tempA = tempA->next;
1539 						}
1540 						temp = temp->next;
1541 					}
1542 					if(!do_mid && handle->stream)
1543 						handle->stream->mid_ext_id = 0;
1544 					if(!do_twcc && handle->stream) {
1545 						handle->stream->do_transport_wide_cc = FALSE;
1546 						handle->stream->transport_wide_cc_ext_id = 0;
1547 					}
1548 					if(!do_abs_send_time && handle->stream)
1549 						handle->stream->abs_send_time_ext_id = 0;
1550 				} else {
1551 					/* Check if the mid RTP extension is being negotiated */
1552 					handle->stream->mid_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_MID);
1553 					/* Check if the RTP Stream ID extension is being negotiated */
1554 					handle->stream->rid_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_RID);
1555 					handle->stream->ridrtx_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_REPAIRED_RID);
1556 					/* Check if the audio level ID extension is being negotiated */
1557 					handle->stream->audiolevel_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_AUDIO_LEVEL);
1558 					/* Check if the video orientation ID extension is being negotiated */
1559 					handle->stream->videoorientation_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_VIDEO_ORIENTATION);
1560 					/* Check if the abs-send-time ID extension is being negotiated */
1561 					handle->stream->abs_send_time_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_ABS_SEND_TIME);
1562 					/* Check if transport wide CC is supported */
1563 					int transport_wide_cc_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_TRANSPORT_WIDE_CC);
1564 					handle->stream->do_transport_wide_cc = transport_wide_cc_ext_id > 0 ? TRUE : FALSE;
1565 					handle->stream->transport_wide_cc_ext_id = transport_wide_cc_ext_id;
1566 				}
1567 			} else {
1568 				/* FIXME This is a renegotiation: we can currently only handle simple changes in media
1569 				 * direction and ICE restarts: anything more complex than that will result in an error */
1570 				JANUS_LOG(LOG_INFO, "[%"SCNu64"] Negotiation update, checking what changed...\n", handle->handle_id);
1571 				if(janus_sdp_process(handle, parsed_sdp, rids_hml, TRUE) < 0) {
1572 					JANUS_LOG(LOG_ERR, "Error processing SDP\n");
1573 					janus_sdp_destroy(parsed_sdp);
1574 					g_free(jsep_type);
1575 					janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1576 					ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNEXPECTED_ANSWER, "Error processing SDP");
1577 					janus_mutex_unlock(&handle->mutex);
1578 					goto jsondone;
1579 				}
1580 				if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ICE_RESTART)) {
1581 					JANUS_LOG(LOG_INFO, "[%"SCNu64"] Restarting ICE...\n", handle->handle_id);
1582 					/* FIXME We only need to do that for offers: if it's an answer, we did that already */
1583 					if(offer) {
1584 						janus_ice_restart(handle);
1585 					} else {
1586 						janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ICE_RESTART);
1587 					}
1588 					/* Update remote credentials for ICE */
1589 					if(handle->stream) {
1590 						nice_agent_set_remote_credentials(handle->agent, handle->stream->stream_id,
1591 							handle->stream->ruser, handle->stream->rpass);
1592 					}
1593 					/* If we're full-trickling, we'll need to resend the candidates later */
1594 					if(janus_ice_is_full_trickle_enabled()) {
1595 						janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RESEND_TRICKLES);
1596 					}
1597 				}
1598 #ifdef HAVE_SCTP
1599 				if(!offer) {
1600 					/* Were datachannels just added? */
1601 					if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS)) {
1602 						janus_ice_stream *stream = handle->stream;
1603 						if(stream != NULL && stream->component != NULL
1604 								&& stream->component->dtls != NULL && stream->component->dtls->sctp == NULL) {
1605 							/* Create SCTP association as well */
1606 							JANUS_LOG(LOG_VERB, "[%"SCNu64"] Creating datachannels...\n", handle->handle_id);
1607 							janus_dtls_srtp_create_sctp(stream->component->dtls);
1608 						}
1609 					}
1610 				}
1611 #endif
1612 				/* Check if renegotiating has added new RTP extensions */
1613 				if(offer) {
1614 					/* Check if the mid RTP extension is being negotiated */
1615 					handle->stream->mid_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_MID);
1616 					/* Check if the RTP Stream ID extension is being negotiated */
1617 					handle->stream->rid_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_RID);
1618 					handle->stream->ridrtx_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_REPAIRED_RID);
1619 					/* Check if the audio level ID extension is being negotiated */
1620 					handle->stream->audiolevel_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_AUDIO_LEVEL);
1621 					/* Check if the video orientation ID extension is being negotiated */
1622 					handle->stream->videoorientation_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_VIDEO_ORIENTATION);
1623 					/* Check if the abs-send-time ID extension is being negotiated */
1624 					handle->stream->abs_send_time_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_ABS_SEND_TIME);
1625 					/* Check if transport wide CC is supported */
1626 					int transport_wide_cc_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_TRANSPORT_WIDE_CC);
1627 					handle->stream->do_transport_wide_cc = transport_wide_cc_ext_id > 0 ? TRUE : FALSE;
1628 					handle->stream->transport_wide_cc_ext_id = transport_wide_cc_ext_id;
1629 				}
1630 			}
1631 			char *tmp = handle->remote_sdp;
1632 			handle->remote_sdp = g_strdup(jsep_sdp);
1633 			g_free(tmp);
1634 			janus_mutex_unlock(&handle->mutex);
1635 			/* Anonymize SDP */
1636 			if(janus_sdp_anonymize(parsed_sdp) < 0) {
1637 				/* Invalid SDP */
1638 				ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_JSEP_INVALID_SDP, "JSEP error: invalid SDP");
1639 				janus_sdp_destroy(parsed_sdp);
1640 				g_free(jsep_type);
1641 				janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1642 				goto jsondone;
1643 			}
1644 			jsep_sdp_stripped = janus_sdp_write(parsed_sdp);
1645 			janus_sdp_destroy(parsed_sdp);
1646 			sdp = NULL;
1647 			if(e2ee)
1648 				janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_E2EE);
1649 			janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1650 		}
1651 
1652 		/* Make sure the app handle is still valid */
1653 		if(handle->app == NULL || !janus_plugin_session_is_alive(handle->app_handle)) {
1654 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "No plugin to handle this message");
1655 			g_free(jsep_type);
1656 			g_free(jsep_sdp_stripped);
1657 			janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1658 			goto jsondone;
1659 		}
1660 
1661 		/* Send the message to the plugin (which must eventually free transaction_text and unref the two objects, body and jsep) */
1662 		json_incref(body);
1663 		json_t *body_jsep = NULL;
1664 		if(jsep_sdp_stripped) {
1665 			body_jsep = json_pack("{ssss}", "type", jsep_type, "sdp", jsep_sdp_stripped);
1666 			/* Check if simulcasting is enabled */
1667 			if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO)) {
1668 				if(handle->stream && (handle->stream->rid[0] || handle->stream->video_ssrc_peer[1])) {
1669 					json_t *simulcast = json_object();
1670 					/* If we have rids, pass those, otherwise pass the SSRCs */
1671 					if(handle->stream->rid[0]) {
1672 						json_t *rids = json_array();
1673 						if(handle->stream->rid[2])
1674 							json_array_append_new(rids, json_string(handle->stream->rid[2]));
1675 						if(handle->stream->rid[1])
1676 							json_array_append_new(rids, json_string(handle->stream->rid[1]));
1677 						json_array_append_new(rids, json_string(handle->stream->rid[0]));
1678 						json_object_set_new(simulcast, "rids", rids);
1679 						json_object_set_new(simulcast, "rid-ext", json_integer(handle->stream->rid_ext_id));
1680 					} else {
1681 						json_t *ssrcs = json_array();
1682 						json_array_append_new(ssrcs, json_integer(handle->stream->video_ssrc_peer[0]));
1683 						if(handle->stream->video_ssrc_peer[1])
1684 							json_array_append_new(ssrcs, json_integer(handle->stream->video_ssrc_peer[1]));
1685 						if(handle->stream->video_ssrc_peer[2])
1686 							json_array_append_new(ssrcs, json_integer(handle->stream->video_ssrc_peer[2]));
1687 						json_object_set_new(simulcast, "ssrcs", ssrcs);
1688 					}
1689 					json_object_set_new(body_jsep, "simulcast", simulcast);
1690 				}
1691 			}
1692 			/* Check if this is a renegotiation or update */
1693 			if(renegotiation)
1694 				json_object_set_new(body_jsep, "update", json_true());
1695 			/* If media is encrypted end-to-end, the plugin may need to know */
1696 			if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_E2EE))
1697 				json_object_set_new(body_jsep, "e2ee", json_true());
1698 		}
1699 		janus_plugin_result *result = plugin_t->handle_message(handle->app_handle,
1700 			g_strdup((char *)transaction_text), body, body_jsep);
1701 		g_free(jsep_type);
1702 		g_free(jsep_sdp_stripped);
1703 		if(result == NULL) {
1704 			/* Something went horribly wrong! */
1705 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "Plugin didn't give a result");
1706 			goto jsondone;
1707 		}
1708 		if(result->type == JANUS_PLUGIN_OK) {
1709 			/* The plugin gave a result already (synchronous request/response) */
1710 			if(result->content == NULL || !json_is_object(result->content)) {
1711 				/* Missing content, or not a JSON object */
1712 				ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE,
1713 					result->content == NULL ?
1714 						"Plugin didn't provide any content for this synchronous response" :
1715 						"Plugin returned an invalid JSON response");
1716 				janus_plugin_result_destroy(result);
1717 				goto jsondone;
1718 			}
1719 			/* Reference the content, as destroying the result instance will decref it */
1720 			json_incref(result->content);
1721 			/* Prepare JSON response */
1722 			json_t *reply = janus_create_message("success", session->session_id, transaction_text);
1723 			json_object_set_new(reply, "sender", json_integer(handle->handle_id));
1724 			if(janus_is_opaqueid_in_api_enabled() && handle->opaque_id != NULL)
1725 				json_object_set_new(reply, "opaque_id", json_string(handle->opaque_id));
1726 			json_t *plugin_data = json_object();
1727 			json_object_set_new(plugin_data, "plugin", json_string(plugin_t->get_package()));
1728 			json_object_set_new(plugin_data, "data", result->content);
1729 			json_object_set_new(reply, "plugindata", plugin_data);
1730 			/* Send the success reply */
1731 			ret = janus_process_success(request, reply);
1732 		} else if(result->type == JANUS_PLUGIN_OK_WAIT) {
1733 			/* The plugin received the request but didn't process it yet, send an ack (asynchronous notifications may follow) */
1734 			json_t *reply = janus_create_message("ack", session_id, transaction_text);
1735 			if(result->text)
1736 				json_object_set_new(reply, "hint", json_string(result->text));
1737 			/* Send the success reply */
1738 			ret = janus_process_success(request, reply);
1739 		} else {
1740 			/* Something went horribly wrong! */
1741 			ret = janus_process_error_string(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE,
1742 				(char *)(result->text ? result->text : "Plugin returned a severe (unknown) error"));
1743 			janus_plugin_result_destroy(result);
1744 			goto jsondone;
1745 		}
1746 		janus_plugin_result_destroy(result);
1747 	} else if(!strcasecmp(message_text, "trickle")) {
1748 		if(handle == NULL) {
1749 			/* Trickle is an handle-level command */
1750 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
1751 			goto jsondone;
1752 		}
1753 		if(handle->app == NULL || !janus_plugin_session_is_alive(handle->app_handle)) {
1754 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "No plugin to handle this trickle candidate");
1755 			goto jsondone;
1756 		}
1757 		json_t *candidate = json_object_get(root, "candidate");
1758 		json_t *candidates = json_object_get(root, "candidates");
1759 		if(candidate == NULL && candidates == NULL) {
1760 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "Missing mandatory element (candidate|candidates)");
1761 			goto jsondone;
1762 		}
1763 		if(candidate != NULL && candidates != NULL) {
1764 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_JSON, "Can't have both candidate and candidates");
1765 			goto jsondone;
1766 		}
1767 		if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING)) {
1768 			JANUS_LOG(LOG_ERR, "[%"SCNu64"] Received a trickle, but still cleaning a previous session\n", handle->handle_id);
1769 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_WEBRTC_STATE, "Still cleaning a previous session");
1770 			goto jsondone;
1771 		}
1772 		janus_mutex_lock(&handle->mutex);
1773 		if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE)) {
1774 			/* It looks like this peer supports Trickle, after all */
1775 			JANUS_LOG(LOG_VERB, "Handle %"SCNu64" supports trickle even if it didn't negotiate it...\n", handle->handle_id);
1776 			janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE);
1777 		}
1778 		/* Is there any stream ready? this trickle may get here before the SDP it relates to */
1779 		if(handle->stream == NULL) {
1780 			JANUS_LOG(LOG_WARN, "[%"SCNu64"] No stream, queueing this trickle as it got here before the SDP...\n", handle->handle_id);
1781 			/* Enqueue this trickle candidate(s), we'll process this later */
1782 			janus_ice_trickle *early_trickle = janus_ice_trickle_new(transaction_text, candidate ? candidate : candidates);
1783 			handle->pending_trickles = g_list_append(handle->pending_trickles, early_trickle);
1784 			/* Send the ack right away, an event will tell the application if the candidate(s) failed */
1785 			goto trickledone;
1786 		}
1787 		/* Is the ICE stack ready already? */
1788 		if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER) ||
1789 				!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER) ||
1790 				!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER)) {
1791 			const char *cause = NULL;
1792 			if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER))
1793 				cause = "processing the offer";
1794 			else if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER))
1795 				cause = "waiting for the answer";
1796 			else if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER))
1797 				cause = "waiting for the offer";
1798 			JANUS_LOG(LOG_VERB, "[%"SCNu64"] Still %s, queueing this trickle to wait until we're done there...\n",
1799 				handle->handle_id, cause);
1800 			/* Enqueue this trickle candidate(s), we'll process this later */
1801 			janus_ice_trickle *early_trickle = janus_ice_trickle_new(transaction_text, candidate ? candidate : candidates);
1802 			handle->pending_trickles = g_list_append(handle->pending_trickles, early_trickle);
1803 			/* Send the ack right away, an event will tell the application if the candidate(s) failed */
1804 			goto trickledone;
1805 		}
1806 		if(candidate != NULL) {
1807 			/* We got a single candidate */
1808 			int error = 0;
1809 			const char *error_string = NULL;
1810 			if((error = janus_ice_trickle_parse(handle, candidate, &error_string)) != 0) {
1811 				ret = janus_process_error(request, session_id, transaction_text, error, "%s", error_string);
1812 				janus_mutex_unlock(&handle->mutex);
1813 				goto jsondone;
1814 			}
1815 		} else {
1816 			/* We got multiple candidates in an array */
1817 			if(!json_is_array(candidates)) {
1818 				ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "candidates is not an array");
1819 				janus_mutex_unlock(&handle->mutex);
1820 				goto jsondone;
1821 			}
1822 			JANUS_LOG(LOG_VERB, "Got multiple candidates (%zu)\n", json_array_size(candidates));
1823 			if(json_array_size(candidates) > 0) {
1824 				/* Handle remote candidates */
1825 				size_t i = 0;
1826 				for(i=0; i<json_array_size(candidates); i++) {
1827 					json_t *c = json_array_get(candidates, i);
1828 					/* FIXME We don't care if any trickle fails to parse */
1829 					janus_ice_trickle_parse(handle, c, NULL);
1830 				}
1831 			}
1832 		}
1833 
1834 trickledone:
1835 		janus_mutex_unlock(&handle->mutex);
1836 		/* We reply right away, not to block the web server... */
1837 		json_t *reply = janus_create_message("ack", session_id, transaction_text);
1838 		/* Send the success reply */
1839 		ret = janus_process_success(request, reply);
1840 	} else {
1841 		ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN_REQUEST, "Unknown request '%s'", message_text);
1842 	}
1843 
1844 jsondone:
1845 	/* Done processing */
1846 	if(handle != NULL)
1847 		janus_refcount_decrease(&handle->ref);
1848 	if(session != NULL)
1849 		janus_refcount_decrease(&session->ref);
1850 	return ret;
1851 }
1852 
janus_json_token_plugin_array(const char * token_value)1853 static json_t *janus_json_token_plugin_array(const char *token_value) {
1854 	json_t *plugins_list = json_array();
1855 	GList *plugins = janus_auth_list_plugins(token_value);
1856 	if(plugins != NULL) {
1857 		GList *tmp = plugins;
1858 		while(tmp) {
1859 			janus_plugin *p = (janus_plugin *)tmp->data;
1860 			if(p != NULL)
1861 				json_array_append_new(plugins_list, json_string(p->get_package()));
1862 			tmp = tmp->next;
1863 		}
1864 		g_list_free(plugins);
1865 		plugins = NULL;
1866 	}
1867 	return plugins_list;
1868 }
1869 
janus_json_list_token_plugins(const char * token_value,const gchar * transaction_text)1870 static json_t *janus_json_list_token_plugins(const char *token_value, const gchar *transaction_text) {
1871 	json_t *plugins_list = janus_json_token_plugin_array(token_value);
1872 	/* Prepare JSON reply */
1873 	json_t *reply = janus_create_message("success", 0, transaction_text);
1874 	json_t *data = json_object();
1875 	json_object_set_new(data, "plugins", plugins_list);
1876 	json_object_set_new(reply, "data", data);
1877 	return reply;
1878 }
1879 
janus_request_allow_token(janus_request * request,guint64 session_id,const gchar * transaction_text,gboolean allow,gboolean add)1880 static int janus_request_allow_token(janus_request *request, guint64 session_id, const gchar *transaction_text, gboolean allow, gboolean add) {
1881 	/* Allow/disallow a valid token valid to access a plugin */
1882 	int ret = -1;
1883 	int error_code = 0;
1884 	char error_cause[100];
1885 	json_t *root = request->message;
1886 	if(!janus_auth_is_stored_mode()) {
1887 		ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Stored-Token based authentication disabled");
1888 		goto jsondone;
1889 	}
1890 	JANUS_VALIDATE_JSON_OBJECT(root, add_token_parameters,
1891 		error_code, error_cause, FALSE,
1892 		JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1893 	/* Any plugin this token should be limited to? */
1894 	json_t *allowed = json_object_get(root, "plugins");
1895 	if(error_code == 0 && !add && (!allowed || json_array_size(allowed) == 0)) {
1896 		error_code = JANUS_ERROR_INVALID_ELEMENT_TYPE;
1897 		g_strlcpy(error_cause, "Invalid element type (plugins should be a non-empty array)", sizeof(error_cause));
1898 	}
1899 	if(error_code != 0) {
1900 		ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1901 		goto jsondone;
1902 	}
1903 	json_t *token = json_object_get(root, "token");
1904 	const char *token_value = json_string_value(token);
1905 	if(add) {
1906 		/* First of all, add the new token */
1907 		if(!janus_auth_add_token(token_value)) {
1908 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Error adding token");
1909 			goto jsondone;
1910 		}
1911 	} else {
1912 		/* Check if the token is valid, first */
1913 		if(!janus_auth_check_token(token_value)) {
1914 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_TOKEN_NOT_FOUND, "Token %s not found", token_value);
1915 			goto jsondone;
1916 		}
1917 	}
1918 	if(allowed && json_array_size(allowed) > 0) {
1919 		/* Specify which plugins this token has access to */
1920 		size_t i = 0;
1921 		gboolean ok = TRUE;
1922 		for(i=0; i<json_array_size(allowed); i++) {
1923 			json_t *p = json_array_get(allowed, i);
1924 			if(!p || !json_is_string(p)) {
1925 				/* FIXME Should we fail here? */
1926 				if(add){
1927 					JANUS_LOG(LOG_WARN, "Invalid plugin passed to the new token request, skipping...\n");
1928 					continue;
1929 				} else {
1930 					JANUS_LOG(LOG_ERR, "Invalid plugin passed to the new token request...\n");
1931 					ok = FALSE;
1932 					break;
1933 				}
1934 			}
1935 			const gchar *plugin_text = json_string_value(p);
1936 			janus_plugin *plugin_t = janus_plugin_find(plugin_text);
1937 			if(plugin_t == NULL) {
1938 				/* FIXME Should we fail here? */
1939 				if(add) {
1940 					JANUS_LOG(LOG_WARN, "No such plugin '%s' passed to the new token request, skipping...\n", plugin_text);
1941 					continue;
1942 				} else {
1943 					JANUS_LOG(LOG_ERR, "No such plugin '%s' passed to the new token request...\n", plugin_text);
1944 					ok = FALSE;
1945 				}
1946 				break;
1947 			}
1948 		}
1949 		if(!ok) {
1950 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (some of the provided plugins are invalid)");
1951 			goto jsondone;
1952 		}
1953 		/* Take care of the plugins access limitations */
1954 		i = 0;
1955 		for(i=0; i<json_array_size(allowed); i++) {
1956 			json_t *p = json_array_get(allowed, i);
1957 			const gchar *plugin_text = json_string_value(p);
1958 			janus_plugin *plugin_t = janus_plugin_find(plugin_text);
1959 			if(!(allow ? janus_auth_allow_plugin(token_value, plugin_t) : janus_auth_disallow_plugin(token_value, plugin_t))) {
1960 				/* FIXME Should we notify individual failures? */
1961 				JANUS_LOG(LOG_WARN, "Error allowing access to '%s' to the new token, bad things may happen...\n", plugin_text);
1962 			}
1963 		}
1964 	} else {
1965 		/* No plugin limitation specified, allow all plugins */
1966 		if(plugins && g_hash_table_size(plugins) > 0) {
1967 			GHashTableIter iter;
1968 			gpointer value;
1969 			g_hash_table_iter_init(&iter, plugins);
1970 			while (g_hash_table_iter_next(&iter, NULL, &value)) {
1971 				janus_plugin *plugin_t = value;
1972 				if(plugin_t == NULL)
1973 					continue;
1974 				if(!janus_auth_allow_plugin(token_value, plugin_t)) {
1975 					JANUS_LOG(LOG_WARN, "Error allowing access to '%s' to the new token, bad things may happen...\n", plugin_t->get_package());
1976 				}
1977 			}
1978 		}
1979 	}
1980 	/* Get the list of plugins this new token can now access */
1981 	json_t *reply = janus_json_list_token_plugins(token_value, transaction_text);
1982 	/* Send the success reply */
1983 	ret = janus_process_success(request, reply);
1984 jsondone:
1985 	return ret;
1986 }
1987 
1988 /* Admin/monitor WebServer requests handler */
janus_process_incoming_admin_request(janus_request * request)1989 int janus_process_incoming_admin_request(janus_request *request) {
1990 	int ret = -1;
1991 	int error_code = 0;
1992 	char error_cause[100];
1993 	if(request == NULL) {
1994 		JANUS_LOG(LOG_ERR, "Missing request or payload to process, giving up...\n");
1995 		return ret;
1996 	}
1997 	json_t *root = request->message;
1998 	/* Ok, let's start with the ids */
1999 	guint64 session_id = 0, handle_id = 0;
2000 	json_t *s = json_object_get(root, "session_id");
2001 	if(s && json_is_integer(s))
2002 		session_id = json_integer_value(s);
2003 	json_t *h = json_object_get(root, "handle_id");
2004 	if(h && json_is_integer(h))
2005 		handle_id = json_integer_value(h);
2006 
2007 	janus_session *session = NULL;
2008 	janus_ice_handle *handle = NULL;
2009 
2010 	/* Get transaction and message request */
2011 	JANUS_VALIDATE_JSON_OBJECT(root, admin_parameters,
2012 		error_code, error_cause, FALSE,
2013 		JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2014 	if(error_code != 0) {
2015 		ret = janus_process_error_string(request, session_id, NULL, error_code, error_cause);
2016 		goto jsondone;
2017 	}
2018 	json_t *transaction = json_object_get(root, "transaction");
2019 	const gchar *transaction_text = json_string_value(transaction);
2020 	json_t *message = json_object_get(root, "janus");
2021 	const gchar *message_text = json_string_value(message);
2022 
2023 	if(session_id == 0 && handle_id == 0) {
2024 		/* Can only be a 'Get all sessions' or some general setting manipulation request */
2025 		if(!strcasecmp(message_text, "info")) {
2026 			/* The generic info request */
2027 			ret = janus_process_success(request, janus_info(transaction_text));
2028 			goto jsondone;
2029 		}
2030 		if(!strcasecmp(message_text, "ping")) {
2031 			/* Prepare JSON reply */
2032 			json_t *reply = janus_create_message("pong", 0, transaction_text);
2033 			ret = janus_process_success(request, reply);
2034 			goto jsondone;
2035 		}
2036 		if(admin_api_secret != NULL) {
2037 			/* There's an admin/monitor secret, check that the client provided it */
2038 			json_t *secret = json_object_get(root, "admin_secret");
2039 			if(!secret || !json_is_string(secret) || !janus_strcmp_const_time(json_string_value(secret), admin_api_secret)) {
2040 				ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED, NULL);
2041 				goto jsondone;
2042 			}
2043 		}
2044 		if(!strcasecmp(message_text, "get_status")) {
2045 			/* Return some info on the settings (mostly debug-related, at the moment) */
2046 			json_t *reply = janus_create_message("success", 0, transaction_text);
2047 			json_t *status = json_object();
2048 			json_object_set_new(status, "token_auth", janus_auth_is_enabled() ? json_true() : json_false());
2049 			json_object_set_new(status, "session_timeout", json_integer(global_session_timeout));
2050 			json_object_set_new(status, "reclaim_session_timeout", json_integer(reclaim_session_timeout));
2051 			json_object_set_new(status, "candidates_timeout", json_integer(candidates_timeout));
2052 			json_object_set_new(status, "log_level", json_integer(janus_log_level));
2053 			json_object_set_new(status, "log_timestamps", janus_log_timestamps ? json_true() : json_false());
2054 			json_object_set_new(status, "log_colors", janus_log_colors ? json_true() : json_false());
2055 			json_object_set_new(status, "locking_debug", lock_debug ? json_true() : json_false());
2056 			json_object_set_new(status, "refcount_debug", refcount_debug ? json_true() : json_false());
2057 			json_object_set_new(status, "libnice_debug", janus_ice_is_ice_debugging_enabled() ? json_true() : json_false());
2058 			json_object_set_new(status, "min_nack_queue", json_integer(janus_get_min_nack_queue()));
2059 			json_object_set_new(status, "nack-optimizations", janus_is_nack_optimizations_enabled() ? json_true() : json_false());
2060 			json_object_set_new(status, "no_media_timer", json_integer(janus_get_no_media_timer()));
2061 			json_object_set_new(status, "slowlink_threshold", json_integer(janus_get_slowlink_threshold()));
2062 			json_object_set_new(reply, "status", status);
2063 			/* Send the success reply */
2064 			ret = janus_process_success(request, reply);
2065 			goto jsondone;
2066 		} else if(!strcasecmp(message_text, "set_session_timeout")) {
2067 			JANUS_VALIDATE_JSON_OBJECT(root, timeout_parameters,
2068 				error_code, error_cause, FALSE,
2069 				JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2070 			if(error_code != 0) {
2071 				ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2072 				goto jsondone;
2073 			}
2074 			json_t *timeout = json_object_get(root, "timeout");
2075 			int timeout_num = json_integer_value(timeout);
2076 			if(timeout_num < 0) {
2077 				ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (timeout should be a positive integer)");
2078 				goto jsondone;
2079 			}
2080 
2081 			/* Set global session timeout */
2082 			global_session_timeout = timeout_num;
2083 
2084 			/* Prepare JSON reply */
2085 			json_t *reply = json_object();
2086 			json_object_set_new(reply, "janus", json_string("success"));
2087 			json_object_set_new(reply, "transaction", json_string(transaction_text));
2088 			json_object_set_new(reply, "timeout", json_integer(timeout_num));
2089 
2090 			/* Send the success reply */
2091 			ret = janus_process_success(request, reply);
2092 			goto jsondone;
2093 		} else if(!strcasecmp(message_text, "set_log_level")) {
2094 			/* Change the debug logging level */
2095 			JANUS_VALIDATE_JSON_OBJECT(root, level_parameters,
2096 				error_code, error_cause, FALSE,
2097 				JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2098 			if(error_code != 0) {
2099 				ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2100 				goto jsondone;
2101 			}
2102 			json_t *level = json_object_get(root, "level");
2103 			int level_num = json_integer_value(level);
2104 			if(level_num < LOG_NONE || level_num > LOG_MAX) {
2105 				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);
2106 				goto jsondone;
2107 			}
2108 			janus_log_level = level_num;
2109 			/* Prepare JSON reply */
2110 			json_t *reply = janus_create_message("success", 0, transaction_text);
2111 			json_object_set_new(reply, "level", json_integer(janus_log_level));
2112 			/* Send the success reply */
2113 			ret = janus_process_success(request, reply);
2114 			goto jsondone;
2115 		} else if(!strcasecmp(message_text, "set_locking_debug")) {
2116 			/* Enable/disable the locking debug (would show a message on the console for every lock attempt) */
2117 			JANUS_VALIDATE_JSON_OBJECT(root, debug_parameters,
2118 				error_code, error_cause, FALSE,
2119 				JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2120 			if(error_code != 0) {
2121 				ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2122 				goto jsondone;
2123 			}
2124 			json_t *debug = json_object_get(root, "debug");
2125 			lock_debug = json_is_true(debug);
2126 			/* Prepare JSON reply */
2127 			json_t *reply = janus_create_message("success", 0, transaction_text);
2128 			json_object_set_new(reply, "locking_debug", lock_debug ? json_true() : json_false());
2129 			/* Send the success reply */
2130 			ret = janus_process_success(request, reply);
2131 			goto jsondone;
2132 		} else if(!strcasecmp(message_text, "set_refcount_debug")) {
2133 			/* Enable/disable the reference counter debug (would show a message on the console for every increase/decrease) */
2134 			JANUS_VALIDATE_JSON_OBJECT(root, debug_parameters,
2135 				error_code, error_cause, FALSE,
2136 				JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2137 			if(error_code != 0) {
2138 				ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2139 				goto jsondone;
2140 			}
2141 			json_t *debug = json_object_get(root, "debug");
2142 			if(json_is_true(debug)) {
2143 				refcount_debug = TRUE;
2144 			} else {
2145 				refcount_debug = FALSE;
2146 			}
2147 			/* Prepare JSON reply */
2148 			json_t *reply = janus_create_message("success", 0, transaction_text);
2149 			json_object_set_new(reply, "refcount_debug", refcount_debug ? json_true() : json_false());
2150 			/* Send the success reply */
2151 			ret = janus_process_success(request, reply);
2152 			goto jsondone;
2153 		} else if(!strcasecmp(message_text, "set_log_timestamps")) {
2154 			/* Enable/disable the log timestamps */
2155 			JANUS_VALIDATE_JSON_OBJECT(root, timestamps_parameters,
2156 				error_code, error_cause, FALSE,
2157 				JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2158 			if(error_code != 0) {
2159 				ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2160 				goto jsondone;
2161 			}
2162 			json_t *timestamps = json_object_get(root, "timestamps");
2163 			janus_log_timestamps = json_is_true(timestamps);
2164 			/* Prepare JSON reply */
2165 			json_t *reply = janus_create_message("success", 0, transaction_text);
2166 			json_object_set_new(reply, "log_timestamps", janus_log_timestamps ? json_true() : json_false());
2167 			/* Send the success reply */
2168 			ret = janus_process_success(request, reply);
2169 			goto jsondone;
2170 		} else if(!strcasecmp(message_text, "set_log_colors")) {
2171 			/* Enable/disable the log colors */
2172 			JANUS_VALIDATE_JSON_OBJECT(root, colors_parameters,
2173 				error_code, error_cause, FALSE,
2174 				JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2175 			if(error_code != 0) {
2176 				ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2177 				goto jsondone;
2178 			}
2179 			json_t *colors = json_object_get(root, "colors");
2180 			janus_log_colors = json_is_true(colors);
2181 			/* Prepare JSON reply */
2182 			json_t *reply = janus_create_message("success", 0, transaction_text);
2183 			json_object_set_new(reply, "log_colors", janus_log_colors ? json_true() : json_false());
2184 			/* Send the success reply */
2185 			ret = janus_process_success(request, reply);
2186 			goto jsondone;
2187 		} else if(!strcasecmp(message_text, "set_libnice_debug")) {
2188 			/* Enable/disable the libnice debugging (http://nice.freedesktop.org/libnice/libnice-Debug-messages.html) */
2189 			JANUS_VALIDATE_JSON_OBJECT(root, debug_parameters,
2190 				error_code, error_cause, FALSE,
2191 				JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2192 			if(error_code != 0) {
2193 				ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2194 				goto jsondone;
2195 			}
2196 			json_t *debug = json_object_get(root, "debug");
2197 			if(json_is_true(debug)) {
2198 				janus_ice_debugging_enable();
2199 			} else {
2200 				janus_ice_debugging_disable();
2201 			}
2202 			/* Prepare JSON reply */
2203 			json_t *reply = janus_create_message("success", 0, transaction_text);
2204 			json_object_set_new(reply, "libnice_debug", janus_ice_is_ice_debugging_enabled() ? json_true() : json_false());
2205 			/* Send the success reply */
2206 			ret = janus_process_success(request, reply);
2207 			goto jsondone;
2208 		} else if(!strcasecmp(message_text, "set_min_nack_queue")) {
2209 			/* Change the current value for the min NACK queue */
2210 			JANUS_VALIDATE_JSON_OBJECT(root, mnq_parameters,
2211 				error_code, error_cause, FALSE,
2212 				JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2213 			if(error_code != 0) {
2214 				ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2215 				goto jsondone;
2216 			}
2217 			json_t *mnq = json_object_get(root, "min_nack_queue");
2218 			int mnq_num = json_integer_value(mnq);
2219 			janus_set_min_nack_queue(mnq_num);
2220 			/* Prepare JSON reply */
2221 			json_t *reply = janus_create_message("success", 0, transaction_text);
2222 			json_object_set_new(reply, "min_nack_queue", json_integer(janus_get_min_nack_queue()));
2223 			/* Send the success reply */
2224 			ret = janus_process_success(request, reply);
2225 			goto jsondone;
2226 		} else if(!strcasecmp(message_text, "set_nack_optimizations")) {
2227 			/* Enable/disable NACK optimizations */
2228 			JANUS_VALIDATE_JSON_OBJECT(root, nopt_parameters,
2229 				error_code, error_cause, FALSE,
2230 				JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2231 			if(error_code != 0) {
2232 				ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2233 				goto jsondone;
2234 			}
2235 			gboolean optimize = json_is_true(json_object_get(root, "nack_optimizations"));
2236 			janus_set_nack_optimizations_enabled(optimize);
2237 			/* Prepare JSON reply */
2238 			json_t *reply = janus_create_message("success", 0, transaction_text);
2239 			json_object_set_new(reply, "nack-optimizations", janus_is_nack_optimizations_enabled() ? json_true() : json_false());
2240 			/* Send the success reply */
2241 			ret = janus_process_success(request, reply);
2242 			goto jsondone;
2243 		} else if(!strcasecmp(message_text, "set_no_media_timer")) {
2244 			/* Change the current value for the no-media timer */
2245 			JANUS_VALIDATE_JSON_OBJECT(root, nmt_parameters,
2246 				error_code, error_cause, FALSE,
2247 				JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2248 			if(error_code != 0) {
2249 				ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2250 				goto jsondone;
2251 			}
2252 			json_t *nmt = json_object_get(root, "no_media_timer");
2253 			int nmt_num = json_integer_value(nmt);
2254 			janus_set_no_media_timer(nmt_num);
2255 			/* Prepare JSON reply */
2256 			json_t *reply = json_object();
2257 			json_object_set_new(reply, "janus", json_string("success"));
2258 			json_object_set_new(reply, "transaction", json_string(transaction_text));
2259 			json_object_set_new(reply, "no_media_timer", json_integer(janus_get_no_media_timer()));
2260 			/* Send the success reply */
2261 			ret = janus_process_success(request, reply);
2262 			goto jsondone;
2263 		} else if(!strcasecmp(message_text, "set_slowlink_threshold")) {
2264 			/* Change the current value for the slowlink-threshold value */
2265 			JANUS_VALIDATE_JSON_OBJECT(root, st_parameters,
2266 				error_code, error_cause, FALSE,
2267 				JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2268 			if(error_code != 0) {
2269 				ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2270 				goto jsondone;
2271 			}
2272 			json_t *nmt = json_object_get(root, "slowlink_threshold");
2273 			int nmt_num = json_integer_value(nmt);
2274 			janus_set_slowlink_threshold(nmt_num);
2275 			/* Prepare JSON reply */
2276 			json_t *reply = json_object();
2277 			json_object_set_new(reply, "janus", json_string("success"));
2278 			json_object_set_new(reply, "transaction", json_string(transaction_text));
2279 			json_object_set_new(reply, "slowlink_threshold", json_integer(janus_get_slowlink_threshold()));
2280 			/* Send the success reply */
2281 			ret = janus_process_success(request, reply);
2282 			goto jsondone;
2283 		} else if(!strcasecmp(message_text, "accept_new_sessions")) {
2284 			/* Configure whether we should accept new incoming sessions or not:
2285 			 * this can be particularly useful whenever, e.g., we want to stop
2286 			 * accepting new sessions because we're draining this server */
2287 			JANUS_VALIDATE_JSON_OBJECT(root, ans_parameters,
2288 				error_code, error_cause, FALSE,
2289 				JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2290 			if(error_code != 0) {
2291 				ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2292 				goto jsondone;
2293 			}
2294 			json_t *accept = json_object_get(root, "accept");
2295 			accept_new_sessions = json_is_true(accept);
2296 			/* Prepare JSON reply */
2297 			json_t *reply = janus_create_message("success", 0, transaction_text);
2298 			json_object_set_new(reply, "accept", accept_new_sessions ? json_true() : json_false());
2299 			/* Send the success reply */
2300 			ret = janus_process_success(request, reply);
2301 			goto jsondone;
2302 		} else if(!strcasecmp(message_text, "message_plugin")) {
2303 			/* Contact a plugin and expect a response */
2304 			JANUS_VALIDATE_JSON_OBJECT(root, messageplugin_parameters,
2305 				error_code, error_cause, FALSE,
2306 				JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2307 			if(error_code != 0) {
2308 				ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2309 				goto jsondone;
2310 			}
2311 			json_t *plugin = json_object_get(root, "plugin");
2312 			const char *plugin_value = json_string_value(plugin);
2313 			janus_plugin *p = janus_plugin_find(plugin_value);
2314 			if(p == NULL) {
2315 				/* No such handler... */
2316 				g_snprintf(error_cause, sizeof(error_cause), "%s", "Invalid plugin");
2317 				ret = janus_process_error_string(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_NOT_FOUND, error_cause);
2318 				goto jsondone;
2319 			}
2320 			if(p->handle_admin_message == NULL) {
2321 				/* Handler doesn't implement the hook... */
2322 				g_snprintf(error_cause, sizeof(error_cause), "%s", "Plugin doesn't support Admin API messages");
2323 				ret = janus_process_error_string(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, error_cause);
2324 				goto jsondone;
2325 			}
2326 			json_t *query = json_object_get(root, "request");
2327 			json_t *response = p->handle_admin_message(query);
2328 			/* Prepare JSON reply */
2329 			json_t *reply = json_object();
2330 			json_object_set_new(reply, "janus", json_string("success"));
2331 			json_object_set_new(reply, "transaction", json_string(transaction_text));
2332 			json_object_set_new(reply, "response", response ? response : json_object());
2333 			/* Send the success reply */
2334 			ret = janus_process_success(request, reply);
2335 			goto jsondone;
2336 		} else if(!strcasecmp(message_text, "query_transport")) {
2337 			/* Contact a transport and expect a response */
2338 			JANUS_VALIDATE_JSON_OBJECT(root, querytransport_parameters,
2339 				error_code, error_cause, FALSE,
2340 				JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2341 			if(error_code != 0) {
2342 				ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2343 				goto jsondone;
2344 			}
2345 			json_t *transport = json_object_get(root, "transport");
2346 			const char *transport_value = json_string_value(transport);
2347 			janus_transport *t = g_hash_table_lookup(transports, transport_value);
2348 			if(t == NULL) {
2349 				/* No such transport... */
2350 				g_snprintf(error_cause, sizeof(error_cause), "%s", "Invalid transport");
2351 				ret = janus_process_error_string(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_NOT_FOUND, error_cause);
2352 				goto jsondone;
2353 			}
2354 			if(t->query_transport == NULL) {
2355 				/* Transport doesn't implement the hook... */
2356 				g_snprintf(error_cause, sizeof(error_cause), "%s", "Transport plugin doesn't support queries");
2357 				ret = janus_process_error_string(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, error_cause);
2358 				goto jsondone;
2359 			}
2360 			json_t *query = json_object_get(root, "request");
2361 			json_t *response = t->query_transport(query);
2362 			/* Prepare JSON reply */
2363 			json_t *reply = json_object();
2364 			json_object_set_new(reply, "janus", json_string("success"));
2365 			json_object_set_new(reply, "transaction", json_string(transaction_text));
2366 			json_object_set_new(reply, "response", response ? response : json_object());
2367 			/* Send the success reply */
2368 			ret = janus_process_success(request, reply);
2369 			goto jsondone;
2370 		} else if(!strcasecmp(message_text, "query_eventhandler")) {
2371 			/* Contact an event handler and expect a response */
2372 			JANUS_VALIDATE_JSON_OBJECT(root, queryhandler_parameters,
2373 				error_code, error_cause, FALSE,
2374 				JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2375 			if(error_code != 0) {
2376 				ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2377 				goto jsondone;
2378 			}
2379 			json_t *handler = json_object_get(root, "handler");
2380 			const char *handler_value = json_string_value(handler);
2381 			janus_eventhandler *evh = g_hash_table_lookup(eventhandlers, handler_value);
2382 			if(evh == NULL) {
2383 				/* No such handler... */
2384 				g_snprintf(error_cause, sizeof(error_cause), "%s", "Invalid event handler");
2385 				ret = janus_process_error_string(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_NOT_FOUND, error_cause);
2386 				goto jsondone;
2387 			}
2388 			if(evh->handle_request == NULL) {
2389 				/* Handler doesn't implement the hook... */
2390 				g_snprintf(error_cause, sizeof(error_cause), "%s", "Event handler doesn't support queries");
2391 				ret = janus_process_error_string(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, error_cause);
2392 				goto jsondone;
2393 			}
2394 			json_t *query = json_object_get(root, "request");
2395 			json_t *response = evh->handle_request(query);
2396 			/* Prepare JSON reply */
2397 			json_t *reply = json_object();
2398 			json_object_set_new(reply, "janus", json_string("success"));
2399 			json_object_set_new(reply, "transaction", json_string(transaction_text));
2400 			json_object_set_new(reply, "response", response ? response : json_object());
2401 			/* Send the success reply */
2402 			ret = janus_process_success(request, reply);
2403 			goto jsondone;
2404 		} else if(!strcasecmp(message_text, "query_logger")) {
2405 			/* Contact a logger and expect a response */
2406 			JANUS_VALIDATE_JSON_OBJECT(root, querylogger_parameters,
2407 				error_code, error_cause, FALSE,
2408 				JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2409 			if(error_code != 0) {
2410 				ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2411 				goto jsondone;
2412 			}
2413 			json_t *logger = json_object_get(root, "logger");
2414 			const char *logger_value = json_string_value(logger);
2415 			janus_logger *l = g_hash_table_lookup(loggers, logger_value);
2416 			if(l == NULL) {
2417 				/* No such handler... */
2418 				g_snprintf(error_cause, sizeof(error_cause), "%s", "Invalid logger");
2419 				ret = janus_process_error_string(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_NOT_FOUND, error_cause);
2420 				goto jsondone;
2421 			}
2422 			if(l->handle_request == NULL) {
2423 				/* Handler doesn't implement the hook... */
2424 				g_snprintf(error_cause, sizeof(error_cause), "%s", "Logger doesn't support queries");
2425 				ret = janus_process_error_string(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, error_cause);
2426 				goto jsondone;
2427 			}
2428 			json_t *query = json_object_get(root, "request");
2429 			json_t *response = l->handle_request(query);
2430 			/* Prepare JSON reply */
2431 			json_t *reply = json_object();
2432 			json_object_set_new(reply, "janus", json_string("success"));
2433 			json_object_set_new(reply, "transaction", json_string(transaction_text));
2434 			json_object_set_new(reply, "response", response ? response : json_object());
2435 			/* Send the success reply */
2436 			ret = janus_process_success(request, reply);
2437 			goto jsondone;
2438 		} else if(!strcasecmp(message_text, "custom_event")) {
2439 			/* Enqueue a custom "external" event to notify via event handlers */
2440 			JANUS_VALIDATE_JSON_OBJECT(root, customevent_parameters,
2441 				error_code, error_cause, FALSE,
2442 				JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2443 			if(error_code != 0) {
2444 				ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2445 				goto jsondone;
2446 			}
2447 			json_t *schema = json_object_get(root, "schema");
2448 			const char *schema_value = json_string_value(schema);
2449 			json_t *data = json_object_get(root, "data");
2450 			if(janus_events_is_enabled()) {
2451 				json_incref(data);
2452 				janus_events_notify_handlers(JANUS_EVENT_TYPE_EXTERNAL, JANUS_EVENT_SUBTYPE_NONE,
2453 					0, schema_value, data);
2454 			}
2455 			/* Prepare JSON reply */
2456 			json_t *reply = json_object();
2457 			json_object_set_new(reply, "janus", json_string("success"));
2458 			json_object_set_new(reply, "transaction", json_string(transaction_text));
2459 			/* Send the success reply */
2460 			ret = janus_process_success(request, reply);
2461 			goto jsondone;
2462 		} else if(!strcasecmp(message_text, "custom_logline")) {
2463 			/* Print something custom on the logs, using the specified debug level */
2464 			JANUS_VALIDATE_JSON_OBJECT(root, customlogline_parameters,
2465 				error_code, error_cause, FALSE,
2466 				JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2467 			if(error_code != 0) {
2468 				ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2469 				goto jsondone;
2470 			}
2471 			json_t *line = json_object_get(root, "line");
2472 			const char *log_line = json_string_value(line);
2473 			json_t *level = json_object_get(root, "level");
2474 			int log_level = LOG_INFO;
2475 			if(level) {
2476 				log_level = json_integer_value(level);
2477 				if(log_level < LOG_NONE || log_level > LOG_MAX)
2478 					log_level = LOG_INFO;
2479 			}
2480 			/* Print the log line on the log */
2481 			JANUS_LOG(log_level, "%s\n", log_line);
2482 			/* Prepare JSON reply */
2483 			json_t *reply = json_object();
2484 			json_object_set_new(reply, "janus", json_string("success"));
2485 			json_object_set_new(reply, "transaction", json_string(transaction_text));
2486 			/* Send the success reply */
2487 			ret = janus_process_success(request, reply);
2488 			goto jsondone;
2489 		} else if(!strcasecmp(message_text, "list_sessions")) {
2490 			/* List sessions */
2491 			session_id = 0;
2492 			json_t *list = json_array();
2493 			if(sessions != NULL && g_hash_table_size(sessions) > 0) {
2494 				janus_mutex_lock(&sessions_mutex);
2495 				GHashTableIter iter;
2496 				gpointer value;
2497 				g_hash_table_iter_init(&iter, sessions);
2498 				while (g_hash_table_iter_next(&iter, NULL, &value)) {
2499 					janus_session *session = value;
2500 					if(session == NULL) {
2501 						continue;
2502 					}
2503 					json_array_append_new(list, json_integer(session->session_id));
2504 				}
2505 				janus_mutex_unlock(&sessions_mutex);
2506 			}
2507 			/* Prepare JSON reply */
2508 			json_t *reply = janus_create_message("success", 0, transaction_text);
2509 			json_object_set_new(reply, "sessions", list);
2510 			/* Send the success reply */
2511 			ret = janus_process_success(request, reply);
2512 			goto jsondone;
2513 		} else if(!strcasecmp(message_text, "add_token")) {
2514 			/* Add a token valid for authentication */
2515 			ret = janus_request_allow_token(request, session_id, transaction_text, TRUE, TRUE);
2516 			goto jsondone;
2517 		} else if(!strcasecmp(message_text, "list_tokens")) {
2518 			/* List all the valid tokens */
2519 			if(!janus_auth_is_stored_mode()) {
2520 				ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Stored-Token based authentication disabled");
2521 				goto jsondone;
2522 			}
2523 			json_t *tokens_list = json_array();
2524 			GList *list = janus_auth_list_tokens();
2525 			if(list != NULL) {
2526 				GList *tmp = list;
2527 				while(tmp) {
2528 					char *token = (char *)tmp->data;
2529 					if(token != NULL) {
2530 						json_t *plugins_list = janus_json_token_plugin_array(token);
2531 						if(json_array_size(plugins_list) > 0) {
2532 							json_t *t = json_object();
2533 							json_object_set_new(t, "token", json_string(token));
2534 							json_object_set_new(t, "allowed_plugins", plugins_list);
2535 							json_array_append_new(tokens_list, t);
2536 						}
2537 						else
2538 							json_decref(plugins_list);
2539 						tmp->data = NULL;
2540 						g_free(token);
2541 					}
2542 					tmp = tmp->next;
2543 				}
2544 				g_list_free(list);
2545 			}
2546 			/* Prepare JSON reply */
2547 			json_t *reply = janus_create_message("success", 0, transaction_text);
2548 			json_t *data = json_object();
2549 			json_object_set_new(data, "tokens", tokens_list);
2550 			json_object_set_new(reply, "data", data);
2551 			/* Send the success reply */
2552 			ret = janus_process_success(request, reply);
2553 			goto jsondone;
2554 		} else if(!strcasecmp(message_text, "allow_token")) {
2555 			/* Allow a valid token valid to access a plugin */
2556 			ret = janus_request_allow_token(request, session_id, transaction_text, TRUE, FALSE);
2557 			goto jsondone;
2558 		} else if(!strcasecmp(message_text, "disallow_token")) {
2559 			/* Disallow a valid token valid from accessing a plugin */
2560 			ret = janus_request_allow_token(request, session_id, transaction_text, FALSE, FALSE);
2561 			goto jsondone;
2562 		} else if(!strcasecmp(message_text, "remove_token")) {
2563 			/* Invalidate a token for authentication purposes */
2564 			if(!janus_auth_is_stored_mode()) {
2565 				ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Stored-Token based authentication disabled");
2566 				goto jsondone;
2567 			}
2568 			JANUS_VALIDATE_JSON_OBJECT(root, token_parameters,
2569 				error_code, error_cause, FALSE,
2570 				JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2571 			if(error_code != 0) {
2572 				ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2573 				goto jsondone;
2574 			}
2575 			json_t *token = json_object_get(root, "token");
2576 			const char *token_value = json_string_value(token);
2577 			if(!janus_auth_remove_token(token_value)) {
2578 				ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Error removing token");
2579 				goto jsondone;
2580 			}
2581 			/* Prepare JSON reply */
2582 			json_t *reply = janus_create_message("success", 0, transaction_text);
2583 			/* Send the success reply */
2584 			ret = janus_process_success(request, reply);
2585 			goto jsondone;
2586 		} else if(!strcasecmp(message_text, "resolve_address")) {
2587 			/* Helper method to evaluate whether this instance can resolve an address, and how soon */
2588 			JANUS_VALIDATE_JSON_OBJECT(root, resaddr_parameters,
2589 				error_code, error_cause, FALSE,
2590 				JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2591 			if(error_code != 0) {
2592 				ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2593 				goto jsondone;
2594 			}
2595 			const char *address = json_string_value(json_object_get(root, "address"));
2596 			/* Resolve the address */
2597 			gint64 start = janus_get_monotonic_time();
2598 			struct addrinfo *res = NULL;
2599 			janus_network_address addr;
2600 			janus_network_address_string_buffer addr_buf;
2601 			if(getaddrinfo(address, NULL, NULL, &res) != 0 ||
2602 					janus_network_address_from_sockaddr(res->ai_addr, &addr) != 0 ||
2603 					janus_network_address_to_string_buffer(&addr, &addr_buf) != 0) {
2604 				JANUS_LOG(LOG_ERR, "Could not resolve %s...\n", address);
2605 				if(res)
2606 					freeaddrinfo(res);
2607 				ret = janus_process_error_string(request, session_id, transaction_text,
2608 					JANUS_ERROR_UNKNOWN, (char *)"Could not resolve address");
2609 				goto jsondone;
2610 			}
2611 			gint64 end = janus_get_monotonic_time();
2612 			freeaddrinfo(res);
2613 			/* Prepare JSON reply */
2614 			json_t *reply = janus_create_message("success", 0, transaction_text);
2615 			json_object_set_new(reply, "ip", json_string(janus_network_address_string_from_buffer(&addr_buf)));
2616 			json_object_set_new(reply, "elapsed", json_integer(end-start));
2617 			/* Send the success reply */
2618 			ret = janus_process_success(request, reply);
2619 			goto jsondone;
2620 		} else if(!strcasecmp(message_text, "test_stun")) {
2621 			/* Helper method to evaluate whether this instance can use STUN with a specific server */
2622 			JANUS_VALIDATE_JSON_OBJECT(root, teststun_parameters,
2623 				error_code, error_cause, FALSE,
2624 				JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2625 			if(error_code != 0) {
2626 				ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2627 				goto jsondone;
2628 			}
2629 			const char *address = json_string_value(json_object_get(root, "address"));
2630 			uint16_t port = json_integer_value(json_object_get(root, "port"));
2631 			uint16_t local_port = json_integer_value(json_object_get(root, "localport"));
2632 			/* Resolve the address */
2633 			gint64 start = janus_get_monotonic_time();
2634 			struct addrinfo *res = NULL;
2635 			janus_network_address addr;
2636 			janus_network_address_string_buffer addr_buf;
2637 			if(getaddrinfo(address, NULL, NULL, &res) != 0 ||
2638 					janus_network_address_from_sockaddr(res->ai_addr, &addr) != 0 ||
2639 					janus_network_address_to_string_buffer(&addr, &addr_buf) != 0) {
2640 				JANUS_LOG(LOG_ERR, "Could not resolve %s...\n", address);
2641 				if(res)
2642 					freeaddrinfo(res);
2643 				ret = janus_process_error_string(request, session_id, transaction_text,
2644 					JANUS_ERROR_UNKNOWN, (char *)"Could not resolve address");
2645 				goto jsondone;
2646 			}
2647 			freeaddrinfo(res);
2648 			/* Test the STUN server */
2649 			janus_network_address public_addr = { 0 };
2650 			uint16_t public_port = 0;
2651 			if(janus_ice_test_stun_server(&addr, port, local_port, &public_addr, &public_port) < 0) {
2652 				ret = janus_process_error_string(request, session_id, transaction_text,
2653 					JANUS_ERROR_UNKNOWN, (char *)"STUN request failed");
2654 				goto jsondone;
2655 			}
2656 			if(janus_network_address_to_string_buffer(&public_addr, &addr_buf) != 0) {
2657 				ret = janus_process_error_string(request, session_id, transaction_text,
2658 					JANUS_ERROR_UNKNOWN, (char *)"Could not resolve public address");
2659 				goto jsondone;
2660 			}
2661 			const char *public_ip_addr = janus_network_address_string_from_buffer(&addr_buf);
2662 			gint64 end = janus_get_monotonic_time();
2663 			/* Prepare JSON reply */
2664 			json_t *reply = janus_create_message("success", 0, transaction_text);
2665 			json_object_set_new(reply, "public_ip", json_string(public_ip_addr));
2666 			json_object_set_new(reply, "public_port", json_integer(public_port));
2667 			json_object_set_new(reply, "elapsed", json_integer(end-start));
2668 			/* Send the success reply */
2669 			ret = janus_process_success(request, reply);
2670 			goto jsondone;
2671 		} else {
2672 			/* No message we know of */
2673 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
2674 			goto jsondone;
2675 		}
2676 	}
2677 	if(session_id < 1) {
2678 		JANUS_LOG(LOG_ERR, "Invalid session\n");
2679 		ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, NULL);
2680 		goto jsondone;
2681 	}
2682 	if(h && handle_id < 1) {
2683 		JANUS_LOG(LOG_ERR, "Invalid handle\n");
2684 		ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, NULL);
2685 		goto jsondone;
2686 	}
2687 
2688 	/* Go on with the processing */
2689 	if(admin_api_secret != NULL) {
2690 		/* There's an API secret, check that the client provided it */
2691 		json_t *secret = json_object_get(root, "admin_secret");
2692 		if(!secret || !json_is_string(secret) || !janus_strcmp_const_time(json_string_value(secret), admin_api_secret)) {
2693 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED, NULL);
2694 			goto jsondone;
2695 		}
2696 	}
2697 
2698 	/* If we got here, make sure we have a session (and/or a handle) */
2699 	session = janus_session_find(session_id);
2700 	if(!session) {
2701 		JANUS_LOG(LOG_ERR, "Couldn't find any session %"SCNu64"...\n", session_id);
2702 		ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, "No such session %"SCNu64"", session_id);
2703 		goto jsondone;
2704 	}
2705 	handle = NULL;
2706 	if(handle_id > 0) {
2707 		handle = janus_session_handles_find(session, handle_id);
2708 		if(!handle) {
2709 			JANUS_LOG(LOG_ERR, "Couldn't find any handle %"SCNu64" in session %"SCNu64"...\n", handle_id, session_id);
2710 			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);
2711 			goto jsondone;
2712 		}
2713 	}
2714 
2715 	/* What is this? */
2716 	if(handle == NULL) {
2717 		/* Session-related */
2718 		if(!strcasecmp(message_text, "destroy_session")) {
2719 			janus_mutex_lock(&sessions_mutex);
2720 			g_hash_table_remove(sessions, &session->session_id);
2721 			janus_mutex_unlock(&sessions_mutex);
2722 			/* Notify the source that the session has been destroyed */
2723 			janus_request *source = janus_session_get_request(session);
2724 			if(source && source->transport)
2725 				source->transport->session_over(source->instance, session->session_id, FALSE, FALSE);
2726 			janus_request_unref(source);
2727 			/* Schedule the session for deletion */
2728 			janus_session_destroy(session);
2729 
2730 			/* Prepare JSON reply */
2731 			json_t *reply = janus_create_message("success", session_id, transaction_text);
2732 			/* Send the success reply */
2733 			ret = janus_process_success(request, reply);
2734 			/* Notify event handlers as well */
2735 			if(janus_events_is_enabled())
2736 				janus_events_notify_handlers(JANUS_EVENT_TYPE_SESSION, JANUS_EVENT_SUBTYPE_NONE,
2737 					session_id, "destroyed", NULL);
2738 			goto jsondone;
2739 		} else if (!strcasecmp(message_text, "set_session_timeout")) {
2740 			/* Specific session timeout setting. */
2741 			JANUS_VALIDATE_JSON_OBJECT(root, session_timeout_parameters,
2742 				error_code, error_cause, FALSE,
2743 				JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2744 			if(error_code != 0) {
2745 				ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2746 				goto jsondone;
2747 			}
2748 
2749 			/* Positive timeout is seconds, 0 is unlimited, -1 is global session timeout */
2750 			json_t *timeout = json_object_get(root, "timeout");
2751 			int timeout_num = json_integer_value(timeout);
2752 			if(timeout_num < -1) {
2753 				ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (timeout should be a non-negative integer or -1)");
2754 				goto jsondone;
2755 			}
2756 
2757 			/* Set specific session timeout */
2758 			janus_mutex_lock(&session->mutex);
2759 			session->timeout = timeout_num;
2760 			janus_mutex_unlock(&session->mutex);
2761 
2762 			/* Prepare JSON reply */
2763 			json_t *reply = json_object();
2764 			json_object_set_new(reply, "janus", json_string("success"));
2765 			json_object_set_new(reply, "transaction", json_string(transaction_text));
2766 			json_object_set_new(reply, "timeout", json_integer(timeout_num));
2767 			json_object_set_new(reply, "session_id", json_integer(session_id));
2768 
2769 			/* Send the success reply */
2770 			ret = janus_process_success(request, reply);
2771 			goto jsondone;
2772 		} else if(strcasecmp(message_text, "list_handles")) {
2773 			/* If this is not a request to destroy a session, it must be a request to list the handles */
2774 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
2775 			goto jsondone;
2776 		}
2777 
2778 		/* List handles */
2779 		json_t *list = janus_session_handles_list_json(session);
2780 		/* Prepare JSON reply */
2781 		json_t *reply = janus_create_message("success", session_id, transaction_text);
2782 		json_object_set_new(reply, "handles", list);
2783 		/* Send the success reply */
2784 		ret = janus_process_success(request, reply);
2785 		goto jsondone;
2786 	} else {
2787 		/* Handle-related */
2788 		if(!strcasecmp(message_text, "detach_handle")) {
2789 			if(handle->app == NULL || handle->app_handle == NULL) {
2790 				ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_DETACH, "No plugin to detach from");
2791 				goto jsondone;
2792 			}
2793 			int error = janus_session_handles_remove(session, handle);
2794 			if(error != 0) {
2795 				/* TODO Make error struct to pass verbose information */
2796 				ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_DETACH, "Couldn't detach from plugin: error '%d'", error);
2797 				/* TODO Delete handle instance */
2798 				goto jsondone;
2799 			}
2800 			/* Prepare JSON reply */
2801 			json_t *reply = janus_create_message("success", session_id, transaction_text);
2802 			/* Send the success reply */
2803 			ret = janus_process_success(request, reply);
2804 			goto jsondone;
2805 		} else if(!strcasecmp(message_text, "hangup_webrtc")) {
2806 			if(handle->app == NULL || handle->app_handle == NULL) {
2807 				ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_DETACH, "No plugin attached");
2808 				goto jsondone;
2809 			}
2810 			janus_ice_webrtc_hangup(handle, "Admin API");
2811 			/* Prepare JSON reply */
2812 			json_t *reply = janus_create_message("success", session_id, transaction_text);
2813 			/* Send the success reply */
2814 			ret = janus_process_success(request, reply);
2815 			goto jsondone;
2816 		} else if(!strcasecmp(message_text, "start_pcap") || !strcasecmp(message_text, "start_text2pcap")) {
2817 			/* Start dumping RTP and RTCP packets to a pcap or text2pcap file */
2818 			JANUS_VALIDATE_JSON_OBJECT(root, text2pcap_parameters,
2819 				error_code, error_cause, FALSE,
2820 				JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2821 			if(error_code != 0) {
2822 				ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2823 				goto jsondone;
2824 			}
2825 			gboolean text = !strcasecmp(message_text, "start_text2pcap");
2826 			const char *folder = json_string_value(json_object_get(root, "folder"));
2827 			const char *filename = json_string_value(json_object_get(root, "filename"));
2828 			int truncate = json_integer_value(json_object_get(root, "truncate"));
2829 			if(handle->text2pcap != NULL) {
2830 				ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN,
2831 					text ? "text2pcap already started" : "pcap already started");
2832 				goto jsondone;
2833 			}
2834 			handle->text2pcap = janus_text2pcap_create(folder, filename, truncate, text);
2835 			if(handle->text2pcap == NULL) {
2836 				ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN,
2837 					text ? "Error starting text2pcap dump" : "Error starting pcap dump");
2838 				goto jsondone;
2839 			}
2840 			g_atomic_int_set(&handle->dump_packets, 1);
2841 			/* Prepare JSON reply */
2842 			json_t *reply = json_object();
2843 			json_object_set_new(reply, "janus", json_string("success"));
2844 			json_object_set_new(reply, "transaction", json_string(transaction_text));
2845 			/* Send the success reply */
2846 			ret = janus_process_success(request, reply);
2847 			goto jsondone;
2848 		} else if(!strcasecmp(message_text, "stop_pcap") || !strcasecmp(message_text, "stop_text2pcap")) {
2849 			/* Stop dumping RTP and RTCP packets to a pcap or text2pcap file */
2850 			if(handle->text2pcap == NULL) {
2851 				ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN,
2852 					"Capture not started");
2853 				goto jsondone;
2854 			}
2855 			if(g_atomic_int_compare_and_exchange(&handle->dump_packets, 1, 0)) {
2856 				janus_text2pcap_close(handle->text2pcap);
2857 				g_clear_pointer(&handle->text2pcap, janus_text2pcap_free);
2858 			}
2859 			/* Prepare JSON reply */
2860 			json_t *reply = json_object();
2861 			json_object_set_new(reply, "janus", json_string("success"));
2862 			json_object_set_new(reply, "transaction", json_string(transaction_text));
2863 			/* Send the success reply */
2864 			ret = janus_process_success(request, reply);
2865 			goto jsondone;
2866 		}
2867 		/* If this is not a request to start/stop debugging to text2pcap, it must be a handle_info */
2868 		if(strcasecmp(message_text, "handle_info")) {
2869 			ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
2870 			goto jsondone;
2871 		}
2872 		JANUS_VALIDATE_JSON_OBJECT(root, handleinfo_parameters,
2873 			error_code, error_cause, FALSE,
2874 			JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2875 		if(error_code != 0) {
2876 			ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2877 			goto jsondone;
2878 		}
2879 		/* Check if we should limit the response to the plugin-specific info */
2880 		gboolean plugin_only = json_is_true(json_object_get(root, "plugin_only"));
2881 		/* Prepare info */
2882 		json_t *info = json_object();
2883 		json_object_set_new(info, "session_id", json_integer(session_id));
2884 		json_object_set_new(info, "session_last_activity", json_integer(session->last_activity));
2885 		json_object_set_new(info, "session_timeout", json_integer(session->timeout == -1 ? global_session_timeout : (guint)session->timeout));
2886 		janus_mutex_lock(&session->mutex);
2887 		if(session->source && session->source->transport)
2888 			json_object_set_new(info, "session_transport", json_string(session->source->transport->get_package()));
2889 		janus_mutex_unlock(&session->mutex);
2890 		janus_mutex_lock(&handle->mutex);
2891 		json_object_set_new(info, "handle_id", json_integer(handle_id));
2892 		if(handle->opaque_id)
2893 			json_object_set_new(info, "opaque_id", json_string(handle->opaque_id));
2894 		if(handle->token)
2895 			json_object_set_new(info, "token", json_string(handle->token));
2896 		json_object_set_new(info, "loop-running", (handle->mainloop != NULL &&
2897 			g_main_loop_is_running(handle->mainloop)) ? json_true() : json_false());
2898 		json_object_set_new(info, "created", json_integer(handle->created));
2899 		json_object_set_new(info, "current_time", json_integer(janus_get_monotonic_time()));
2900 		if(handle->app && janus_plugin_session_is_alive(handle->app_handle)) {
2901 			janus_plugin *plugin = (janus_plugin *)handle->app;
2902 			json_object_set_new(info, "plugin", json_string(plugin->get_package()));
2903 			if(plugin->query_session) {
2904 				/* FIXME This check will NOT work with legacy plugins that were compiled BEFORE the method was specified in plugin.h */
2905 				json_t *query = plugin->query_session(handle->app_handle);
2906 				if(query != NULL) {
2907 					/* Make sure this is a JSON object */
2908 					if(!json_is_object(query)) {
2909 						JANUS_LOG(LOG_WARN, "Ignoring invalid query response from the plugin (not an object)\n");
2910 						json_decref(query);
2911 					} else {
2912 						json_object_set_new(info, "plugin_specific", query);
2913 					}
2914 					query = NULL;
2915 				}
2916 			}
2917 		}
2918 		if(plugin_only)
2919 			goto info_done;
2920 		json_t *flags = json_object();
2921 		json_object_set_new(flags, "got-offer", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER) ? json_true() : json_false());
2922 		json_object_set_new(flags, "got-answer", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER) ? json_true() : json_false());
2923 		json_object_set_new(flags, "negotiated", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_NEGOTIATED) ? json_true() : json_false());
2924 		json_object_set_new(flags, "processing-offer", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER) ? json_true() : json_false());
2925 		json_object_set_new(flags, "starting", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_START) ? json_true() : json_false());
2926 		json_object_set_new(flags, "ice-restart", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ICE_RESTART) ? json_true() : json_false());
2927 		json_object_set_new(flags, "ready", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_READY) ? json_true() : json_false());
2928 		json_object_set_new(flags, "stopped", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP) ? json_true() : json_false());
2929 		json_object_set_new(flags, "alert", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT) ? json_true() : json_false());
2930 		json_object_set_new(flags, "trickle", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE) ? json_true() : json_false());
2931 		json_object_set_new(flags, "all-trickles", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALL_TRICKLES) ? json_true() : json_false());
2932 		json_object_set_new(flags, "resend-trickles", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RESEND_TRICKLES) ? json_true() : json_false());
2933 		json_object_set_new(flags, "trickle-synced", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE_SYNCED) ? json_true() : json_false());
2934 		json_object_set_new(flags, "data-channels", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS) ? json_true() : json_false());
2935 		json_object_set_new(flags, "has-audio", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO) ? json_true() : json_false());
2936 		json_object_set_new(flags, "has-video", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO) ? json_true() : json_false());
2937 		json_object_set_new(flags, "new-datachan-sdp", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_NEW_DATACHAN_SDP) ? json_true() : json_false());
2938 		json_object_set_new(flags, "rfc4588-rtx", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RFC4588_RTX) ? json_true() : json_false());
2939 		json_object_set_new(flags, "cleaning", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING) ? json_true() : json_false());
2940 		json_object_set_new(flags, "e2ee", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_E2EE) ? json_true() : json_false());
2941 		json_object_set_new(info, "flags", flags);
2942 		if(handle->agent) {
2943 			json_object_set_new(info, "agent-created", json_integer(handle->agent_created));
2944 			json_object_set_new(info, "ice-mode", json_string(janus_ice_is_ice_lite_enabled() ? "lite" : "full"));
2945 			json_object_set_new(info, "ice-role", json_string(handle->controlling ? "controlling" : "controlled"));
2946 		}
2947 		json_t *sdps = json_object();
2948 		if(handle->rtp_profile)
2949 			json_object_set_new(sdps, "profile", json_string(handle->rtp_profile));
2950 		if(handle->local_sdp)
2951 			json_object_set_new(sdps, "local", json_string(handle->local_sdp));
2952 		if(handle->remote_sdp)
2953 			json_object_set_new(sdps, "remote", json_string(handle->remote_sdp));
2954 		json_object_set_new(info, "sdps", sdps);
2955 		if(handle->pending_trickles)
2956 			json_object_set_new(info, "pending-trickles", json_integer(g_list_length(handle->pending_trickles)));
2957 		if(handle->queued_packets)
2958 			json_object_set_new(info, "queued-packets", json_integer(g_async_queue_length(handle->queued_packets)));
2959 		if(g_atomic_int_get(&handle->dump_packets) && handle->text2pcap) {
2960 			if(handle->text2pcap->text) {
2961 				json_object_set_new(info, "dump-to-text2pcap", json_true());
2962 				json_object_set_new(info, "text2pcap-file", json_string(handle->text2pcap->filename));
2963 			} else {
2964 				json_object_set_new(info, "dump-to-pcap", json_true());
2965 				json_object_set_new(info, "pcap-file", json_string(handle->text2pcap->filename));
2966 			}
2967 		}
2968 		json_t *streams = json_array();
2969 		if(handle->stream) {
2970 			json_t *s = janus_admin_stream_summary(handle->stream);
2971 			if(s)
2972 				json_array_append_new(streams, s);
2973 		}
2974 		json_object_set_new(info, "streams", streams);
2975 info_done:
2976 		janus_mutex_unlock(&handle->mutex);
2977 		/* Prepare JSON reply */
2978 		json_t *reply = janus_create_message("success", session_id, transaction_text);
2979 		json_object_set_new(reply, "handle_id", json_integer(handle_id));
2980 		json_object_set_new(reply, "info", info);
2981 		/* Send the success reply */
2982 		ret = janus_process_success(request, reply);
2983 		goto jsondone;
2984 	}
2985 
2986 jsondone:
2987 	/* Done processing */
2988 	if(handle != NULL)
2989 		janus_refcount_decrease(&handle->ref);
2990 	if(session != NULL)
2991 		janus_refcount_decrease(&session->ref);
2992 	return ret;
2993 }
2994 
janus_process_success(janus_request * request,json_t * payload)2995 int janus_process_success(janus_request *request, json_t *payload)
2996 {
2997 	if(!request || !payload)
2998 		return -1;
2999 	/* Pass to the right transport plugin */
3000 	JANUS_LOG(LOG_HUGE, "Sending %s API response to %s (%p)\n", request->admin ? "admin" : "Janus", request->transport->get_package(), request->instance);
3001 	return request->transport->send_message(request->instance, request->request_id, request->admin, payload);
3002 }
3003 
janus_process_error_string(janus_request * request,uint64_t session_id,const char * transaction,gint error,gchar * error_string)3004 static int janus_process_error_string(janus_request *request, uint64_t session_id, const char *transaction, gint error, gchar *error_string)
3005 {
3006 	if(!request)
3007 		return -1;
3008 	/* Done preparing error */
3009 	JANUS_LOG(LOG_VERB, "[%s] Returning %s API error %d (%s)\n", transaction, request->admin ? "admin" : "Janus", error, error_string);
3010 	/* Prepare JSON error */
3011 	json_t *reply = janus_create_message("error", session_id, transaction);
3012 	json_t *error_data = json_object();
3013 	json_object_set_new(error_data, "code", json_integer(error));
3014 	json_object_set_new(error_data, "reason", json_string(error_string));
3015 	json_object_set_new(reply, "error", error_data);
3016 	/* Pass to the right transport plugin */
3017 	return request->transport->send_message(request->instance, request->request_id, request->admin, reply);
3018 }
3019 
janus_process_error(janus_request * request,uint64_t session_id,const char * transaction,gint error,const char * format,...)3020 int janus_process_error(janus_request *request, uint64_t session_id, const char *transaction, gint error, const char *format, ...)
3021 {
3022 	if(!request)
3023 		return -1;
3024 	gchar *error_string = NULL;
3025 	gchar error_buf[512];
3026 	if(format == NULL) {
3027 		/* No error string provided, use the default one */
3028 		error_string = (gchar *)janus_get_api_error(error);
3029 	} else {
3030 		/* This callback has variable arguments (error string) */
3031 		va_list ap;
3032 		va_start(ap, format);
3033 		g_vsnprintf(error_buf, sizeof(error_buf), format, ap);
3034 		va_end(ap);
3035 		error_string = error_buf;
3036 	}
3037 	return janus_process_error_string(request, session_id, transaction, error, error_string);
3038 }
3039 
3040 /* Admin/monitor helpers */
janus_admin_stream_summary(janus_ice_stream * stream)3041 json_t *janus_admin_stream_summary(janus_ice_stream *stream) {
3042 	if(stream == NULL)
3043 		return NULL;
3044 	json_t *s = json_object();
3045 	json_object_set_new(s, "id", json_integer(stream->stream_id));
3046 	json_object_set_new(s, "ready", json_integer(stream->cdone));
3047 	json_t *ss = json_object();
3048 	if(stream->audio_ssrc)
3049 		json_object_set_new(ss, "audio", json_integer(stream->audio_ssrc));
3050 	if(stream->video_ssrc)
3051 		json_object_set_new(ss, "video", json_integer(stream->video_ssrc));
3052 	if(stream->video_ssrc_rtx)
3053 		json_object_set_new(ss, "video-rtx", json_integer(stream->video_ssrc_rtx));
3054 	if(stream->audio_ssrc_peer)
3055 		json_object_set_new(ss, "audio-peer", json_integer(stream->audio_ssrc_peer));
3056 	if(stream->video_ssrc_peer[0])
3057 		json_object_set_new(ss, "video-peer", json_integer(stream->video_ssrc_peer[0]));
3058 	if(stream->video_ssrc_peer[1])
3059 		json_object_set_new(ss, "video-peer-sim-1", json_integer(stream->video_ssrc_peer[1]));
3060 	if(stream->video_ssrc_peer[2])
3061 		json_object_set_new(ss, "video-peer-sim-2", json_integer(stream->video_ssrc_peer[2]));
3062 	if(stream->video_ssrc_peer_rtx[0])
3063 		json_object_set_new(ss, "video-peer-rtx", json_integer(stream->video_ssrc_peer_rtx[0]));
3064 	if(stream->video_ssrc_peer_rtx[1])
3065 		json_object_set_new(ss, "video-peer-sim-1-rtx", json_integer(stream->video_ssrc_peer_rtx[1]));
3066 	if(stream->video_ssrc_peer_rtx[2])
3067 		json_object_set_new(ss, "video-peer-sim-2-rtx", json_integer(stream->video_ssrc_peer_rtx[2]));
3068 	json_object_set_new(s, "ssrc", ss);
3069 	if(stream->rid[0] && stream->rid_ext_id > 0) {
3070 		json_t *sr = json_object();
3071 		json_t *rid = json_array();
3072 		if(stream->rid[2])
3073 			json_array_append_new(rid, json_string(stream->rid[2]));
3074 		if(stream->rid[1])
3075 			json_array_append_new(rid, json_string(stream->rid[1]));
3076 		json_array_append_new(rid, json_string(stream->rid[0]));
3077 		json_object_set_new(sr, "rid", rid);
3078 		json_object_set_new(sr, "rid-ext-id", json_integer(stream->rid_ext_id));
3079 		if(stream->ridrtx_ext_id > 0)
3080 			json_object_set_new(sr, "ridrtx-ext-id", json_integer(stream->ridrtx_ext_id));
3081 		json_object_set_new(sr, "rid-order", json_string(stream->rids_hml ? "hml" : "lmh"));
3082 		if(stream->legacy_rid)
3083 			json_object_set_new(sr, "rid-syntax", json_string("legacy"));
3084 		json_object_set_new(s, "rid-simulcast", sr);
3085 	}
3086 	json_t *sd = json_object();
3087 	json_object_set_new(sd, "audio-send", stream->audio_send ? json_true() : json_false());
3088 	json_object_set_new(sd, "audio-recv", stream->audio_recv ? json_true() : json_false());
3089 	json_object_set_new(sd, "video-send", stream->video_send ? json_true() : json_false());
3090 	json_object_set_new(sd, "video-recv", stream->video_recv ? json_true() : json_false());
3091 	json_object_set_new(s, "direction", sd);
3092 	if(stream->audio_payload_type > -1 || stream->video_payload_type > -1) {
3093 		json_t *sc = json_object();
3094 		if(stream->audio_payload_type > -1)
3095 			json_object_set_new(sc, "audio-pt", json_integer(stream->audio_payload_type));
3096 		if(stream->audio_codec != NULL)
3097 			json_object_set_new(sc, "audio-codec", json_string(stream->audio_codec));
3098 		if(stream->video_payload_type > -1)
3099 			json_object_set_new(sc, "video-pt", json_integer(stream->video_payload_type));
3100 		if(stream->video_rtx_payload_type > -1)
3101 			json_object_set_new(sc, "video-rtx-pt", json_integer(stream->video_rtx_payload_type));
3102 		if(stream->video_codec != NULL)
3103 			json_object_set_new(sc, "video-codec", json_string(stream->video_codec));
3104 		json_object_set_new(s, "codecs", sc);
3105 	}
3106 	json_t *se = json_object();
3107 	if(stream->mid_ext_id > 0)
3108 		json_object_set_new(se, JANUS_RTP_EXTMAP_MID, json_integer(stream->mid_ext_id));
3109 	if(stream->rid_ext_id > 0)
3110 		json_object_set_new(se, JANUS_RTP_EXTMAP_RID, json_integer(stream->rid_ext_id));
3111 	if(stream->ridrtx_ext_id > 0)
3112 		json_object_set_new(se, JANUS_RTP_EXTMAP_REPAIRED_RID, json_integer(stream->ridrtx_ext_id));
3113 	if(stream->abs_send_time_ext_id > 0)
3114 		json_object_set_new(se, JANUS_RTP_EXTMAP_ABS_SEND_TIME, json_integer(stream->abs_send_time_ext_id));
3115 	if(stream->transport_wide_cc_ext_id > 0)
3116 		json_object_set_new(se, JANUS_RTP_EXTMAP_TRANSPORT_WIDE_CC, json_integer(stream->transport_wide_cc_ext_id));
3117 	if(stream->audiolevel_ext_id > 0)
3118 		json_object_set_new(se, JANUS_RTP_EXTMAP_AUDIO_LEVEL, json_integer(stream->audiolevel_ext_id));
3119 	if(stream->videoorientation_ext_id > 0)
3120 		json_object_set_new(se, JANUS_RTP_EXTMAP_VIDEO_ORIENTATION, json_integer(stream->videoorientation_ext_id));
3121 	json_object_set_new(s, "extensions", se);
3122 	json_t *bwe = json_object();
3123 	json_object_set_new(bwe, "twcc", stream->do_transport_wide_cc ? json_true() : json_false());
3124 	if(stream->transport_wide_cc_ext_id > 0)
3125 		json_object_set_new(bwe, "twcc-ext-id", json_integer(stream->transport_wide_cc_ext_id));
3126 	json_object_set_new(s, "bwe", bwe);
3127 	json_object_set_new(s, "nack-queue-ms", json_integer(stream->nack_queue_ms));
3128 	json_t *components = json_array();
3129 	if(stream->component) {
3130 		json_t *c = janus_admin_component_summary(stream->component);
3131 		if(c)
3132 			json_array_append_new(components, c);
3133 	}
3134 	json_t *rtcp_stats = NULL;
3135 	if(stream->audio_rtcp_ctx != NULL) {
3136 		rtcp_stats = json_object();
3137 		json_t *audio_rtcp_stats = json_object();
3138 		json_object_set_new(audio_rtcp_stats, "base", json_integer(stream->audio_rtcp_ctx->tb));
3139 		json_object_set_new(audio_rtcp_stats, "rtt", json_integer(janus_rtcp_context_get_rtt(stream->audio_rtcp_ctx)));
3140 		json_object_set_new(audio_rtcp_stats, "lost", json_integer(janus_rtcp_context_get_lost_all(stream->audio_rtcp_ctx, FALSE)));
3141 		json_object_set_new(audio_rtcp_stats, "lost-by-remote", json_integer(janus_rtcp_context_get_lost_all(stream->audio_rtcp_ctx, TRUE)));
3142 		json_object_set_new(audio_rtcp_stats, "jitter-local", json_integer(janus_rtcp_context_get_jitter(stream->audio_rtcp_ctx, FALSE)));
3143 		json_object_set_new(audio_rtcp_stats, "jitter-remote", json_integer(janus_rtcp_context_get_jitter(stream->audio_rtcp_ctx, TRUE)));
3144 		json_object_set_new(audio_rtcp_stats, "in-link-quality", json_integer(janus_rtcp_context_get_in_link_quality(stream->audio_rtcp_ctx)));
3145 		json_object_set_new(audio_rtcp_stats, "in-media-link-quality", json_integer(janus_rtcp_context_get_in_media_link_quality(stream->audio_rtcp_ctx)));
3146 		json_object_set_new(audio_rtcp_stats, "out-link-quality", json_integer(janus_rtcp_context_get_out_link_quality(stream->audio_rtcp_ctx)));
3147 		json_object_set_new(audio_rtcp_stats, "out-media-link-quality", json_integer(janus_rtcp_context_get_out_media_link_quality(stream->audio_rtcp_ctx)));
3148 		json_object_set_new(rtcp_stats, "audio", audio_rtcp_stats);
3149 	}
3150 	int vindex=0;
3151 	for(vindex=0; vindex<3; vindex++) {
3152 		if(stream->video_rtcp_ctx[vindex] != NULL) {
3153 			if(rtcp_stats == NULL)
3154 				rtcp_stats = json_object();
3155 			json_t *video_rtcp_stats = json_object();
3156 			json_object_set_new(video_rtcp_stats, "base", json_integer(stream->video_rtcp_ctx[vindex]->tb));
3157 			if(vindex == 0)
3158 				json_object_set_new(video_rtcp_stats, "rtt", json_integer(janus_rtcp_context_get_rtt(stream->video_rtcp_ctx[vindex])));
3159 			json_object_set_new(video_rtcp_stats, "lost", json_integer(janus_rtcp_context_get_lost_all(stream->video_rtcp_ctx[vindex], FALSE)));
3160 			json_object_set_new(video_rtcp_stats, "lost-by-remote", json_integer(janus_rtcp_context_get_lost_all(stream->video_rtcp_ctx[vindex], TRUE)));
3161 			json_object_set_new(video_rtcp_stats, "jitter-local", json_integer(janus_rtcp_context_get_jitter(stream->video_rtcp_ctx[vindex], FALSE)));
3162 			json_object_set_new(video_rtcp_stats, "jitter-remote", json_integer(janus_rtcp_context_get_jitter(stream->video_rtcp_ctx[vindex], TRUE)));
3163 			json_object_set_new(video_rtcp_stats, "in-link-quality", json_integer(janus_rtcp_context_get_in_link_quality(stream->video_rtcp_ctx[vindex])));
3164 			json_object_set_new(video_rtcp_stats, "in-media-link-quality", json_integer(janus_rtcp_context_get_in_media_link_quality(stream->video_rtcp_ctx[vindex])));
3165 			json_object_set_new(video_rtcp_stats, "out-link-quality", json_integer(janus_rtcp_context_get_out_link_quality(stream->video_rtcp_ctx[vindex])));
3166 			json_object_set_new(video_rtcp_stats, "out-media-link-quality", json_integer(janus_rtcp_context_get_out_media_link_quality(stream->video_rtcp_ctx[vindex])));
3167 			if(vindex == 0)
3168 				json_object_set_new(rtcp_stats, "video", video_rtcp_stats);
3169 			else if(vindex == 1)
3170 				json_object_set_new(rtcp_stats, "video-sim1", video_rtcp_stats);
3171 			else
3172 				json_object_set_new(rtcp_stats, "video-sim2", video_rtcp_stats);
3173 		}
3174 	}
3175 	if(rtcp_stats != NULL)
3176 		json_object_set_new(s, "rtcp_stats", rtcp_stats);
3177 	if(stream->remb_bitrate > 0)
3178 		json_object_set_new(s, "remb-bitrate", json_integer(stream->remb_bitrate));
3179 	json_object_set_new(s, "components", components);
3180 	return s;
3181 }
3182 
janus_admin_component_summary(janus_ice_component * component)3183 json_t *janus_admin_component_summary(janus_ice_component *component) {
3184 	if(component == NULL)
3185 		return NULL;
3186 	janus_ice_handle *handle = component->stream ? component->stream->handle : NULL;
3187 	json_t *c = json_object();
3188 	json_object_set_new(c, "id", json_integer(component->component_id));
3189 	json_object_set_new(c, "state", json_string(janus_get_ice_state_name(component->state)));
3190 	if(component->icefailed_detected) {
3191 		json_object_set_new(c, "failed-detected", json_integer(component->icefailed_detected));
3192 		json_object_set_new(c, "icetimer-started", component->icestate_source ? json_true() : json_false());
3193 	}
3194 	if(component->component_connected > 0)
3195 		json_object_set_new(c, "connected", json_integer(component->component_connected));
3196 	if(component->local_candidates) {
3197 		json_t *cs = json_array();
3198 		GSList *candidates = component->local_candidates, *i = NULL;
3199 		for (i = candidates; i; i = i->next) {
3200 			gchar *lc = (gchar *) i->data;
3201 			if(lc)
3202 				json_array_append_new(cs, json_string(lc));
3203 		}
3204 		json_object_set_new(c, "local-candidates", cs);
3205 	}
3206 	if(component->remote_candidates) {
3207 		json_t *cs = json_array();
3208 		GSList *candidates = component->remote_candidates, *i = NULL;
3209 		for (i = candidates; i; i = i->next) {
3210 			gchar *rc = (gchar *) i->data;
3211 			if(rc)
3212 				json_array_append_new(cs, json_string(rc));
3213 		}
3214 		json_object_set_new(c, "remote-candidates", cs);
3215 	}
3216 	if(component->selected_pair) {
3217 		json_object_set_new(c, "selected-pair", json_string(component->selected_pair));
3218 	}
3219 	json_t *d = json_object();
3220 	json_t *in_stats = json_object();
3221 	json_t *out_stats = json_object();
3222 	if(component->dtls) {
3223 		janus_dtls_srtp *dtls = component->dtls;
3224 		json_object_set_new(d, "fingerprint", json_string(janus_dtls_get_local_fingerprint()));
3225 		if(component->stream) {
3226 			if(component->stream->remote_fingerprint)
3227 				json_object_set_new(d, "remote-fingerprint", json_string(component->stream->remote_fingerprint));
3228 			if(component->stream->remote_hashing)
3229 				json_object_set_new(d, "remote-fingerprint-hash", json_string(component->stream->remote_hashing));
3230 			json_object_set_new(d, "dtls-role", json_string(janus_get_dtls_srtp_role(component->stream->dtls_role)));
3231 		}
3232 		json_object_set_new(d, "dtls-state", json_string(janus_get_dtls_srtp_state(dtls->dtls_state)));
3233 		json_object_set_new(d, "retransmissions", json_integer(dtls->retransmissions));
3234 		json_object_set_new(d, "valid", dtls->srtp_valid ? json_true() : json_false());
3235 		const char *srtp_profile = janus_get_dtls_srtp_profile(dtls->srtp_profile);
3236 		json_object_set_new(d, "srtp-profile", json_string(srtp_profile ? srtp_profile : "none"));
3237 		json_object_set_new(d, "ready", dtls->ready ? json_true() : json_false());
3238 		if(dtls->dtls_started > 0)
3239 			json_object_set_new(d, "handshake-started", json_integer(dtls->dtls_started));
3240 		if(dtls->dtls_connected > 0)
3241 			json_object_set_new(d, "connected", json_integer(dtls->dtls_connected));
3242 		if(handle && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO)) {
3243 			json_object_set_new(in_stats, "audio_packets", json_integer(component->in_stats.audio.packets));
3244 			json_object_set_new(in_stats, "audio_bytes", json_integer(component->in_stats.audio.bytes));
3245 			json_object_set_new(in_stats, "audio_bytes_lastsec", json_integer(component->in_stats.audio.bytes_lastsec));
3246 			json_object_set_new(in_stats, "do_audio_nacks", component->do_audio_nacks ? json_true() : json_false());
3247 			if(component->do_audio_nacks) {
3248 				json_object_set_new(in_stats, "audio_nacks", json_integer(component->in_stats.audio.nacks));
3249 				if(component->stream && component->stream->audio_rtcp_ctx)
3250 					json_object_set_new(in_stats, "audio_retransmissions", json_integer(component->stream->audio_rtcp_ctx->retransmitted));
3251 			}
3252 		}
3253 		if(handle && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO)) {
3254 			int vindex=0;
3255 			for(vindex=0; vindex<3; vindex++) {
3256 				if(vindex > 0 && component->stream->video_ssrc_peer[vindex] == 0)
3257 					continue;
3258 				json_t *container = (vindex == 0 ? in_stats : json_object());
3259 				json_object_set_new(container, "video_packets", json_integer(component->in_stats.video[vindex].packets));
3260 				json_object_set_new(container, "video_bytes", json_integer(component->in_stats.video[vindex].bytes));
3261 				json_object_set_new(container, "video_bytes_lastsec", json_integer(component->in_stats.video[vindex].bytes_lastsec));
3262 				if(vindex == 0)
3263 					json_object_set_new(container, "do_video_nacks", component->do_video_nacks ? json_true() : json_false());
3264 				if(component->do_video_nacks) {
3265 					json_object_set_new(container, "video_nacks", json_integer(component->in_stats.video[vindex].nacks));
3266 					if(component->stream && component->stream->video_rtcp_ctx[vindex])
3267 						json_object_set_new(in_stats, "video_retransmissions", json_integer(component->stream->video_rtcp_ctx[vindex]->retransmitted));
3268 				}
3269 				if(vindex == 1)
3270 					json_object_set_new(in_stats, "video-simulcast-1", container);
3271 				else if(vindex == 2)
3272 					json_object_set_new(in_stats, "video-simulcast-2", container);
3273 			}
3274 		}
3275 		json_object_set_new(in_stats, "data_packets", json_integer(component->in_stats.data.packets));
3276 		json_object_set_new(in_stats, "data_bytes", json_integer(component->in_stats.data.bytes));
3277 		if(handle && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO)) {
3278 			json_object_set_new(out_stats, "audio_packets", json_integer(component->out_stats.audio.packets));
3279 			json_object_set_new(out_stats, "audio_bytes", json_integer(component->out_stats.audio.bytes));
3280 			json_object_set_new(out_stats, "audio_bytes_lastsec", json_integer(component->out_stats.audio.bytes_lastsec));
3281 			json_object_set_new(out_stats, "audio_nacks", json_integer(component->out_stats.audio.nacks));
3282 		}
3283 		if(handle && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO)) {
3284 			json_object_set_new(out_stats, "video_packets", json_integer(component->out_stats.video[0].packets));
3285 			json_object_set_new(out_stats, "video_bytes", json_integer(component->out_stats.video[0].bytes));
3286 			json_object_set_new(out_stats, "video_bytes_lastsec", json_integer(component->out_stats.video[0].bytes_lastsec));
3287 			json_object_set_new(out_stats, "video_nacks", json_integer(component->out_stats.video[0].nacks));
3288 		}
3289 		json_object_set_new(out_stats, "data_packets", json_integer(component->out_stats.data.packets));
3290 		json_object_set_new(out_stats, "data_bytes", json_integer(component->out_stats.data.bytes));
3291 #ifdef HAVE_SCTP
3292 		/* FIXME Actually check if this succeeded? */
3293 		json_object_set_new(d, "sctp-association", dtls->sctp ? json_true() : json_false());
3294 #endif
3295 	}
3296 	json_object_set_new(c, "dtls", d);
3297 	json_object_set_new(c, "in_stats", in_stats);
3298 	json_object_set_new(c, "out_stats", out_stats);
3299 	return c;
3300 }
3301 
3302 
3303 /* Transports */
janus_transport_close(gpointer key,gpointer value,gpointer user_data)3304 void janus_transport_close(gpointer key, gpointer value, gpointer user_data) {
3305 	janus_transport *transport = (janus_transport *)value;
3306 	if(!transport)
3307 		return;
3308 	transport->destroy();
3309 }
3310 
janus_transportso_close(gpointer key,gpointer value,gpointer user_data)3311 void janus_transportso_close(gpointer key, gpointer value, gpointer user_data) {
3312 	void *transport = value;
3313 	if(!transport)
3314 		return;
3315 	/* FIXME We don't dlclose transports to be sure we can detect leaks */
3316 	//~ dlclose(transport);
3317 }
3318 
3319 /* Transport callback interface */
janus_transport_incoming_request(janus_transport * plugin,janus_transport_session * transport,void * request_id,gboolean admin,json_t * message,json_error_t * error)3320 void janus_transport_incoming_request(janus_transport *plugin, janus_transport_session *transport, void *request_id, gboolean admin, json_t *message, json_error_t *error) {
3321 	JANUS_LOG(LOG_VERB, "Got %s API request from %s (%p)\n", admin ? "an admin" : "a Janus", plugin->get_package(), transport);
3322 	/* Create a janus_request instance to handle the request */
3323 	janus_request *request = janus_request_new(plugin, transport, request_id, admin, message);
3324 	/* Enqueue the request, the thread will pick it up */
3325 	g_async_queue_push(requests, request);
3326 }
3327 
janus_transport_gone(janus_transport * plugin,janus_transport_session * transport)3328 void janus_transport_gone(janus_transport *plugin, janus_transport_session *transport) {
3329 	/* Get rid of sessions this transport was handling */
3330 	JANUS_LOG(LOG_VERB, "A %s transport instance has gone away (%p)\n", plugin->get_package(), transport);
3331 	janus_mutex_lock(&sessions_mutex);
3332 	if(sessions && g_hash_table_size(sessions) > 0) {
3333 		GHashTableIter iter;
3334 		gpointer value;
3335 		g_hash_table_iter_init(&iter, sessions);
3336 		while(g_hash_table_iter_next(&iter, NULL, &value)) {
3337 			janus_session *session = (janus_session *) value;
3338 			if(!session || g_atomic_int_get(&session->destroyed) || g_atomic_int_get(&session->timedout) || session->last_activity == 0)
3339 				continue;
3340 			if(session->source && session->source->instance == transport) {
3341 				JANUS_LOG(LOG_VERB, "  -- Session %"SCNu64" will be over if not reclaimed\n", session->session_id);
3342 				JANUS_LOG(LOG_VERB, "  -- Marking Session %"SCNu64" as over\n", session->session_id);
3343 				if(reclaim_session_timeout < 1) { /* Reclaim session timeouts are disabled */
3344 					/* Mark the session as destroyed */
3345 					janus_session_destroy(session);
3346 					g_hash_table_iter_remove(&iter);
3347 				} else {
3348 					/* Set flag for transport_gone. The Janus sessions watchdog will clean this up if not reclaimed */
3349 					g_atomic_int_set(&session->transport_gone, 1);
3350 				}
3351 			}
3352 		}
3353 	}
3354 	janus_mutex_unlock(&sessions_mutex);
3355 }
3356 
janus_transport_is_api_secret_needed(janus_transport * plugin)3357 gboolean janus_transport_is_api_secret_needed(janus_transport *plugin) {
3358 	return api_secret != NULL;
3359 }
3360 
janus_transport_is_api_secret_valid(janus_transport * plugin,const char * apisecret)3361 gboolean janus_transport_is_api_secret_valid(janus_transport *plugin, const char *apisecret) {
3362 	if(api_secret == NULL)
3363 		return TRUE;
3364 	return apisecret && janus_strcmp_const_time(apisecret, api_secret);
3365 }
3366 
janus_transport_is_auth_token_needed(janus_transport * plugin)3367 gboolean janus_transport_is_auth_token_needed(janus_transport *plugin) {
3368 	return janus_auth_is_enabled();
3369 }
3370 
janus_transport_is_auth_token_valid(janus_transport * plugin,const char * token)3371 gboolean janus_transport_is_auth_token_valid(janus_transport *plugin, const char *token) {
3372 	if(!janus_auth_is_enabled())
3373 		return TRUE;
3374 	return token && janus_auth_check_token(token);
3375 }
3376 
janus_transport_notify_event(janus_transport * plugin,void * transport,json_t * event)3377 void janus_transport_notify_event(janus_transport *plugin, void *transport, json_t *event) {
3378 	/* A plugin asked to notify an event to the handlers */
3379 	if(!plugin || !event || !json_is_object(event))
3380 		return;
3381 	/* Notify event handlers */
3382 	if(janus_events_is_enabled()) {
3383 		janus_events_notify_handlers(JANUS_EVENT_TYPE_TRANSPORT, JANUS_EVENT_SUBTYPE_NONE,
3384 			0, plugin->get_package(), transport, event);
3385 	} else {
3386 		json_decref(event);
3387 	}
3388 }
3389 
janus_transport_task(gpointer data,gpointer user_data)3390 void janus_transport_task(gpointer data, gpointer user_data) {
3391 	JANUS_LOG(LOG_VERB, "Transport task pool, serving request\n");
3392 	janus_request *request = (janus_request *)data;
3393 	if(request == NULL) {
3394 		JANUS_LOG(LOG_ERR, "Missing request\n");
3395 		return;
3396 	}
3397 	if(!request->admin)
3398 		janus_process_incoming_request(request);
3399 	else
3400 		janus_process_incoming_admin_request(request);
3401 	/* Done */
3402 	janus_request_destroy(request);
3403 }
3404 
3405 
3406 /* Thread to handle incoming requests: may involve an asynchronous task for plugin messaging */
janus_transport_requests(void * data)3407 static void *janus_transport_requests(void *data) {
3408 	JANUS_LOG(LOG_INFO, "Joining Janus requests handler thread\n");
3409 	janus_request *request = NULL;
3410 	gboolean destroy = FALSE;
3411 	while(!g_atomic_int_get(&stop)) {
3412 		request = g_async_queue_pop(requests);
3413 		if(request == &exit_message)
3414 			break;
3415 		/* Should we process the request synchronously or with a task from the thread pool? */
3416 		destroy = TRUE;
3417 		/* Process the request synchronously only it's not a message for a plugin */
3418 		json_t *message = json_object_get(request->message, "janus");
3419 		const gchar *message_text = json_string_value(message);
3420 		if(message_text && !strcasecmp(message_text, request->admin ? "message_plugin" : "message")) {
3421 			/* Spawn a task thread */
3422 			GError *tperror = NULL;
3423 			g_thread_pool_push(tasks, request, &tperror);
3424 			if(tperror != NULL) {
3425 				/* Something went wrong... */
3426 				JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to push task in thread pool...\n",
3427 					tperror->code, tperror->message ? tperror->message : "??");
3428 				g_error_free(tperror);
3429 				json_t *transaction = json_object_get(message, "transaction");
3430 				const char *transaction_text = json_is_string(transaction) ? json_string_value(transaction) : NULL;
3431 				janus_process_error(request, 0, transaction_text, JANUS_ERROR_UNKNOWN, "Thread pool error");
3432 			} else {
3433 				/* Don't destroy the request now, the task will take care of that */
3434 				destroy = FALSE;
3435 			}
3436 		} else {
3437 			if(!request->admin)
3438 				janus_process_incoming_request(request);
3439 			else
3440 				janus_process_incoming_admin_request(request);
3441 		}
3442 		/* Done */
3443 		if(destroy)
3444 			janus_request_destroy(request);
3445 	}
3446 	JANUS_LOG(LOG_INFO, "Leaving Janus requests handler thread\n");
3447 	return NULL;
3448 }
3449 
3450 
3451 /* Event handlers */
janus_eventhandler_close(gpointer key,gpointer value,gpointer user_data)3452 void janus_eventhandler_close(gpointer key, gpointer value, gpointer user_data) {
3453 	janus_eventhandler *eventhandler = (janus_eventhandler *)value;
3454 	if(!eventhandler)
3455 		return;
3456 	eventhandler->destroy();
3457 }
3458 
janus_eventhandlerso_close(gpointer key,gpointer value,gpointer user_data)3459 void janus_eventhandlerso_close(gpointer key, gpointer value, gpointer user_data) {
3460 	void *eventhandler = (janus_eventhandler *)value;
3461 	if(!eventhandler)
3462 		return;
3463 	//~ dlclose(eventhandler);
3464 }
3465 
3466 
3467 /* Loggers */
janus_logger_close(gpointer key,gpointer value,gpointer user_data)3468 void janus_logger_close(gpointer key, gpointer value, gpointer user_data) {
3469 	janus_logger *logger = (janus_logger *)value;
3470 	if(!logger)
3471 		return;
3472 	logger->destroy();
3473 }
3474 
janus_loggerso_close(gpointer key,gpointer value,gpointer user_data)3475 void janus_loggerso_close(gpointer key, gpointer value, gpointer user_data) {
3476 	void *logger = (janus_logger *)value;
3477 	if(!logger)
3478 		return;
3479 	//~ dlclose(logger);
3480 }
3481 
3482 
3483 /* Plugins */
janus_plugin_close(gpointer key,gpointer value,gpointer user_data)3484 void janus_plugin_close(gpointer key, gpointer value, gpointer user_data) {
3485 	janus_plugin *plugin = (janus_plugin *)value;
3486 	if(!plugin)
3487 		return;
3488 	plugin->destroy();
3489 }
3490 
janus_pluginso_close(gpointer key,gpointer value,gpointer user_data)3491 void janus_pluginso_close(gpointer key, gpointer value, gpointer user_data) {
3492 	void *plugin = value;
3493 	if(!plugin)
3494 		return;
3495 	/* FIXME We don't dlclose plugins to be sure we can detect leaks */
3496 	//~ dlclose(plugin);
3497 }
3498 
janus_plugin_find(const gchar * package)3499 janus_plugin *janus_plugin_find(const gchar *package) {
3500 	if(package != NULL && plugins != NULL)	/* FIXME Do we need to fix the key pointer? */
3501 		return g_hash_table_lookup(plugins, package);
3502 	return NULL;
3503 }
3504 
3505 
3506 /* Plugin callback interface */
janus_plugin_push_event(janus_plugin_session * plugin_session,janus_plugin * plugin,const char * transaction,json_t * message,json_t * jsep)3507 int janus_plugin_push_event(janus_plugin_session *plugin_session, janus_plugin *plugin, const char *transaction, json_t *message, json_t *jsep) {
3508 	if(!plugin || !message)
3509 		return -1;
3510 	if(!janus_plugin_session_is_alive(plugin_session))
3511 		return -2;
3512 	janus_refcount_increase(&plugin_session->ref);
3513 	janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
3514 	if(!ice_handle || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)) {
3515 		janus_refcount_decrease(&plugin_session->ref);
3516 		return JANUS_ERROR_SESSION_NOT_FOUND;
3517 	}
3518 	janus_refcount_increase(&ice_handle->ref);
3519 	janus_session *session = ice_handle->session;
3520 	if(!session || g_atomic_int_get(&session->destroyed)) {
3521 		janus_refcount_decrease(&plugin_session->ref);
3522 		janus_refcount_decrease(&ice_handle->ref);
3523 		return JANUS_ERROR_SESSION_NOT_FOUND;
3524 	}
3525 	/* Make sure this is a JSON object */
3526 	if(!json_is_object(message)) {
3527 		JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (JSON error: not an object)\n", ice_handle->handle_id);
3528 		janus_refcount_decrease(&plugin_session->ref);
3529 		janus_refcount_decrease(&ice_handle->ref);
3530 		return JANUS_ERROR_INVALID_JSON_OBJECT;
3531 	}
3532 	/* Attach JSEP if possible? */
3533 	const char *sdp_type = json_string_value(json_object_get(jsep, "type"));
3534 	const char *sdp = json_string_value(json_object_get(jsep, "sdp"));
3535 	gboolean restart = json_object_get(jsep, "sdp") ? json_is_true(json_object_get(jsep, "restart")) : FALSE;
3536 	gboolean e2ee = json_object_get(jsep, "sdp") ? json_is_true(json_object_get(jsep, "e2ee")) : FALSE;
3537 	json_t *merged_jsep = NULL;
3538 	if(sdp_type != NULL && sdp != NULL) {
3539 		merged_jsep = janus_plugin_handle_sdp(plugin_session, plugin, sdp_type, sdp, restart);
3540 		if(merged_jsep == NULL) {
3541 			if(ice_handle == NULL || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
3542 					|| janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)) {
3543 				JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (handle not available anymore or negotiation stopped)\n", ice_handle->handle_id);
3544 				janus_refcount_decrease(&plugin_session->ref);
3545 				janus_refcount_decrease(&ice_handle->ref);
3546 				return JANUS_ERROR_HANDLE_NOT_FOUND;
3547 			} else {
3548 				JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (JSON error: problem with the SDP)\n", ice_handle->handle_id);
3549 				janus_refcount_decrease(&plugin_session->ref);
3550 				janus_refcount_decrease(&ice_handle->ref);
3551 				return JANUS_ERROR_JSEP_INVALID_SDP;
3552 			}
3553 		}
3554 	}
3555 	/* Reference the payload, as the plugin may still need it and will do a decref itself */
3556 	json_incref(message);
3557 	/* Prepare JSON event */
3558 	json_t *event = janus_create_message("event", session->session_id, transaction);
3559 	json_object_set_new(event, "sender", json_integer(ice_handle->handle_id));
3560 	if(janus_is_opaqueid_in_api_enabled() && ice_handle->opaque_id != NULL)
3561 		json_object_set_new(event, "opaque_id", json_string(ice_handle->opaque_id));
3562 	json_t *plugin_data = json_object();
3563 	json_object_set_new(plugin_data, "plugin", json_string(plugin->get_package()));
3564 	json_object_set_new(plugin_data, "data", message);
3565 	json_object_set_new(event, "plugindata", plugin_data);
3566 	if(merged_jsep != NULL) {
3567 		if(e2ee)
3568 			janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_E2EE);
3569 		if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_E2EE))
3570 			json_object_set_new(merged_jsep, "e2ee", json_true());
3571 		json_object_set_new(event, "jsep", merged_jsep);
3572 		/* In case event handlers are enabled, push the local SDP to all handlers */
3573 		if(janus_events_is_enabled()) {
3574 			const char *merged_sdp_type = json_string_value(json_object_get(merged_jsep, "type"));
3575 			const char *merged_sdp = json_string_value(json_object_get(merged_jsep, "sdp"));
3576 			/* Notify event handlers as well */
3577 			janus_events_notify_handlers(JANUS_EVENT_TYPE_JSEP, JANUS_EVENT_SUBTYPE_NONE,
3578 				session->session_id, ice_handle->handle_id, ice_handle->opaque_id, "local", merged_sdp_type, merged_sdp);
3579 		}
3580 	}
3581 	/* Send the event */
3582 	JANUS_LOG(LOG_VERB, "[%"SCNu64"] Sending event to transport...\n", ice_handle->handle_id);
3583 	janus_session_notify_event(session, event);
3584 
3585 	if((restart || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RESEND_TRICKLES))
3586 			&& janus_ice_is_full_trickle_enabled()) {
3587 		/* We're restarting ICE, send our trickle candidates again */
3588 		janus_ice_resend_trickles(ice_handle);
3589 	}
3590 
3591 	janus_refcount_decrease(&plugin_session->ref);
3592 	janus_refcount_decrease(&ice_handle->ref);
3593 	return JANUS_OK;
3594 }
3595 
janus_plugin_handle_sdp(janus_plugin_session * plugin_session,janus_plugin * plugin,const char * sdp_type,const char * sdp,gboolean restart)3596 json_t *janus_plugin_handle_sdp(janus_plugin_session *plugin_session, janus_plugin *plugin, const char *sdp_type, const char *sdp, gboolean restart) {
3597 	if(!janus_plugin_session_is_alive(plugin_session) ||
3598 			plugin == NULL || sdp_type == NULL || sdp == NULL) {
3599 		JANUS_LOG(LOG_ERR, "Invalid arguments\n");
3600 		return NULL;
3601 	}
3602 	janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
3603 	//~ if(ice_handle == NULL || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_READY)) {
3604 	if(ice_handle == NULL) {
3605 		JANUS_LOG(LOG_ERR, "Invalid ICE handle\n");
3606 		return NULL;
3607 	}
3608 	int offer = 0;
3609 	if(!strcasecmp(sdp_type, "offer")) {
3610 		/* This is an offer from a plugin */
3611 		offer = 1;
3612 		janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER);
3613 		janus_flags_clear(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER);
3614 	} else if(!strcasecmp(sdp_type, "answer")) {
3615 		/* This is an answer from a plugin */
3616 		janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER);
3617 		if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER))
3618 			janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_NEGOTIATED);
3619 	} else {
3620 		/* TODO Handle other messages */
3621 		JANUS_LOG(LOG_ERR, "Unknown type '%s'\n", sdp_type);
3622 		return NULL;
3623 	}
3624 	/* Is this valid SDP? */
3625 	char error_str[512];
3626 	error_str[0] = '\0';
3627 	int audio = 0, video = 0, data = 0;
3628 	janus_sdp *parsed_sdp = janus_sdp_preparse(ice_handle, sdp, error_str, sizeof(error_str), &audio, &video, &data);
3629 	if(parsed_sdp == NULL) {
3630 		JANUS_LOG(LOG_ERR, "[%"SCNu64"] Couldn't parse SDP... %s\n", ice_handle->handle_id, error_str);
3631 		return NULL;
3632 	}
3633 	gboolean updating = FALSE;
3634 	if(offer) {
3635 		/* We may still not have a local ICE setup */
3636 		JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio %s been negotiated\n", ice_handle->handle_id, audio ? "has" : "has NOT");
3637 		if(audio > 1) {
3638 			JANUS_LOG(LOG_ERR, "[%"SCNu64"] More than one audio line? only going to negotiate one...\n", ice_handle->handle_id);
3639 		}
3640 		JANUS_LOG(LOG_VERB, "[%"SCNu64"] Video %s been negotiated\n", ice_handle->handle_id, video ? "has" : "has NOT");
3641 		if(video > 1) {
3642 			JANUS_LOG(LOG_ERR, "[%"SCNu64"] More than one video line? only going to negotiate one...\n", ice_handle->handle_id);
3643 		}
3644 		JANUS_LOG(LOG_VERB, "[%"SCNu64"] SCTP/DataChannels %s been negotiated\n", ice_handle->handle_id, data ? "have" : "have NOT");
3645 		if(data > 1) {
3646 			JANUS_LOG(LOG_ERR, "[%"SCNu64"] More than one data line? only going to negotiate one...\n", ice_handle->handle_id);
3647 		}
3648 #ifndef HAVE_SCTP
3649 		if(data) {
3650 			JANUS_LOG(LOG_WARN, "[%"SCNu64"]   -- DataChannels have been negotiated, but support for them has not been compiled...\n", ice_handle->handle_id);
3651 		}
3652 #endif
3653 		/* Are we still cleaning up from a previous media session? */
3654 		if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING)) {
3655 			JANUS_LOG(LOG_VERB, "[%"SCNu64"] Still cleaning up from a previous media session, let's wait a bit...\n", ice_handle->handle_id);
3656 			gint64 waited = 0;
3657 			while(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING)) {
3658 				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Still cleaning up from a previous media session, let's wait a bit...\n", ice_handle->handle_id);
3659 				g_usleep(100000);
3660 				waited += 100000;
3661 				if(waited >= 3*G_USEC_PER_SEC) {
3662 					JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Waited 3 seconds, that's enough!\n", ice_handle->handle_id);
3663 					JANUS_LOG(LOG_ERR, "[%"SCNu64"] Still cleaning a previous session\n", ice_handle->handle_id);
3664 					janus_sdp_destroy(parsed_sdp);
3665 					return NULL;
3666 				}
3667 			}
3668 		}
3669 		if(ice_handle->agent == NULL) {
3670 			/* We still need to configure the WebRTC stuff: negotiate RFC4588 by default */
3671 			janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RFC4588_RTX);
3672 			/* Process SDP in order to setup ICE locally (this is going to result in an answer from the browser) */
3673 			janus_mutex_lock(&ice_handle->mutex);
3674 			if(janus_ice_setup_local(ice_handle, 0, audio, video, data, 1) < 0) {
3675 				JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error setting ICE locally\n", ice_handle->handle_id);
3676 				janus_sdp_destroy(parsed_sdp);
3677 				janus_mutex_unlock(&ice_handle->mutex);
3678 				return NULL;
3679 			}
3680 			janus_mutex_unlock(&ice_handle->mutex);
3681 		} else {
3682 			updating = TRUE;
3683 			JANUS_LOG(LOG_INFO, "[%"SCNu64"] Updating existing session\n", ice_handle->handle_id);
3684 			if(offer && ice_handle->stream) {
3685 				/* We might need some new properties set as well */
3686 				janus_ice_stream *stream = ice_handle->stream;
3687 				if(audio) {
3688 					if(!janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO)) {
3689 						janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO);
3690 						stream->audio_ssrc = janus_random_uint32();	/* FIXME Should we look for conflicts? */
3691 						if(stream->audio_rtcp_ctx == NULL) {
3692 							stream->audio_rtcp_ctx = g_malloc0(sizeof(rtcp_context));
3693 							stream->audio_rtcp_ctx->tb = 48000;	/* May change later */
3694 						}
3695 					}
3696 					if(ice_handle->audio_mid == NULL)
3697 						ice_handle->audio_mid = g_strdup("audio");
3698 				}
3699 				if(video) {
3700 					if(!janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO)) {
3701 						janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO);
3702 						stream->video_ssrc = janus_random_uint32();	/* FIXME Should we look for conflicts? */
3703 						if(stream->video_rtcp_ctx[0] == NULL) {
3704 							stream->video_rtcp_ctx[0] = g_malloc0(sizeof(rtcp_context));
3705 							stream->video_rtcp_ctx[0]->tb = 90000;	/* May change later */
3706 						}
3707 					}
3708 					if(ice_handle->video_mid == NULL)
3709 						ice_handle->video_mid = g_strdup("video");
3710 				}
3711 				if(data) {
3712 					if(ice_handle->data_mid == NULL)
3713 						ice_handle->data_mid = g_strdup("data");
3714 				}
3715 			}
3716 		}
3717 		/* Make sure we don't send the rid/repaired-rid attributes when offering ourselves */
3718 		int mid_ext_id = 0, transport_wide_cc_ext_id = 0, abs_send_time_ext_id = 0,
3719 			audiolevel_ext_id = 0, videoorientation_ext_id = 0;
3720 		GList *temp = parsed_sdp->m_lines;
3721 		while(temp) {
3722 			janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
3723 			GList *tempA = m->attributes;
3724 			while(tempA) {
3725 				janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
3726 				if(a->name && a->value) {
3727 					if(strstr(a->value, JANUS_RTP_EXTMAP_MID))
3728 						mid_ext_id = atoi(a->value);
3729 					else if(strstr(a->value, JANUS_RTP_EXTMAP_TRANSPORT_WIDE_CC))
3730 						transport_wide_cc_ext_id = atoi(a->value);
3731 					else if(strstr(a->value, JANUS_RTP_EXTMAP_ABS_SEND_TIME))
3732 						abs_send_time_ext_id = atoi(a->value);
3733 					else if(strstr(a->value, JANUS_RTP_EXTMAP_AUDIO_LEVEL))
3734 						audiolevel_ext_id = atoi(a->value);
3735 					else if(strstr(a->value, JANUS_RTP_EXTMAP_VIDEO_ORIENTATION))
3736 						videoorientation_ext_id = atoi(a->value);
3737 					else if(strstr(a->value, JANUS_RTP_EXTMAP_RID) ||
3738 							strstr(a->value, JANUS_RTP_EXTMAP_REPAIRED_RID)) {
3739 						m->attributes = g_list_remove(m->attributes, a);
3740 						tempA = m->attributes;
3741 						janus_sdp_attribute_destroy(a);
3742 						continue;
3743 					}
3744 				}
3745 				tempA = tempA->next;
3746 			}
3747 			temp = temp->next;
3748 		}
3749 		if(ice_handle->stream && ice_handle->stream->mid_ext_id != mid_ext_id)
3750 			ice_handle->stream->mid_ext_id = mid_ext_id;
3751 		if(ice_handle->stream && ice_handle->stream->transport_wide_cc_ext_id != transport_wide_cc_ext_id) {
3752 			ice_handle->stream->do_transport_wide_cc = transport_wide_cc_ext_id > 0 ? TRUE : FALSE;
3753 			ice_handle->stream->transport_wide_cc_ext_id = transport_wide_cc_ext_id;
3754 		}
3755 		if(ice_handle->stream && ice_handle->stream->abs_send_time_ext_id != abs_send_time_ext_id)
3756 			ice_handle->stream->abs_send_time_ext_id = abs_send_time_ext_id;
3757 		if(ice_handle->stream && ice_handle->stream->audiolevel_ext_id != audiolevel_ext_id)
3758 			ice_handle->stream->audiolevel_ext_id = audiolevel_ext_id;
3759 		if(ice_handle->stream && ice_handle->stream->videoorientation_ext_id != videoorientation_ext_id)
3760 			ice_handle->stream->videoorientation_ext_id = videoorientation_ext_id;
3761 	} else {
3762 		/* Check if the answer does contain the mid/rid/repaired-rid/abs-send-time/twcc extmaps */
3763 		gboolean do_mid = FALSE, do_rid = FALSE, do_repaired_rid = FALSE,
3764 			do_twcc = FALSE, do_abs_send_time = FALSE;
3765 		GList *temp = parsed_sdp->m_lines;
3766 		while(temp) {
3767 			janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
3768 			GList *tempA = m->attributes;
3769 			while(tempA) {
3770 				janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
3771 				if(a->name && a->value && !strcasecmp(a->name, "extmap")) {
3772 					if(strstr(a->value, JANUS_RTP_EXTMAP_MID))
3773 						do_mid = TRUE;
3774 					else if(strstr(a->value, JANUS_RTP_EXTMAP_RID))
3775 						do_rid = TRUE;
3776 					else if(strstr(a->value, JANUS_RTP_EXTMAP_REPAIRED_RID))
3777 						do_repaired_rid = TRUE;
3778 					else if(strstr(a->value, JANUS_RTP_EXTMAP_TRANSPORT_WIDE_CC))
3779 						do_twcc = TRUE;
3780 					else if(strstr(a->value, JANUS_RTP_EXTMAP_ABS_SEND_TIME))
3781 						do_abs_send_time = TRUE;
3782 				}
3783 				tempA = tempA->next;
3784 			}
3785 			temp = temp->next;
3786 		}
3787 		if(!do_mid && ice_handle->stream)
3788 			ice_handle->stream->mid_ext_id = 0;
3789 		if(!do_rid && ice_handle->stream) {
3790 			ice_handle->stream->rid_ext_id = 0;
3791 			ice_handle->stream->ridrtx_ext_id = 0;
3792 			g_free(ice_handle->stream->rid[0]);
3793 			ice_handle->stream->rid[0] = NULL;
3794 			g_free(ice_handle->stream->rid[1]);
3795 			ice_handle->stream->rid[1] = NULL;
3796 			g_free(ice_handle->stream->rid[2]);
3797 			ice_handle->stream->rid[2] = NULL;
3798 			if(ice_handle->stream->video_ssrc_peer_temp > 0) {
3799 				ice_handle->stream->video_ssrc_peer[0] = ice_handle->stream->video_ssrc_peer_temp;
3800 				ice_handle->stream->video_ssrc_peer_temp = 0;
3801 			}
3802 		}
3803 		if(!do_repaired_rid && ice_handle->stream)
3804 			ice_handle->stream->ridrtx_ext_id = 0;
3805 		if(!do_twcc && ice_handle->stream) {
3806 			ice_handle->stream->do_transport_wide_cc = FALSE;
3807 			ice_handle->stream->transport_wide_cc_ext_id = 0;
3808 		}
3809 		if(!do_abs_send_time && ice_handle->stream)
3810 			ice_handle->stream->abs_send_time_ext_id = 0;
3811 	}
3812 	if(!updating && !janus_ice_is_full_trickle_enabled()) {
3813 		/* Wait for candidates-done callback */
3814 		int waiting = 0;
3815 		while(ice_handle->cdone < 1) {
3816 			if(ice_handle == NULL || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
3817 					|| janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)) {
3818 				JANUS_LOG(LOG_WARN, "[%"SCNu64"] Handle detached or PC closed, giving up...!\n", ice_handle ? ice_handle->handle_id : 0);
3819 				janus_sdp_destroy(parsed_sdp);
3820 				return NULL;
3821 			}
3822 			if(ice_handle->cdone < 0) {
3823 				JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error gathering candidates!\n", ice_handle->handle_id);
3824 				janus_sdp_destroy(parsed_sdp);
3825 				return NULL;
3826 			}
3827 			if(waiting && (waiting % 5000) == 0) {
3828 				JANUS_LOG(LOG_WARN, "[%"SCNu64"] Waited 5s for candidates, that's way too much... going on with what we have (WebRTC setup might fail)\n", ice_handle->handle_id);
3829 				break;
3830 			}
3831 			if(waiting && (waiting % 1000) == 0) {
3832 				JANUS_LOG(LOG_WARN, "[%"SCNu64"] %s for candidates-done callback... (slow gathering, are you using STUN or TURN for Janus too, instead of just for users? Consider enabling full-trickle instead)\n",
3833 					ice_handle->handle_id, (waiting == 1000 ? "Waiting" : "Still waiting"));
3834 			}
3835 			waiting++;
3836 			g_usleep(1000);
3837 		}
3838 	}
3839 	/* Anonymize SDP */
3840 	if(janus_sdp_anonymize(parsed_sdp) < 0) {
3841 		/* Invalid SDP */
3842 		JANUS_LOG(LOG_ERR, "[%"SCNu64"] Invalid SDP\n", ice_handle->handle_id);
3843 		janus_sdp_destroy(parsed_sdp);
3844 		return NULL;
3845 	}
3846 
3847 	/* Check if this is a renegotiation and we need an ICE restart */
3848 	if(offer && restart)
3849 		janus_ice_restart(ice_handle);
3850 	/* Add our details */
3851 	janus_mutex_lock(&ice_handle->mutex);
3852 	janus_ice_stream *stream = ice_handle->stream;
3853 	if (stream == NULL) {
3854 		JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error stream not found\n", ice_handle->handle_id);
3855 		janus_sdp_destroy(parsed_sdp);
3856 		janus_mutex_unlock(&ice_handle->mutex);
3857 		return NULL;
3858 	}
3859 	if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RFC4588_RTX) &&
3860 			stream->rtx_payload_types == NULL) {
3861 		/* Make sure we have a list of rtx payload types to generate, if needed */
3862 		janus_sdp_mline *m = janus_sdp_mline_find(parsed_sdp, JANUS_SDP_VIDEO);
3863 		if(m && m->ptypes) {
3864 			stream->rtx_payload_types = g_hash_table_new(NULL, NULL);
3865 			GList *ptypes = g_list_copy(m->ptypes), *tempP = ptypes;
3866 			GList *rtx_ptypes = g_hash_table_get_values(stream->rtx_payload_types);
3867 			while(tempP) {
3868 				int ptype = GPOINTER_TO_INT(tempP->data);
3869 				int rtx_ptype = ptype+1;
3870 				if(rtx_ptype > 127)
3871 					rtx_ptype = 96;
3872 				while(g_list_find(m->ptypes, GINT_TO_POINTER(rtx_ptype))
3873 						|| g_list_find(rtx_ptypes, GINT_TO_POINTER(rtx_ptype))) {
3874 					rtx_ptype++;
3875 					if(rtx_ptype > 127)
3876 						rtx_ptype = 96;
3877 					if(rtx_ptype == ptype) {
3878 						/* We did a whole round? should never happen... */
3879 						rtx_ptype = -1;
3880 						break;
3881 					}
3882 				}
3883 				if(rtx_ptype > 0)
3884 					g_hash_table_insert(stream->rtx_payload_types, GINT_TO_POINTER(ptype), GINT_TO_POINTER(rtx_ptype));
3885 				g_list_free(rtx_ptypes);
3886 				rtx_ptypes = g_hash_table_get_values(stream->rtx_payload_types);
3887 				tempP = tempP->next;
3888 			}
3889 			g_list_free(ptypes);
3890 			g_list_free(rtx_ptypes);
3891 		}
3892 	}
3893 	/* Enrich the SDP the plugin gave us with all the WebRTC related stuff */
3894 	char *sdp_merged = janus_sdp_merge(ice_handle, parsed_sdp, offer ? TRUE : FALSE);
3895 	if(sdp_merged == NULL) {
3896 		/* Couldn't merge SDP */
3897 		JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error merging SDP\n", ice_handle->handle_id);
3898 		janus_sdp_destroy(parsed_sdp);
3899 		janus_mutex_unlock(&ice_handle->mutex);
3900 		return NULL;
3901 	}
3902 	janus_sdp_destroy(parsed_sdp);
3903 
3904 	if(!updating) {
3905 		if(offer) {
3906 			/* We set the flag to wait for an answer before handling trickle candidates */
3907 			janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
3908 		} else {
3909 			JANUS_LOG(LOG_VERB, "[%"SCNu64"] Sending answer, ready to setup remote candidates and send connectivity checks...\n", ice_handle->handle_id);
3910 			janus_request_ice_handle_answer(ice_handle, audio, video, data, NULL);
3911 		}
3912 	}
3913 #ifdef HAVE_SCTP
3914 	if(!offer && janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_READY)) {
3915 		/* Renegotiation: check if datachannels were just added on an existing PeerConnection */
3916 		if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS)) {
3917 			janus_ice_stream *stream = ice_handle->stream;
3918 			if(stream != NULL && stream->component != NULL &&
3919 					stream->component->dtls != NULL && stream->component->dtls->sctp == NULL) {
3920 				/* Create SCTP association as well */
3921 				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Creating datachannels...\n", ice_handle->handle_id);
3922 				janus_dtls_srtp_create_sctp(stream->component->dtls);
3923 			}
3924 		}
3925 	}
3926 #endif
3927 
3928 	/* Prepare JSON event */
3929 	json_t *jsep = json_object();
3930 	json_object_set_new(jsep, "type", json_string(sdp_type));
3931 	json_object_set_new(jsep, "sdp", json_string(sdp_merged));
3932 	char *tmp = ice_handle->local_sdp;
3933 	ice_handle->local_sdp = sdp_merged;
3934 	janus_mutex_unlock(&ice_handle->mutex);
3935 	g_free(tmp);
3936 	return jsep;
3937 }
3938 
janus_plugin_relay_rtp(janus_plugin_session * plugin_session,janus_plugin_rtp * packet)3939 void janus_plugin_relay_rtp(janus_plugin_session *plugin_session, janus_plugin_rtp *packet) {
3940 	if((plugin_session < (janus_plugin_session *)0x1000) || g_atomic_int_get(&plugin_session->stopped) ||
3941 			packet == NULL || packet->buffer == NULL || packet->length < 1)
3942 		return;
3943 	janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
3944 	if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
3945 			|| janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
3946 		return;
3947 	janus_ice_relay_rtp(handle, packet);
3948 }
3949 
janus_plugin_relay_rtcp(janus_plugin_session * plugin_session,janus_plugin_rtcp * packet)3950 void janus_plugin_relay_rtcp(janus_plugin_session *plugin_session, janus_plugin_rtcp *packet) {
3951 	if((plugin_session < (janus_plugin_session *)0x1000) || g_atomic_int_get(&plugin_session->stopped) ||
3952 			packet == NULL || packet->buffer == NULL || packet->length < 1)
3953 		return;
3954 	janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
3955 	if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
3956 			|| janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
3957 		return;
3958 	janus_ice_relay_rtcp(handle, packet);
3959 }
3960 
janus_plugin_relay_data(janus_plugin_session * plugin_session,janus_plugin_data * packet)3961 void janus_plugin_relay_data(janus_plugin_session *plugin_session, janus_plugin_data *packet) {
3962 	if((plugin_session < (janus_plugin_session *)0x1000) || g_atomic_int_get(&plugin_session->stopped) ||
3963 			packet == NULL || packet->buffer == NULL || packet->length < 1)
3964 		return;
3965 	janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
3966 	if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
3967 			|| janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
3968 		return;
3969 #ifdef HAVE_SCTP
3970 	janus_ice_relay_data(handle, packet);
3971 #else
3972 	JANUS_LOG(LOG_WARN, "Asked to relay data, but Data Channels support has not been compiled...\n");
3973 #endif
3974 }
3975 
janus_plugin_send_pli(janus_plugin_session * plugin_session)3976 void janus_plugin_send_pli(janus_plugin_session *plugin_session) {
3977 	if((plugin_session < (janus_plugin_session *)0x1000) || g_atomic_int_get(&plugin_session->stopped))
3978 		return;
3979 	janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
3980 	if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
3981 			|| janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
3982 		return;
3983 	janus_ice_send_pli(handle);
3984 }
3985 
janus_plugin_send_remb(janus_plugin_session * plugin_session,uint32_t bitrate)3986 void janus_plugin_send_remb(janus_plugin_session *plugin_session, uint32_t bitrate) {
3987 	if((plugin_session < (janus_plugin_session *)0x1000) || g_atomic_int_get(&plugin_session->stopped))
3988 		return;
3989 	janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
3990 	if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
3991 			|| janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
3992 		return;
3993 	janus_ice_send_remb(handle, bitrate);
3994 }
3995 
janus_plugin_close_pc_internal(gpointer user_data)3996 static gboolean janus_plugin_close_pc_internal(gpointer user_data) {
3997 	/* We actually enforce the close_pc here */
3998 	janus_plugin_session *plugin_session = (janus_plugin_session *) user_data;
3999 	janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
4000 	if(!ice_handle || !g_atomic_int_compare_and_exchange(&ice_handle->closepc, 1, 0)) {
4001 		janus_refcount_decrease(&plugin_session->ref);
4002 		return G_SOURCE_REMOVE;
4003 	}
4004 	janus_refcount_increase(&ice_handle->ref);
4005 	if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
4006 			|| janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)) {
4007 		janus_refcount_decrease(&plugin_session->ref);
4008 		janus_refcount_decrease(&ice_handle->ref);
4009 		return G_SOURCE_REMOVE;
4010 	}
4011 	JANUS_LOG(LOG_VERB, "[%"SCNu64"] Plugin asked to hangup PeerConnection: sending alert\n", ice_handle->handle_id);
4012 	/* Send an alert on all the DTLS connections */
4013 	janus_ice_webrtc_hangup(ice_handle, "Close PC");
4014 	janus_refcount_decrease(&plugin_session->ref);
4015 	janus_refcount_decrease(&ice_handle->ref);
4016 
4017 	return G_SOURCE_REMOVE;
4018 }
4019 
janus_plugin_close_pc(janus_plugin_session * plugin_session)4020 void janus_plugin_close_pc(janus_plugin_session *plugin_session) {
4021 	/* A plugin asked to get rid of a PeerConnection: enqueue it as a timed source */
4022 	if(!janus_plugin_session_is_alive(plugin_session))
4023 		return;
4024 	janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
4025 	if(!ice_handle || !g_atomic_int_compare_and_exchange(&ice_handle->closepc, 0, 1))
4026 		return;
4027 	janus_refcount_increase(&plugin_session->ref);
4028 	GSource *timeout_source = g_timeout_source_new_seconds(0);
4029 	g_source_set_callback(timeout_source, janus_plugin_close_pc_internal, plugin_session, NULL);
4030 	g_source_attach(timeout_source, sessions_watchdog_context);
4031 	g_source_unref(timeout_source);
4032 }
4033 
janus_plugin_end_session_internal(gpointer user_data)4034 static gboolean janus_plugin_end_session_internal(gpointer user_data) {
4035 	/* We actually enforce the end_session here */
4036 	janus_plugin_session *plugin_session = (janus_plugin_session *) user_data;
4037 	janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
4038 	if(!ice_handle) {
4039 		janus_refcount_decrease(&plugin_session->ref);
4040 		return G_SOURCE_REMOVE;
4041 	}
4042 	janus_refcount_increase(&ice_handle->ref);
4043 	if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)) {
4044 		janus_refcount_decrease(&plugin_session->ref);
4045 		janus_refcount_decrease(&ice_handle->ref);
4046 		return G_SOURCE_REMOVE;
4047 	}
4048 	janus_session *session = (janus_session *)ice_handle->session;
4049 	if(!session) {
4050 		janus_refcount_decrease(&plugin_session->ref);
4051 		janus_refcount_decrease(&ice_handle->ref);
4052 		return G_SOURCE_REMOVE;
4053 	}
4054 	/* Destroy the handle */
4055 	janus_session_handles_remove(session, ice_handle);
4056 
4057 	janus_refcount_decrease(&plugin_session->ref);
4058 	janus_refcount_decrease(&ice_handle->ref);
4059 	return G_SOURCE_REMOVE;
4060 }
4061 
janus_plugin_end_session(janus_plugin_session * plugin_session)4062 void janus_plugin_end_session(janus_plugin_session *plugin_session) {
4063 	/* A plugin asked to get rid of a handle: enqueue it as a timed source */
4064 	if(!janus_plugin_session_is_alive(plugin_session))
4065 		return;
4066 	janus_refcount_increase(&plugin_session->ref);
4067 	GSource *timeout_source = g_timeout_source_new_seconds(0);
4068 	g_source_set_callback(timeout_source, janus_plugin_end_session_internal, plugin_session, NULL);
4069 	g_source_attach(timeout_source, sessions_watchdog_context);
4070 	g_source_unref(timeout_source);
4071 }
4072 
janus_plugin_notify_event(janus_plugin * plugin,janus_plugin_session * plugin_session,json_t * event)4073 void janus_plugin_notify_event(janus_plugin *plugin, janus_plugin_session *plugin_session, json_t *event) {
4074 	/* A plugin asked to notify an event to the handlers */
4075 	if(!plugin || !event || !json_is_object(event))
4076 		return;
4077 	guint64 session_id = 0, handle_id = 0;
4078 	char *opaque_id = NULL;
4079 	if(plugin_session != NULL) {
4080 		if(!janus_plugin_session_is_alive(plugin_session)) {
4081 			json_decref(event);
4082 			return;
4083 		}
4084 		janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
4085 		if(!ice_handle) {
4086 			json_decref(event);
4087 			return;
4088 		}
4089 		handle_id = ice_handle->handle_id;
4090 		opaque_id = ice_handle->opaque_id;
4091 		janus_session *session = (janus_session *)ice_handle->session;
4092 		if(!session) {
4093 			json_decref(event);
4094 			return;
4095 		}
4096 		session_id = session->session_id;
4097 	}
4098 	/* Notify event handlers */
4099 	if(janus_events_is_enabled()) {
4100 		janus_events_notify_handlers(JANUS_EVENT_TYPE_PLUGIN, JANUS_EVENT_SUBTYPE_NONE,
4101 			session_id, handle_id, opaque_id, plugin->get_package(), event);
4102 	} else {
4103 		json_decref(event);
4104 	}
4105 }
4106 
janus_plugin_auth_is_signed()4107 gboolean janus_plugin_auth_is_signed() {
4108 	return janus_auth_is_signed_mode();
4109 }
4110 
janus_plugin_auth_is_signature_valid(janus_plugin * plugin,const char * token)4111 gboolean janus_plugin_auth_is_signature_valid(janus_plugin *plugin, const char *token) {
4112 	return janus_auth_check_signature(token, plugin->get_package());
4113 }
4114 
janus_plugin_auth_signature_contains(janus_plugin * plugin,const char * token,const char * descriptor)4115 gboolean janus_plugin_auth_signature_contains(janus_plugin *plugin, const char *token, const char *descriptor) {
4116 	return janus_auth_check_signature_contains(token, plugin->get_package(), descriptor);
4117 }
4118 
4119 
4120 /* Main */
main(int argc,char * argv[])4121 gint main(int argc, char *argv[])
4122 {
4123 	/* Core dumps may be disallowed by parent of this process; change that */
4124 	struct rlimit core_limits;
4125 	core_limits.rlim_cur = core_limits.rlim_max = RLIM_INFINITY;
4126 	setrlimit(RLIMIT_CORE, &core_limits);
4127 
4128 	g_print("Janus commit: %s\n", janus_build_git_sha);
4129 	g_print("Compiled on:  %s\n\n", janus_build_git_time);
4130 
4131 	struct gengetopt_args_info args_info;
4132 	/* Let's call our cmdline parser */
4133 	if(cmdline_parser(argc, argv, &args_info) != 0)
4134 		exit(1);
4135 
4136 	/* Any configuration to open? */
4137 	if(args_info.config_given) {
4138 		config_file = g_strdup(args_info.config_arg);
4139 	}
4140 	if(args_info.configs_folder_given) {
4141 		configs_folder = g_strdup(args_info.configs_folder_arg);
4142 	} else {
4143 		configs_folder = g_strdup(CONFDIR);
4144 	}
4145 	if(config_file == NULL) {
4146 		char file[255];
4147 		g_snprintf(file, 255, "%s/janus.jcfg", configs_folder);
4148 		config_file = g_strdup(file);
4149 	}
4150 	if((config = janus_config_parse(config_file)) == NULL) {
4151 		/* We failed to load the libconfig configuration file, let's try the INI */
4152 		g_print("Failed to load %s, trying the INI instead...\n", config_file);
4153 		g_free(config_file);
4154 		char file[255];
4155 		g_snprintf(file, 255, "%s/janus.cfg", configs_folder);
4156 		config_file = g_strdup(file);
4157 		if((config = janus_config_parse(config_file)) == NULL) {
4158 			if(args_info.config_given) {
4159 				/* We only give up if the configuration file was explicitly provided */
4160 				g_print("Error reading configuration from %s\n", config_file);
4161 				exit(1);
4162 			}
4163 			g_print("Error reading/parsing the configuration file in %s, going on with the defaults and the command line arguments\n",
4164 				configs_folder);
4165 			config = janus_config_create("janus.cfg");
4166 			if(config == NULL) {
4167 				/* If we can't even create an empty configuration, something's definitely wrong */
4168 				exit(1);
4169 			}
4170 		}
4171 	}
4172 	/* Pre-fetch some categories (creates them if they don't exist) */
4173 	janus_config_category *config_general = janus_config_get_create(config, NULL, janus_config_type_category, "general");
4174 	janus_config_category *config_certs = janus_config_get_create(config, NULL, janus_config_type_category, "certificates");
4175 	janus_config_category *config_nat = janus_config_get_create(config, NULL, janus_config_type_category, "nat");
4176 	janus_config_category *config_media = janus_config_get_create(config, NULL, janus_config_type_category, "media");
4177 	janus_config_category *config_transports = janus_config_get_create(config, NULL, janus_config_type_category, "transports");
4178 	janus_config_category *config_plugins = janus_config_get_create(config, NULL, janus_config_type_category, "plugins");
4179 	janus_config_category *config_events = janus_config_get_create(config, NULL, janus_config_type_category, "events");
4180 	janus_config_category *config_loggers = janus_config_get_create(config, NULL, janus_config_type_category, "loggers");
4181 
4182 	/* Any log prefix? */
4183 	janus_config_array *lp = janus_config_get(config, config_general, janus_config_type_item, "log_prefix");
4184 	if(lp && lp->value)
4185 		janus_log_global_prefix = g_strdup(lp->value);
4186 
4187 	/* Check if there are folders to protect */
4188 	janus_config_array *pfs = janus_config_get(config, config_general, janus_config_type_array, "protected_folders");
4189 	if(pfs && pfs->list) {
4190 		GList *item = pfs->list;
4191 		while(item) {
4192 			janus_config_item *pf = (janus_config_item *)item->data;
4193 			if(pf && pf->type == janus_config_type_item && pf->name == NULL && pf->value != NULL)
4194 				janus_protected_folder_add(pf->value);
4195 			item = item->next;
4196 		}
4197 	}
4198 
4199 	/* Check if we need to log to console and/or file */
4200 	gboolean use_stdout = TRUE;
4201 	if(args_info.disable_stdout_given) {
4202 		use_stdout = FALSE;
4203 		janus_config_add(config, config_general, janus_config_item_create("log_to_stdout", "no"));
4204 	} else if(!args_info.log_stdout_given) {
4205 		/* Check if the configuration file is saying anything about this */
4206 		janus_config_item *item = janus_config_get(config, config_general, janus_config_type_item, "log_to_stdout");
4207 		if(item && item->value && !janus_is_true(item->value))
4208 			use_stdout = FALSE;
4209 	}
4210 	const char *logfile = NULL;
4211 	if(args_info.log_file_given) {
4212 		logfile = args_info.log_file_arg;
4213 		janus_config_add(config, config_general, janus_config_item_create("log_to_file", "no"));
4214 	} else {
4215 		/* Check if the configuration file is saying anything about this */
4216 		janus_config_item *item = janus_config_get(config, config_general, janus_config_type_item, "log_to_file");
4217 		if(item && item->value)
4218 			logfile = item->value;
4219 	}
4220 
4221 	/* Check if we're going to daemonize Janus */
4222 	if(args_info.daemon_given) {
4223 		daemonize = TRUE;
4224 		janus_config_add(config, config_general, janus_config_item_create("daemonize", "yes"));
4225 	} else {
4226 		/* Check if the configuration file is saying anything about this */
4227 		janus_config_item *item = janus_config_get(config, config_general, janus_config_type_item, "daemonize");
4228 		if(item && item->value && janus_is_true(item->value))
4229 			daemonize = TRUE;
4230 	}
4231 	/* If we're going to daemonize, make sure logging to stdout is disabled and a log file has been specified */
4232 	if(daemonize && use_stdout && !args_info.log_stdout_given) {
4233 		use_stdout = FALSE;
4234 	}
4235 	/* Daemonize now, if we need to */
4236 	if(daemonize) {
4237 		g_print("Running Janus as a daemon\n");
4238 
4239 		/* Create a pipe for parent<->child communication during the startup phase */
4240 		if(pipe(pipefd) == -1) {
4241 			g_print("pipe error!\n");
4242 			exit(1);
4243 		}
4244 
4245 		/* Fork off the parent process */
4246 		pid_t pid = fork();
4247 		if(pid < 0) {
4248 			g_print("Fork error!\n");
4249 			exit(1);
4250 		}
4251 		if(pid > 0) {
4252 			/* Ok, we're the parent: let's wait for the child to tell us everything started fine */
4253 			close(pipefd[1]);
4254 			int code = -1;
4255 			struct pollfd pollfds;
4256 
4257 			while(code < 0) {
4258 				pollfds.fd = pipefd[0];
4259 				pollfds.events = POLLIN;
4260 				int res = poll(&pollfds, 1, -1);
4261 				if(res < 0)
4262 					break;
4263 				if(res == 0)
4264 					continue;
4265 				if(pollfds.revents & POLLERR || pollfds.revents & POLLHUP)
4266 					break;
4267 				if(pollfds.revents & POLLIN) {
4268 					res = read(pipefd[0], &code, sizeof(int));
4269 					break;
4270 				}
4271 			}
4272 			if(code < 0)
4273 				code = 1;
4274 
4275 			/* Leave the parent and return the exit code we received from the child */
4276 			if(code)
4277 				g_print("Error launching Janus (error code %d), check the logs for more details\n", code);
4278 			exit(code);
4279 		}
4280 		/* Child here */
4281 		close(pipefd[0]);
4282 
4283 		/* Change the file mode mask */
4284 		umask(0);
4285 
4286 		/* Create a new SID for the child process */
4287 		pid_t sid = setsid();
4288 		if(sid < 0) {
4289 			g_print("Error setting SID!\n");
4290 			exit(1);
4291 		}
4292 		/* Change the current working directory */
4293 		const char *cwd = (args_info.cwd_path_given) ? args_info.cwd_path_arg : "/";
4294 		if((chdir(cwd)) < 0) {
4295 			g_print("Error changing the current working directory!\n");
4296 			exit(1);
4297 		}
4298 		/* We close stdin/stdout/stderr when initializing the logger */
4299 	}
4300 
4301 	/* Was a custom instance name provided? */
4302 	if(args_info.server_name_given) {
4303 		janus_config_add(config, config_general, janus_config_item_create("server_name", args_info.server_name_arg));
4304 	}
4305 	janus_config_item *item = janus_config_get(config, config_general, janus_config_type_item, "server_name");
4306 	if(item && item->value) {
4307 		server_name = g_strdup(item->value);
4308 	}
4309 
4310 	/* Check if we should exit immediately on dlopen or dlsym errors */
4311 	item = janus_config_get(config, config_general, janus_config_type_item, "exit_on_dl_error");
4312 	if(item && item->value && janus_is_true(item->value))
4313 		exit_on_dl_error = TRUE;
4314 
4315 	/* Initialize logger */
4316 	if(janus_log_init(daemonize, use_stdout, logfile) < 0)
4317 		exit(1);
4318 	/* Check if there are external loggers we need to load as well */
4319 	const char *path = NULL;
4320 	DIR *dir = NULL;
4321 	/* External loggers are usually disabled by default: they need to be enabled in the configuration */
4322 	gchar **disabled_loggers = NULL;
4323 	path = LOGGERDIR;
4324 	item = janus_config_get(config, config_general, janus_config_type_item, "loggers_folder");
4325 	if(item && item->value)
4326 		path = (char *)item->value;
4327 	JANUS_LOG(LOG_INFO, "Logger plugins folder: %s\n", path);
4328 	dir = opendir(path);
4329 	if(!dir) {
4330 		/* Not really fatal, we don't care and go on anyway: loggers are not fundamental */
4331 		JANUS_LOG(LOG_WARN, "\tCouldn't access logger plugins folder...\n");
4332 	} else {
4333 		/* Any loggers to ignore? */
4334 		item = janus_config_get(config, config_loggers, janus_config_type_item, "disable");
4335 		if(item && item->value)
4336 			disabled_loggers = g_strsplit(item->value, ",", -1);
4337 		/* Open the shared objects */
4338 		struct dirent *eventent = NULL;
4339 		char eventpath[1024];
4340 		while((eventent = readdir(dir))) {
4341 			int len = strlen(eventent->d_name);
4342 			if (len < 4) {
4343 				continue;
4344 			}
4345 			if (strcasecmp(eventent->d_name+len-strlen(SHLIB_EXT), SHLIB_EXT)) {
4346 				continue;
4347 			}
4348 			/* Check if this logger has been disabled in the configuration file */
4349 			if(disabled_loggers != NULL) {
4350 				gchar *index = disabled_loggers[0];
4351 				if(index != NULL) {
4352 					int i=0;
4353 					gboolean skip = FALSE;
4354 					while(index != NULL) {
4355 						while(isspace(*index))
4356 							index++;
4357 						if(strlen(index) && !strcmp(index, eventent->d_name)) {
4358 							JANUS_LOG(LOG_WARN, "Logger plugin '%s' has been disabled, skipping...\n", eventent->d_name);
4359 							skip = TRUE;
4360 							break;
4361 						}
4362 						i++;
4363 						index = disabled_loggers[i];
4364 					}
4365 					if(skip)
4366 						continue;
4367 				}
4368 			}
4369 			JANUS_LOG(LOG_INFO, "Loading logger plugin '%s'...\n", eventent->d_name);
4370 			memset(eventpath, 0, 1024);
4371 			g_snprintf(eventpath, 1024, "%s/%s", path, eventent->d_name);
4372 			void *event = dlopen(eventpath, RTLD_NOW | RTLD_GLOBAL);
4373 			if (!event) {
4374 				JANUS_LOG(exit_on_dl_error ? LOG_FATAL : LOG_ERR, "\tCouldn't load logger plugin '%s': %s\n", eventent->d_name, dlerror());
4375 				if (exit_on_dl_error)
4376 					exit(1);
4377 			} else {
4378 				dlerror();
4379 				create_l *create = (create_l*) dlsym(event, "create");
4380 				const char *dlsym_error = dlerror();
4381 				if (dlsym_error) {
4382 					JANUS_LOG(exit_on_dl_error ? LOG_FATAL : LOG_ERR, "\tCouldn't load symbol 'create': %s\n", dlsym_error);
4383 					if (exit_on_dl_error)
4384 						exit(1);
4385 					continue;
4386 				}
4387 				janus_logger *janus_logger = create();
4388 				if(!janus_logger) {
4389 					JANUS_LOG(LOG_ERR, "\tCouldn't use function 'create'...\n");
4390 					continue;
4391 				}
4392 				/* Are all the mandatory methods and callbacks implemented? */
4393 				if(!janus_logger->init || !janus_logger->destroy ||
4394 						!janus_logger->get_api_compatibility ||
4395 						!janus_logger->get_version ||
4396 						!janus_logger->get_version_string ||
4397 						!janus_logger->get_description ||
4398 						!janus_logger->get_package ||
4399 						!janus_logger->get_name ||
4400 						!janus_logger->incoming_logline) {
4401 					JANUS_LOG(LOG_ERR, "\tMissing some mandatory methods/callbacks, skipping this logger plugin...\n");
4402 					continue;
4403 				}
4404 				if(janus_logger->get_api_compatibility() < JANUS_LOGGER_API_VERSION) {
4405 					JANUS_LOG(LOG_ERR, "The '%s' logger plugin was compiled against an older version of the API (%d < %d), skipping it: update it to enable it again\n",
4406 						janus_logger->get_package(), janus_logger->get_api_compatibility(), JANUS_LOGGER_API_VERSION);
4407 					continue;
4408 				}
4409 				janus_logger->init(server_name ? server_name : JANUS_SERVER_NAME, configs_folder);
4410 				JANUS_LOG(LOG_VERB, "\tVersion: %d (%s)\n", janus_logger->get_version(), janus_logger->get_version_string());
4411 				JANUS_LOG(LOG_VERB, "\t   [%s] %s\n", janus_logger->get_package(), janus_logger->get_name());
4412 				JANUS_LOG(LOG_VERB, "\t   %s\n", janus_logger->get_description());
4413 				JANUS_LOG(LOG_VERB, "\t   Plugin API version: %d\n", janus_logger->get_api_compatibility());
4414 				if(loggers == NULL)
4415 					loggers = g_hash_table_new(g_str_hash, g_str_equal);
4416 				g_hash_table_insert(loggers, (gpointer)janus_logger->get_package(), janus_logger);
4417 				if(loggers_so == NULL)
4418 					loggers_so = g_hash_table_new(g_str_hash, g_str_equal);
4419 				g_hash_table_insert(loggers_so, (gpointer)janus_logger->get_package(), event);
4420 			}
4421 		}
4422 		closedir(dir);
4423 	}
4424 	if(disabled_loggers != NULL)
4425 		g_strfreev(disabled_loggers);
4426 	disabled_loggers = NULL;
4427 	janus_log_set_loggers(loggers);
4428 
4429 	JANUS_PRINT("---------------------------------------------------\n");
4430 	JANUS_PRINT("  Starting Meetecho Janus (WebRTC Server) v%s\n", janus_version_string);
4431 	JANUS_PRINT("---------------------------------------------------\n\n");
4432 
4433 	/* Handle SIGINT (CTRL-C), SIGTERM (from service managers) */
4434 	signal(SIGINT, janus_handle_signal);
4435 	signal(SIGTERM, janus_handle_signal);
4436 	atexit(janus_termination_handler);
4437 
4438 	/* Setup Glib */
4439 #if !GLIB_CHECK_VERSION(2, 36, 0)
4440 	g_type_init();
4441 #endif
4442 
4443 	/* Logging level: default is info and no timestamps */
4444 	janus_log_level = LOG_INFO;
4445 	janus_log_timestamps = FALSE;
4446 	janus_log_colors = TRUE;
4447 	if(args_info.debug_level_given) {
4448 		if(args_info.debug_level_arg < LOG_NONE)
4449 			args_info.debug_level_arg = 0;
4450 		else if(args_info.debug_level_arg > LOG_MAX)
4451 			args_info.debug_level_arg = LOG_MAX;
4452 		janus_log_level = args_info.debug_level_arg;
4453 	}
4454 
4455 	/* Any PID we need to create? */
4456 	const char *pidfile = NULL;
4457 	if(args_info.pid_file_given) {
4458 		pidfile = args_info.pid_file_arg;
4459 		janus_config_add(config, config_general, janus_config_item_create("pid_file", pidfile));
4460 	} else {
4461 		/* Check if the configuration file is saying anything about this */
4462 		item = janus_config_get(config, config_general, janus_config_type_item, "pid_file");
4463 		if(item && item->value)
4464 			pidfile = item->value;
4465 	}
4466 	if(janus_pidfile_create(pidfile) < 0)
4467 		exit(1);
4468 
4469 	/* Proceed with the rest of the configuration */
4470 	janus_config_print(config);
4471 	if(args_info.debug_level_given) {
4472 		char debug[5];
4473 		g_snprintf(debug, 5, "%d", args_info.debug_level_arg);
4474 		janus_config_add(config, config_general, janus_config_item_create("debug_level", debug));
4475 	} else {
4476 		/* No command line directive on logging, try the configuration file */
4477 		item = janus_config_get(config, config_general, janus_config_type_item, "debug_level");
4478 		if(item && item->value) {
4479 			int temp_level = atoi(item->value);
4480 			if(temp_level == 0 && strcmp(item->value, "0")) {
4481 				JANUS_PRINT("Invalid debug level %s (configuration), using default (info=4)\n", item->value);
4482 			} else {
4483 				janus_log_level = temp_level;
4484 				if(janus_log_level < LOG_NONE)
4485 					janus_log_level = 0;
4486 				else if(janus_log_level > LOG_MAX)
4487 					janus_log_level = LOG_MAX;
4488 			}
4489 		}
4490 	}
4491 	/* Any command line argument that should overwrite the configuration? */
4492 	JANUS_PRINT("Checking command line arguments...\n");
4493 	if(args_info.debug_timestamps_given) {
4494 		janus_config_add(config, config_general, janus_config_item_create("debug_timestamps", "yes"));
4495 	}
4496 	if(args_info.disable_colors_given) {
4497 		janus_config_add(config, config_general, janus_config_item_create("debug_colors", "no"));
4498 	}
4499 	if(args_info.debug_locks_given) {
4500 		janus_config_add(config, config_general, janus_config_item_create("debug_locks", "yes"));
4501 	}
4502 	if(args_info.session_timeout_given) {
4503 		char st[20];
4504 		g_snprintf(st, 20, "%d", args_info.session_timeout_arg);
4505 		janus_config_add(config, config_general, janus_config_item_create("session_timeout", st));
4506 	}
4507 	if(args_info.reclaim_session_timeout_given) {
4508 		char st[20];
4509 		g_snprintf(st, 20, "%d", args_info.reclaim_session_timeout_arg);
4510 		janus_config_add(config, config_general, janus_config_item_create("reclaim_session_timeout", st));
4511 	}
4512  	if(args_info.interface_given) {
4513 		janus_config_add(config, config_general, janus_config_item_create("interface", args_info.interface_arg));
4514 	}
4515 	if(args_info.configs_folder_given) {
4516 		janus_config_add(config, config_general, janus_config_item_create("configs_folder", args_info.configs_folder_arg));
4517 	}
4518 	if(args_info.plugins_folder_given) {
4519 		janus_config_add(config, config_general, janus_config_item_create("plugins_folder", args_info.plugins_folder_arg));
4520 	}
4521 	if(args_info.apisecret_given) {
4522 		janus_config_add(config, config_general, janus_config_item_create("api_secret", args_info.apisecret_arg));
4523 	}
4524 	if(args_info.token_auth_given) {
4525 		janus_config_add(config, config_general, janus_config_item_create("token_auth", "yes"));
4526 	}
4527 	if(args_info.token_auth_secret_given) {
4528 		janus_config_add(config, config_general, janus_config_item_create("token_auth_secret", args_info.token_auth_secret_arg));
4529 	}
4530 	if(args_info.no_webrtc_encryption_given) {
4531 		janus_config_add(config, config_general, janus_config_item_create("no_webrtc_encryption", "yes"));
4532 	}
4533 	if(args_info.cert_pem_given) {
4534 		janus_config_add(config, config_certs, janus_config_item_create("cert_pem", args_info.cert_pem_arg));
4535 	}
4536 	if(args_info.cert_key_given) {
4537 		janus_config_add(config, config_certs, janus_config_item_create("cert_key", args_info.cert_key_arg));
4538 	}
4539 	if(args_info.cert_pwd_given) {
4540 		janus_config_add(config, config_certs, janus_config_item_create("cert_pwd", args_info.cert_pwd_arg));
4541 	}
4542 	if(args_info.stun_server_given) {
4543 		/* Split in server and port (if port missing, use 3478 as default) */
4544 		char *stunport = strrchr(args_info.stun_server_arg, ':');
4545 		if(stunport != NULL) {
4546 			*stunport = '\0';
4547 			stunport++;
4548 			janus_config_add(config, config_nat, janus_config_item_create("stun_server", args_info.stun_server_arg));
4549 			janus_config_add(config, config_nat, janus_config_item_create("stun_port", stunport));
4550 		} else {
4551 			janus_config_add(config, config_nat, janus_config_item_create("stun_server", args_info.stun_server_arg));
4552 			janus_config_add(config, config_nat, janus_config_item_create("stun_port", "3478"));
4553 		}
4554 	}
4555 	if(args_info.nat_1_1_given) {
4556 		janus_config_add(config, config_nat, janus_config_item_create("nat_1_1_mapping", args_info.nat_1_1_arg));
4557 	}
4558 	if(args_info.keep_private_host_given) {
4559 		janus_config_add(config, config_nat, janus_config_item_create("keep_private_host", "true"));
4560 	}
4561 	if(args_info.ice_enforce_list_given) {
4562 		janus_config_add(config, config_nat, janus_config_item_create("ice_enforce_list", args_info.ice_enforce_list_arg));
4563 	}
4564 	if(args_info.ice_ignore_list_given) {
4565 		janus_config_add(config, config_nat, janus_config_item_create("ice_ignore_list", args_info.ice_ignore_list_arg));
4566 	}
4567 	if(args_info.libnice_debug_given) {
4568 		janus_config_add(config, config_nat, janus_config_item_create("nice_debug", "true"));
4569 	}
4570 	if(args_info.full_trickle_given) {
4571 		janus_config_add(config, config_nat, janus_config_item_create("full_trickle", "true"));
4572 	}
4573 	if(args_info.ice_lite_given) {
4574 		janus_config_add(config, config_nat, janus_config_item_create("ice_lite", "true"));
4575 	}
4576 	if(args_info.ice_tcp_given) {
4577 		janus_config_add(config, config_nat, janus_config_item_create("ice_tcp", "true"));
4578 	}
4579 	if(args_info.ipv6_candidates_given) {
4580 		janus_config_add(config, config_media, janus_config_item_create("ipv6", "true"));
4581 	}
4582 	if(args_info.ipv6_link_local_given) {
4583 		janus_config_add(config, config_media, janus_config_item_create("ipv6_linklocal", "true"));
4584 	}
4585 	if(args_info.min_nack_queue_given) {
4586 		char mnq[20];
4587 		g_snprintf(mnq, 20, "%d", args_info.min_nack_queue_arg);
4588 		janus_config_add(config, config_media, janus_config_item_create("min_nack_queue", mnq));
4589 	}
4590 	if(args_info.no_media_timer_given) {
4591 		char nmt[20];
4592 		g_snprintf(nmt, 20, "%d", args_info.no_media_timer_arg);
4593 		janus_config_add(config, config_media, janus_config_item_create("no_media_timer", nmt));
4594 	}
4595 	if(args_info.slowlink_threshold_given) {
4596 		char st[20];
4597 		g_snprintf(st, 20, "%d", args_info.slowlink_threshold_arg);
4598 		janus_config_add(config, config_media, janus_config_item_create("slowlink_threshold", st));
4599 	}
4600 	if(args_info.twcc_period_given) {
4601 		char tp[20];
4602 		g_snprintf(tp, 20, "%d", args_info.twcc_period_arg);
4603 		janus_config_add(config, config_media, janus_config_item_create("twcc_period", tp));
4604 	}
4605 	if(args_info.rtp_port_range_given) {
4606 		janus_config_add(config, config_media, janus_config_item_create("rtp_port_range", args_info.rtp_port_range_arg));
4607 	}
4608 	if(args_info.event_handlers_given) {
4609 		janus_config_add(config, config_events, janus_config_item_create("broadcast", "yes"));
4610 	}
4611 	janus_config_print(config);
4612 
4613 	/* Logging/debugging */
4614 	JANUS_PRINT("Debug/log level is %d\n", janus_log_level);
4615 	item = janus_config_get(config, config_general, janus_config_type_item, "debug_timestamps");
4616 	if(item && item->value)
4617 		janus_log_timestamps = janus_is_true(item->value);
4618 	JANUS_PRINT("Debug/log timestamps are %s\n", janus_log_timestamps ? "enabled" : "disabled");
4619 	item = janus_config_get(config, config_general, janus_config_type_item, "debug_colors");
4620 	if(item && item->value)
4621 		janus_log_colors = janus_is_true(item->value);
4622 	JANUS_PRINT("Debug/log colors are %s\n", janus_log_colors ? "enabled" : "disabled");
4623 	item = janus_config_get(config, config_general, janus_config_type_item, "debug_locks");
4624 	if(item && item->value)
4625 		lock_debug = janus_is_true(item->value);
4626 	if(lock_debug) {
4627 		JANUS_PRINT("Lock/mutex debugging is enabled\n");
4628 	}
4629 
4630 	/* First of all, let's check if we're disabling WebRTC encryption for debugging purposes */
4631 	item = janus_config_get(config, config_general, janus_config_type_item, "no_webrtc_encryption");
4632 	if(item && item->value && janus_is_true(item->value)) {
4633 		JANUS_LOG(LOG_WARN, "Disabling WebRTC encryption: *THIS IS ONLY ACCEPTABLE WHEN DEBUGGING!*\n");
4634 		webrtc_encryption = FALSE;
4635 	}
4636 
4637 	/* Any IP/interface to enforce/ignore? */
4638 	item = janus_config_get(config, config_nat, janus_config_type_item, "ice_enforce_list");
4639 	if(item && item->value) {
4640 		gchar **list = g_strsplit(item->value, ",", -1);
4641 		gchar *index = list[0];
4642 		if(index != NULL) {
4643 			int i=0;
4644 			while(index != NULL) {
4645 				if(strlen(index) > 0) {
4646 					JANUS_LOG(LOG_INFO, "Adding '%s' to the ICE enforce list...\n", index);
4647 					janus_ice_enforce_interface(g_strdup(index));
4648 				}
4649 				i++;
4650 				index = list[i];
4651 			}
4652 		}
4653 		g_clear_pointer(&list, g_strfreev);
4654 	}
4655 	item = janus_config_get(config, config_nat, janus_config_type_item, "ice_ignore_list");
4656 	if(item && item->value) {
4657 		gchar **list = g_strsplit(item->value, ",", -1);
4658 		gchar *index = list[0];
4659 		if(index != NULL) {
4660 			int i=0;
4661 			while(index != NULL) {
4662 				if(strlen(index) > 0) {
4663 					JANUS_LOG(LOG_INFO, "Adding '%s' to the ICE ignore list...\n", index);
4664 					janus_ice_ignore_interface(g_strdup(index));
4665 				}
4666 				i++;
4667 				index = list[i];
4668 			}
4669 		}
4670 		g_clear_pointer(&list, g_strfreev);
4671 	}
4672 	/* What is the local IP? */
4673 	JANUS_LOG(LOG_VERB, "Selecting local IP address...\n");
4674 	item = janus_config_get(config, config_general, janus_config_type_item, "interface");
4675 	if(item && item->value) {
4676 		JANUS_LOG(LOG_VERB, "  -- Will try to use %s\n", item->value);
4677 		/* Verify that the address is valid */
4678 		struct ifaddrs *ifas = NULL;
4679 		janus_network_address iface;
4680 		janus_network_address_string_buffer ibuf;
4681 		if(getifaddrs(&ifas) == -1) {
4682 			JANUS_LOG(LOG_ERR, "Unable to acquire list of network devices/interfaces; some configurations may not work as expected... %d (%s)\n",
4683 				errno, g_strerror(errno));
4684 		} else {
4685 			if(janus_network_lookup_interface(ifas, item->value, &iface) != 0) {
4686 				JANUS_LOG(LOG_WARN, "Error setting local IP address to %s, falling back to detecting IP address...\n", item->value);
4687 			} else {
4688 				if(janus_network_address_to_string_buffer(&iface, &ibuf) != 0 || janus_network_address_string_buffer_is_null(&ibuf)) {
4689 					JANUS_LOG(LOG_WARN, "Error getting local IP address from %s, falling back to detecting IP address...\n", item->value);
4690 				} else {
4691 					local_ip = g_strdup(janus_network_address_string_from_buffer(&ibuf));
4692 				}
4693 			}
4694 			freeifaddrs(ifas);
4695 		}
4696 	}
4697 	if(local_ip == NULL) {
4698 		local_ip = janus_network_detect_local_ip_as_string(janus_network_query_options_any_ip);
4699 		if(local_ip == NULL) {
4700 			JANUS_LOG(LOG_WARN, "Couldn't find any address! using 127.0.0.1 as the local IP... (which is NOT going to work out of your machine)\n");
4701 			local_ip = g_strdup("127.0.0.1");
4702 		}
4703 	}
4704 	JANUS_LOG(LOG_INFO, "Using %s as local IP...\n", local_ip);
4705 
4706 	/* Check if a custom session timeout value was specified */
4707 	item = janus_config_get(config, config_general, janus_config_type_item, "session_timeout");
4708 	if(item && item->value) {
4709 		int st = atoi(item->value);
4710 		if(st < 0) {
4711 			JANUS_LOG(LOG_WARN, "Ignoring session_timeout value as it's not a positive integer\n");
4712 		} else {
4713 			if(st == 0) {
4714 				JANUS_LOG(LOG_WARN, "Session timeouts have been disabled (note, may result in orphaned sessions)\n");
4715 			}
4716 			global_session_timeout = st;
4717 		}
4718 	}
4719 
4720 	/* Check if a custom reclaim session timeout value was specified */
4721 	item = janus_config_get(config, config_general, janus_config_type_item, "reclaim_session_timeout");
4722 	if(item && item->value) {
4723 		int rst = atoi(item->value);
4724 		if(rst < 0) {
4725 			JANUS_LOG(LOG_WARN, "Ignoring reclaim_session_timeout value as it's not a positive integer\n");
4726 		} else {
4727 			if(rst == 0) {
4728 				JANUS_LOG(LOG_WARN, "Reclaim session timeouts have been disabled, will cleanup immediately\n");
4729 			}
4730 			reclaim_session_timeout = rst;
4731 		}
4732 	}
4733 
4734 	/* Check if a custom candidates timeout value was specified */
4735 	item = janus_config_get(config, config_general, janus_config_type_item, "candidates_timeout");
4736 	if(item && item->value) {
4737 		int ct = atoi(item->value);
4738 		if(ct <= 0) {
4739 			JANUS_LOG(LOG_WARN, "Ignoring candidates_timeout value as it's not a positive integer\n");
4740 		} else {
4741 			candidates_timeout = ct;
4742 		}
4743 	}
4744 
4745 	/* Is there any API secret to consider? */
4746 	api_secret = NULL;
4747 	item = janus_config_get(config, config_general, janus_config_type_item, "api_secret");
4748 	if(item && item->value) {
4749 		api_secret = g_strdup(item->value);
4750 	}
4751 	/* Is there any API secret to consider? */
4752 	admin_api_secret = NULL;
4753 	item = janus_config_get(config, config_general, janus_config_type_item, "admin_secret");
4754 	if(item && item->value) {
4755 		admin_api_secret = g_strdup(item->value);
4756 	}
4757 	/* Also check if the token based authentication mechanism needs to be enabled */
4758 	item = janus_config_get(config, config_general, janus_config_type_item, "token_auth");
4759 	gboolean auth_enabled = item && item->value && janus_is_true(item->value);
4760 	item = janus_config_get(config, config_general, janus_config_type_item, "token_auth_secret");
4761 	const char *auth_secret = NULL;
4762 	if (item && item->value)
4763 		auth_secret = item->value;
4764 	janus_auth_init(auth_enabled, auth_secret);
4765 
4766 	/* Check if opaque IDs should be sent back in the Janus API too */
4767 	item = janus_config_get(config, config_general, janus_config_type_item, "opaqueid_in_api");
4768 	if(item && item->value && janus_is_true(item->value))
4769 		janus_enable_opaqueid_in_api();
4770 
4771 	/* Initialize the recorder code */
4772 	item = janus_config_get(config, config_general, janus_config_type_item, "recordings_tmp_ext");
4773 	if(item && item->value) {
4774 		janus_recorder_init(TRUE, item->value);
4775 	} else {
4776 		janus_recorder_init(FALSE, NULL);
4777 	}
4778 
4779 	/* Check if we should hide dependencies in "info" requests */
4780 	item = janus_config_get(config, config_general, janus_config_type_item, "hide_dependencies");
4781 	if(item && item->value && janus_is_true(item->value))
4782 		hide_dependencies = TRUE;
4783 
4784 	/* Setup ICE stuff (e.g., checking if the provided STUN server is correct) */
4785 	char *stun_server = NULL, *turn_server = NULL;
4786 	uint16_t stun_port = 0, turn_port = 0;
4787 	char *turn_type = NULL, *turn_user = NULL, *turn_pwd = NULL;
4788 	char *turn_rest_api = NULL, *turn_rest_api_key = NULL;
4789 #ifdef HAVE_TURNRESTAPI
4790 	char *turn_rest_api_method = NULL;
4791 	uint turn_rest_api_timeout = 10;
4792 #endif
4793 	uint16_t rtp_min_port = 0, rtp_max_port = 0;
4794 	gboolean ice_lite = FALSE, ice_tcp = FALSE, full_trickle = FALSE, ipv6 = FALSE,
4795 		ipv6_linklocal = FALSE, ignore_mdns = FALSE, ignore_unreachable_ice_server = FALSE;
4796 	item = janus_config_get(config, config_media, janus_config_type_item, "ipv6");
4797 	ipv6 = (item && item->value) ? janus_is_true(item->value) : FALSE;
4798 	if(ipv6) {
4799 		item = janus_config_get(config, config_media, janus_config_type_item, "ipv6_linklocal");
4800 		ipv6_linklocal = (item && item->value) ? janus_is_true(item->value) : FALSE;
4801 	}
4802 	item = janus_config_get(config, config_media, janus_config_type_item, "rtp_port_range");
4803 	if(item && item->value) {
4804 		/* Split in min and max port */
4805 		char *maxport = strrchr(item->value, '-');
4806 		if(maxport != NULL) {
4807 			*maxport = '\0';
4808 			maxport++;
4809 			if(janus_string_to_uint16(item->value, &rtp_min_port) < 0)
4810 				JANUS_LOG(LOG_WARN, "Invalid RTP min port value: %s (assuming 0)\n", item->value);
4811 			if(janus_string_to_uint16(maxport, &rtp_max_port) < 0)
4812 				JANUS_LOG(LOG_WARN, "Invalid RTP max port value: %s (assuming 0)\n", maxport);
4813 			maxport--;
4814 			*maxport = '-';
4815 		}
4816 		if(rtp_min_port > rtp_max_port) {
4817 			uint16_t temp_port = rtp_min_port;
4818 			rtp_min_port = rtp_max_port;
4819 			rtp_max_port = temp_port;
4820 		}
4821 		if(rtp_max_port == 0)
4822 			rtp_max_port = 65535;
4823 		JANUS_LOG(LOG_INFO, "RTP port range: %u -- %u\n", rtp_min_port, rtp_max_port);
4824 	}
4825 	/* Check if we need to enable the ICE Lite mode */
4826 	item = janus_config_get(config, config_nat, janus_config_type_item, "ice_lite");
4827 	ice_lite = (item && item->value) ? janus_is_true(item->value) : FALSE;
4828 	/* Check if we need to enable ICE-TCP support (warning: still broken, for debugging only) */
4829 	item = janus_config_get(config, config_nat, janus_config_type_item, "ice_tcp");
4830 	ice_tcp = (item && item->value) ? janus_is_true(item->value) : FALSE;
4831 	/* Check if we need to do full-trickle instead of half-trickle */
4832 	item = janus_config_get(config, config_nat, janus_config_type_item, "full_trickle");
4833 	full_trickle = (item && item->value) ? janus_is_true(item->value) : FALSE;
4834 	/* Check if we should exit if a STUN or TURN server is unreachable */
4835 	item = janus_config_get(config, config_nat, janus_config_type_item, "ignore_unreachable_ice_server");
4836 	ignore_unreachable_ice_server = (item && item->value) ? janus_is_true(item->value) : FALSE;
4837 	/* Any STUN server to use in Janus? */
4838 	item = janus_config_get(config, config_nat, janus_config_type_item, "stun_server");
4839 	if(item && item->value)
4840 		stun_server = (char *)item->value;
4841 	item = janus_config_get(config, config_nat, janus_config_type_item, "stun_port");
4842 	if(item && item->value && janus_string_to_uint16(item->value, &stun_port) < 0) {
4843 		JANUS_LOG(LOG_WARN, "Invalid STUN port: %s (disabling STUN)\n", item->value);
4844 		stun_server = NULL;
4845 	}
4846 	/* Check if we should drop mDNS candidates */
4847 	item = janus_config_get(config, config_nat, janus_config_type_item, "ignore_mdns");
4848 	ignore_mdns = (item && item->value) ? janus_is_true(item->value) : FALSE;
4849 	/* Any 1:1 NAT mapping to take into account? */
4850 	item = janus_config_get(config, config_nat, janus_config_type_item, "nat_1_1_mapping");
4851 	if(item && item->value) {
4852 		JANUS_LOG(LOG_INFO, "Using nat_1_1_mapping for public IP: %s\n", item->value);
4853 		char **list = g_strsplit(item->value, ",", -1);
4854 		char *index = list[0];
4855 		if(index != NULL) {
4856 			int i=0;
4857 			while(index != NULL) {
4858 				if(strlen(index) > 0) {
4859 					if(!janus_network_string_is_valid_address(janus_network_query_options_any_ip, index)) {
4860 						JANUS_LOG(LOG_WARN, "Invalid nat_1_1_mapping address %s, skipping...\n", index);
4861 					} else {
4862 						janus_add_public_ip(index);
4863 					}
4864 				}
4865 				i++;
4866 				index = list[i];
4867 			}
4868 		}
4869 		g_strfreev(list);
4870 		if(janus_get_public_ip_count() > 0) {
4871 			/* Check if we should replace the private host, or advertise both candidates */
4872 			gboolean keep_private_host = FALSE;
4873 			item = janus_config_get(config, config_nat, janus_config_type_item, "keep_private_host");
4874 			if(item && item->value && janus_is_true(item->value)) {
4875 				JANUS_LOG(LOG_INFO, "  -- Going to keep the private host too (separate candidates)\n");
4876 				keep_private_host = TRUE;
4877 			}
4878 			janus_ice_enable_nat_1_1(keep_private_host);
4879 		}
4880 	}
4881 	/* Any TURN server to use in Janus? */
4882 	item = janus_config_get(config, config_nat, janus_config_type_item, "turn_server");
4883 	if(item && item->value)
4884 		turn_server = (char *)item->value;
4885 	item = janus_config_get(config, config_nat, janus_config_type_item, "turn_port");
4886 	if(item && item->value && janus_string_to_uint16(item->value, &turn_port) < 0) {
4887 		JANUS_LOG(LOG_WARN, "Invalid TURN port: %s (disabling TURN)\n", item->value);
4888 		turn_server = NULL;
4889 	}
4890 	item = janus_config_get(config, config_nat, janus_config_type_item, "turn_type");
4891 	if(item && item->value)
4892 		turn_type = (char *)item->value;
4893 	item = janus_config_get(config, config_nat, janus_config_type_item, "turn_user");
4894 	if(item && item->value)
4895 		turn_user = (char *)item->value;
4896 	item = janus_config_get(config, config_nat, janus_config_type_item, "turn_pwd");
4897 	if(item && item->value)
4898 		turn_pwd = (char *)item->value;
4899 	/* Check if there's any TURN REST API backend to use */
4900 	item = janus_config_get(config, config_nat, janus_config_type_item, "turn_rest_api");
4901 	if(item && item->value)
4902 		turn_rest_api = (char *)item->value;
4903 	item = janus_config_get(config, config_nat, janus_config_type_item, "turn_rest_api_key");
4904 	if(item && item->value)
4905 		turn_rest_api_key = (char *)item->value;
4906 #ifdef HAVE_TURNRESTAPI
4907 	item = janus_config_get(config, config_nat, janus_config_type_item, "turn_rest_api_method");
4908 	if(item && item->value)
4909 		turn_rest_api_method = (char *)item->value;
4910 	item = janus_config_get(config, config_nat, janus_config_type_item, "turn_rest_api_timeout");
4911 	if(item && item->value) {
4912 		int rst = atoi(item->value);
4913 		if(rst <= 0) { /* Don't allow user to set 0 seconds i.e., infinite wait */
4914 			JANUS_LOG(LOG_WARN, "Ignoring turn_rest_api_timeout as it's not a positive integer, leaving at default (10 seconds)\n");
4915 		} else {
4916 			turn_rest_api_timeout = rst;
4917 		}
4918 	}
4919 #endif
4920 	item = janus_config_get(config, config_nat, janus_config_type_item, "allow_force_relay");
4921 	if(item && item->value && janus_is_true(item->value)) {
4922 		JANUS_LOG(LOG_WARN, "Note: applications/users will be allowed to force Janus to use TURN. Make sure you know what you're doing!\n");
4923 		janus_ice_allow_force_relay();
4924 	}
4925 	/* Do we need a limited number of static event loops, or is it ok to have one per handle (the default)? */
4926 	item = janus_config_get(config, config_general, janus_config_type_item, "event_loops");
4927 	if(item && item->value) {
4928 		int loops = atoi(item->value);
4929 		/* Check if we should allow API calls to specify which loops to use for new handles */
4930 		gboolean loops_api = FALSE;
4931 		item = janus_config_get(config, config_general, janus_config_type_item, "allow_loop_indication");
4932 		if(item && item->value)
4933 			loops_api = janus_is_true(item->value);
4934 		janus_ice_set_static_event_loops(loops, loops_api);
4935 	}
4936 	/* Initialize the ICE stack now */
4937 	janus_ice_init(ice_lite, ice_tcp, full_trickle, ignore_mdns, ipv6, ipv6_linklocal, rtp_min_port, rtp_max_port);
4938 	if(janus_ice_set_stun_server(stun_server, stun_port) < 0) {
4939 		if(!ignore_unreachable_ice_server) {
4940 			JANUS_LOG(LOG_FATAL, "Invalid STUN address %s:%u\n", stun_server, stun_port);
4941 			exit(1);
4942 		} else {
4943 			JANUS_LOG(LOG_ERR, "Invalid STUN address %s:%u. STUN will be disabled\n", stun_server, stun_port);
4944 		}
4945 	}
4946 	item = janus_config_get(config, config_nat, janus_config_type_item, "ice_nomination");
4947 	if(item && item->value) {
4948 #ifndef HAVE_ICE_NOMINATION
4949 		JANUS_LOG(LOG_WARN, "This version of libnice doesn't support setting the ICE nomination mode, ignoring '%s'\n", item->value);
4950 #else
4951 		janus_ice_set_nomination_mode(item->value);
4952 #endif
4953 	}
4954 	item = janus_config_get(config, config_nat, janus_config_type_item, "ice_keepalive_conncheck");
4955 	if(item && item->value)
4956 		janus_ice_set_keepalive_conncheck_enabled(janus_is_true(item->value));
4957 	if(janus_ice_set_turn_server(turn_server, turn_port, turn_type, turn_user, turn_pwd) < 0) {
4958 		if(!ignore_unreachable_ice_server) {
4959 			JANUS_LOG(LOG_FATAL, "Invalid TURN address %s:%u\n", turn_server, turn_port);
4960 			exit(1);
4961 		} else {
4962 			JANUS_LOG(LOG_ERR, "Invalid TURN address %s:%u. TURN will be disabled\n", turn_server, turn_port);
4963 		}
4964 	}
4965 #ifndef HAVE_TURNRESTAPI
4966 	if(turn_rest_api != NULL || turn_rest_api_key != NULL) {
4967 		JANUS_LOG(LOG_WARN, "A TURN REST API backend specified in the settings, but libcurl support has not been built\n");
4968 	}
4969 #else
4970 	if(janus_ice_set_turn_rest_api(turn_rest_api, turn_rest_api_key, turn_rest_api_method, turn_rest_api_timeout) < 0) {
4971 		JANUS_LOG(LOG_FATAL, "Invalid TURN REST API configuration: %s (%s, %s)\n", turn_rest_api, turn_rest_api_key, turn_rest_api_method);
4972 		exit(1);
4973 	}
4974 #endif
4975 	item = janus_config_get(config, config_nat, janus_config_type_item, "nice_debug");
4976 	if(item && item->value && janus_is_true(item->value)) {
4977 		/* Enable libnice debugging */
4978 		janus_ice_debugging_enable();
4979 	}
4980 	if(stun_server == NULL && turn_server == NULL) {
4981 		/* No STUN and TURN server provided for Janus: make sure it isn't on a private address */
4982 		int num_ips = janus_get_public_ip_count();
4983 		if(num_ips == 0) {
4984 			/* If nat_1_1_mapping is off, the first (and only) public IP is the local_ip */
4985 			num_ips++;
4986 		}
4987 		/* Check each public IP */
4988 		int i = 0;
4989 		for(i = 0; i < num_ips; i++) {
4990 			gboolean private_address = FALSE;
4991 			const gchar *test_ip = janus_get_public_ip(i);
4992 			janus_network_address addr;
4993 			if(janus_network_string_to_address(janus_network_query_options_any_ip, test_ip, &addr) != 0) {
4994 				JANUS_LOG(LOG_ERR, "Invalid address %s..?\n", test_ip);
4995 			} else {
4996 				if(addr.family == AF_INET) {
4997 					unsigned short int ip[4];
4998 					sscanf(test_ip, "%hu.%hu.%hu.%hu", &ip[0], &ip[1], &ip[2], &ip[3]);
4999 					if(ip[0] == 10) {
5000 						/* Class A private address */
5001 						private_address = TRUE;
5002 					} else if(ip[0] == 172 && (ip[1] >= 16 && ip[1] <= 31)) {
5003 						/* Class B private address */
5004 						private_address = TRUE;
5005 					} else if(ip[0] == 192 && ip[1] == 168) {
5006 						/* Class C private address */
5007 						private_address = TRUE;
5008 					}
5009 				} else {
5010 					/* TODO Similar check for IPv6... */
5011 				}
5012 			}
5013 			if(private_address) {
5014 				JANUS_LOG(LOG_WARN, "Janus is deployed on a private address (%s) but you didn't specify any STUN server!"
5015 			                    " Expect trouble if this is supposed to work over the internet and not just in a LAN...\n", test_ip);
5016 			}
5017 		}
5018 	}
5019 
5020 	/* Is there any DSCP TOS to apply? */
5021 	item = janus_config_get(config, config_media, janus_config_type_item, "dscp");
5022 	if(!item || !item->value)	/* Just for backwards compatibility */
5023 		item = janus_config_get(config, config_media, janus_config_type_item, "dscp_tos");
5024 	if(item && item->value) {
5025 		int dscp = atoi(item->value);
5026 		if(dscp < 0) {
5027 			JANUS_LOG(LOG_WARN, "Ignoring dscp value as it's not a positive integer\n");
5028 		} else {
5029 			janus_set_dscp(dscp);
5030 		}
5031 	}
5032 
5033 	/* NACK related stuff */
5034 	item = janus_config_get(config, config_media, janus_config_type_item, "min_nack_queue");
5035 	if(item && item->value) {
5036 		int mnq = atoi(item->value);
5037 		if(mnq < 0) {
5038 			JANUS_LOG(LOG_WARN, "Ignoring min_nack_queue value as it's not a positive integer\n");
5039 		} else {
5040 			janus_set_min_nack_queue(mnq);
5041 		}
5042 	}
5043 	item = janus_config_get(config, config_media, janus_config_type_item, "nack_optimizations");
5044 	if(item && item->value) {
5045 		gboolean optimize = janus_is_true(item->value);
5046 		janus_set_nack_optimizations_enabled(optimize);
5047 	}
5048 	/* no-media timer */
5049 	item = janus_config_get(config, config_media, janus_config_type_item, "no_media_timer");
5050 	if(item && item->value) {
5051 		int nmt = atoi(item->value);
5052 		if(nmt < 0) {
5053 			JANUS_LOG(LOG_WARN, "Ignoring no_media_timer value as it's not a positive integer\n");
5054 		} else {
5055 			janus_set_no_media_timer(nmt);
5056 		}
5057 	}
5058 	/* slowlink-threshold value */
5059 	item = janus_config_get(config, config_media, janus_config_type_item, "slowlink_threshold");
5060 	if(item && item->value) {
5061 		int st = atoi(item->value);
5062 		if(st < 0) {
5063 			JANUS_LOG(LOG_WARN, "Ignoring slowlink_threshold value as it's not a positive integer\n");
5064 		} else {
5065 			janus_set_slowlink_threshold(st);
5066 		}
5067 	}
5068 	/* TWCC period */
5069 	item = janus_config_get(config, config_media, janus_config_type_item, "twcc_period");
5070 	if(item && item->value) {
5071 		int tp = atoi(item->value);
5072 		if(tp <= 0) {
5073 			JANUS_LOG(LOG_WARN, "Ignoring twcc_period value as it's not a positive integer\n");
5074 		} else {
5075 			janus_set_twcc_period(tp);
5076 		}
5077 	}
5078 
5079 	/* Setup OpenSSL stuff */
5080 	const char *server_pem;
5081 	item = janus_config_get(config, config_certs, janus_config_type_item, "cert_pem");
5082 	if(!item || !item->value) {
5083 		server_pem = NULL;
5084 	} else {
5085 		server_pem = item->value;
5086 	}
5087 	const char *server_key;
5088 	item = janus_config_get(config, config_certs, janus_config_type_item, "cert_key");
5089 	if(!item || !item->value) {
5090 		server_key = NULL;
5091 	} else {
5092 		server_key = item->value;
5093 	}
5094 	const char *password;
5095 	item = janus_config_get(config, config_certs, janus_config_type_item, "cert_pwd");
5096 	if(!item || !item->value) {
5097 		password = NULL;
5098 	} else {
5099 		password = item->value;
5100 	}
5101 	JANUS_LOG(LOG_VERB, "Using certificates:\n\t%s\n\t%s\n", server_pem, server_key);
5102 
5103 	SSL_library_init();
5104 	SSL_load_error_strings();
5105 	OpenSSL_add_all_algorithms();
5106 	/* Check if random pool looks ok (this does not give any guarantees for later, though) */
5107 	if(RAND_status() != 1) {
5108 		JANUS_LOG(LOG_FATAL, "Random pool is not properly seeded, cannot generate random numbers\n");
5109 		exit(1);
5110 	}
5111 	/* ... and DTLS-SRTP in particular */
5112 	const char *dtls_ciphers = NULL;
5113 	item = janus_config_get(config, config_certs, janus_config_type_item, "dtls_ciphers");
5114 	if(item && item->value)
5115 		dtls_ciphers = item->value;
5116 	guint16 dtls_timeout = 1000;
5117 	item = janus_config_get(config, config_media, janus_config_type_item, "dtls_timeout");
5118 	if(item && item->value && janus_string_to_uint16(item->value, &dtls_timeout) < 0) {
5119 		JANUS_LOG(LOG_WARN, "Invalid DTLS timeout: %s (falling back to default)\n", item->value);
5120 		dtls_timeout = 1000;
5121 	}
5122 	gboolean rsa_private_key = FALSE;
5123 	item = janus_config_get(config, config_certs, janus_config_type_item, "rsa_private_key");
5124 	if(item && item->value)
5125 		rsa_private_key = janus_is_true(item->value);
5126 	gboolean dtls_accept_selfsigned = TRUE;
5127 	item = janus_config_get(config, config_certs, janus_config_type_item, "dtls_accept_selfsigned");
5128 	if(item && item->value)
5129 		dtls_accept_selfsigned = janus_is_true(item->value);
5130 	if(janus_dtls_srtp_init(server_pem, server_key, password, dtls_ciphers, dtls_timeout, rsa_private_key, dtls_accept_selfsigned) < 0) {
5131 		exit(1);
5132 	}
5133 	/* Check if there's any custom value for the starting MTU to use in the BIO filter */
5134 	item = janus_config_get(config, config_media, janus_config_type_item, "dtls_mtu");
5135 	if(item && item->value)
5136 		janus_dtls_bio_agent_set_mtu(atoi(item->value));
5137 
5138 #ifdef HAVE_SCTP
5139 	/* Initialize SCTP for DataChannels */
5140 	if(janus_sctp_init() < 0) {
5141 		exit(1);
5142 	}
5143 #else
5144 	JANUS_LOG(LOG_WARN, "Data Channels support not compiled\n");
5145 #endif
5146 
5147 	/* Sessions */
5148 	sessions = g_hash_table_new_full(g_int64_hash, g_int64_equal, (GDestroyNotify)g_free, NULL);
5149 	janus_mutex_init(&sessions_mutex);
5150 	/* Start the sessions timeout watchdog */
5151 	sessions_watchdog_context = g_main_context_new();
5152 	GMainLoop *watchdog_loop = g_main_loop_new(sessions_watchdog_context, FALSE);
5153 	GError *error = NULL;
5154 	GThread *watchdog = g_thread_try_new("timeout watchdog", &janus_sessions_watchdog, watchdog_loop, &error);
5155 	if(error != NULL) {
5156 		JANUS_LOG(LOG_FATAL, "Got error %d (%s) trying to start sessions timeout watchdog...\n",
5157 			error->code, error->message ? error->message : "??");
5158 		g_error_free(error);
5159 		exit(1);
5160 	}
5161 	/* Start the thread that will dispatch incoming requests */
5162 	requests = g_async_queue_new_full((GDestroyNotify)janus_request_destroy);
5163 	GThread *requests_thread = g_thread_try_new("sessions requests", &janus_transport_requests, NULL, &error);
5164 	if(error != NULL) {
5165 		JANUS_LOG(LOG_FATAL, "Got error %d (%s) trying to start requests thread...\n",
5166 			error->code, error->message ? error->message : "??");
5167 		g_error_free(error);
5168 		exit(1);
5169 	}
5170 	/* Create a thread pool to handle asynchronous requests, no matter what the transport */
5171 	error = NULL;
5172 	tasks = g_thread_pool_new(janus_transport_task, NULL, -1, FALSE, &error);
5173 	if(error != NULL) {
5174 		/* Something went wrong... */
5175 		JANUS_LOG(LOG_FATAL, "Got error %d (%s) trying to launch the request pool task thread...\n",
5176 			error->code, error->message ? error->message : "??");
5177 		g_error_free(error);
5178 		exit(1);
5179 	}
5180 	/* Wait 120 seconds before stopping idle threads to avoid the creation of too many threads for AddressSanitizer. */
5181 	g_thread_pool_set_max_idle_time(120 * 1000);
5182 
5183 	/* Load event handlers */
5184 	path = NULL;
5185 	dir = NULL;
5186 	/* Event handlers are disabled by default, though: they need to be enabled in the configuration */
5187 	item = janus_config_get(config, config_events, janus_config_type_item, "broadcast");
5188 	gboolean enable_events = FALSE;
5189 	if(item && item->value)
5190 		enable_events = janus_is_true(item->value);
5191 	if(!enable_events) {
5192 		JANUS_LOG(LOG_INFO, "Event handlers support disabled\n");
5193 	} else {
5194 		gchar **disabled_eventhandlers = NULL;
5195 		path = EVENTDIR;
5196 		item = janus_config_get(config, config_general, janus_config_type_item, "events_folder");
5197 		if(item && item->value)
5198 			path = (char *)item->value;
5199 		JANUS_LOG(LOG_INFO, "Event handler plugins folder: %s\n", path);
5200 		dir = opendir(path);
5201 		if(!dir) {
5202 			/* Not really fatal, we don't care and go on anyway: event handlers are not fundamental */
5203 			JANUS_LOG(LOG_WARN, "\tCouldn't access event handler plugins folder...\n");
5204 		} else {
5205 			item = janus_config_get(config, config_events, janus_config_type_item, "stats_period");
5206 			if(item && item->value) {
5207 				/* Check if we need to use a larger period for pushing statistics to event handlers */
5208 				int period = atoi(item->value);
5209 				if(period < 0) {
5210 					JANUS_LOG(LOG_WARN, "Invalid event handlers statistics period, using default value (1 second)\n");
5211 				} else if(period == 0) {
5212 					janus_ice_set_event_stats_period(0);
5213 					JANUS_LOG(LOG_WARN, "Disabling event handlers statistics period, no media statistics will be pushed to event handlers\n");
5214 				} else {
5215 					janus_ice_set_event_stats_period(period);
5216 					JANUS_LOG(LOG_INFO, "Setting event handlers statistics period to %d seconds\n", period);
5217 				}
5218 			}
5219 			item = janus_config_get(config, config_events, janus_config_type_item, "combine_media_stats");
5220 			if(item && item->value) {
5221 				gboolean combine = janus_is_true(item->value);
5222 				janus_ice_event_set_combine_media_stats(combine);
5223 				if(combine)
5224 					JANUS_LOG(LOG_INFO, "Event handler configured to send media stats combined in a single event\n");
5225 			}
5226 			/* Any event handlers to ignore? */
5227 			item = janus_config_get(config, config_events, janus_config_type_item, "disable");
5228 			if(item && item->value)
5229 				disabled_eventhandlers = g_strsplit(item->value, ",", -1);
5230 			/* Open the shared objects */
5231 			struct dirent *eventent = NULL;
5232 			char eventpath[1024];
5233 			while((eventent = readdir(dir))) {
5234 				int len = strlen(eventent->d_name);
5235 				if (len < 4) {
5236 					continue;
5237 				}
5238 				if (strcasecmp(eventent->d_name+len-strlen(SHLIB_EXT), SHLIB_EXT)) {
5239 					continue;
5240 				}
5241 				/* Check if this event handler has been disabled in the configuration file */
5242 				if(disabled_eventhandlers != NULL) {
5243 					gchar *index = disabled_eventhandlers[0];
5244 					if(index != NULL) {
5245 						int i=0;
5246 						gboolean skip = FALSE;
5247 						while(index != NULL) {
5248 							while(isspace(*index))
5249 								index++;
5250 							if(strlen(index) && !strcmp(index, eventent->d_name)) {
5251 								JANUS_LOG(LOG_WARN, "Event handler plugin '%s' has been disabled, skipping...\n", eventent->d_name);
5252 								skip = TRUE;
5253 								break;
5254 							}
5255 							i++;
5256 							index = disabled_eventhandlers[i];
5257 						}
5258 						if(skip)
5259 							continue;
5260 					}
5261 				}
5262 				JANUS_LOG(LOG_INFO, "Loading event handler plugin '%s'...\n", eventent->d_name);
5263 				memset(eventpath, 0, 1024);
5264 				g_snprintf(eventpath, 1024, "%s/%s", path, eventent->d_name);
5265 				void *event = dlopen(eventpath, RTLD_NOW | RTLD_GLOBAL);
5266 				if (!event) {
5267 					JANUS_LOG(exit_on_dl_error ? LOG_FATAL : LOG_ERR, "\tCouldn't load event handler plugin '%s': %s\n", eventent->d_name, dlerror());
5268 					if (exit_on_dl_error)
5269 						exit(1);
5270 				} else {
5271 					create_e *create = (create_e*) dlsym(event, "create");
5272 					const char *dlsym_error = dlerror();
5273 					if (dlsym_error) {
5274 						JANUS_LOG(exit_on_dl_error ? LOG_FATAL : LOG_ERR, "\tCouldn't load symbol 'create': %s\n", dlsym_error);
5275 						if (exit_on_dl_error)
5276 							exit(1);
5277 						continue;
5278 					}
5279 					janus_eventhandler *janus_eventhandler = create();
5280 					if(!janus_eventhandler) {
5281 						JANUS_LOG(LOG_ERR, "\tCouldn't use function 'create'...\n");
5282 						continue;
5283 					}
5284 					/* Are all the mandatory methods and callbacks implemented? */
5285 					if(!janus_eventhandler->init || !janus_eventhandler->destroy ||
5286 							!janus_eventhandler->get_api_compatibility ||
5287 							!janus_eventhandler->get_version ||
5288 							!janus_eventhandler->get_version_string ||
5289 							!janus_eventhandler->get_description ||
5290 							!janus_eventhandler->get_package ||
5291 							!janus_eventhandler->get_name ||
5292 							!janus_eventhandler->incoming_event) {
5293 						JANUS_LOG(LOG_ERR, "\tMissing some mandatory methods/callbacks, skipping this event handler plugin...\n");
5294 						continue;
5295 					}
5296 					if(janus_eventhandler->get_api_compatibility() < JANUS_EVENTHANDLER_API_VERSION) {
5297 						JANUS_LOG(LOG_ERR, "The '%s' event handler plugin was compiled against an older version of the API (%d < %d), skipping it: update it to enable it again\n",
5298 							janus_eventhandler->get_package(), janus_eventhandler->get_api_compatibility(), JANUS_EVENTHANDLER_API_VERSION);
5299 						continue;
5300 					}
5301 					janus_eventhandler->init(configs_folder);
5302 					JANUS_LOG(LOG_VERB, "\tVersion: %d (%s)\n", janus_eventhandler->get_version(), janus_eventhandler->get_version_string());
5303 					JANUS_LOG(LOG_VERB, "\t   [%s] %s\n", janus_eventhandler->get_package(), janus_eventhandler->get_name());
5304 					JANUS_LOG(LOG_VERB, "\t   %s\n", janus_eventhandler->get_description());
5305 					JANUS_LOG(LOG_VERB, "\t   Plugin API version: %d\n", janus_eventhandler->get_api_compatibility());
5306 					JANUS_LOG(LOG_VERB, "\t   Subscriptions:");
5307 					if(janus_eventhandler->events_mask == 0) {
5308 						JANUS_LOG(LOG_VERB, " none");
5309 					} else {
5310 						if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_SESSION))
5311 							JANUS_LOG(LOG_VERB, " sessions");
5312 						if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_HANDLE))
5313 							JANUS_LOG(LOG_VERB, " handles");
5314 						if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_JSEP))
5315 							JANUS_LOG(LOG_VERB, " jsep");
5316 						if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_WEBRTC))
5317 							JANUS_LOG(LOG_VERB, " webrtc");
5318 						if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_MEDIA))
5319 							JANUS_LOG(LOG_VERB, " media");
5320 						if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_PLUGIN))
5321 							JANUS_LOG(LOG_VERB, " plugins");
5322 						if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_TRANSPORT))
5323 							JANUS_LOG(LOG_VERB, " transports");
5324 					}
5325 					JANUS_LOG(LOG_VERB, "\n");
5326 					if(eventhandlers == NULL)
5327 						eventhandlers = g_hash_table_new(g_str_hash, g_str_equal);
5328 					g_hash_table_insert(eventhandlers, (gpointer)janus_eventhandler->get_package(), janus_eventhandler);
5329 					if(eventhandlers_so == NULL)
5330 						eventhandlers_so = g_hash_table_new(g_str_hash, g_str_equal);
5331 					g_hash_table_insert(eventhandlers_so, (gpointer)janus_eventhandler->get_package(), event);
5332 				}
5333 			}
5334 			closedir(dir);
5335 		}
5336 		if(disabled_eventhandlers != NULL)
5337 			g_strfreev(disabled_eventhandlers);
5338 		disabled_eventhandlers = NULL;
5339 		/* Initialize the event broadcaster */
5340 		if(janus_events_init(enable_events, (server_name ? server_name : (char *)JANUS_SERVER_NAME), eventhandlers) < 0) {
5341 			JANUS_LOG(LOG_FATAL, "Error initializing the Event handlers mechanism...\n");
5342 			exit(1);
5343 		}
5344 	}
5345 
5346 	/* Load plugins */
5347 	path = PLUGINDIR;
5348 	item = janus_config_get(config, config_general, janus_config_type_item, "plugins_folder");
5349 	if(item && item->value)
5350 		path = (char *)item->value;
5351 	JANUS_LOG(LOG_INFO, "Plugins folder: %s\n", path);
5352 	dir = opendir(path);
5353 	if(!dir) {
5354 		JANUS_LOG(LOG_FATAL, "\tCouldn't access plugins folder...\n");
5355 		exit(1);
5356 	}
5357 	/* Any plugin to ignore? */
5358 	gchar **disabled_plugins = NULL;
5359 	item = janus_config_get(config, config_plugins, janus_config_type_item, "disable");
5360 	if(item && item->value)
5361 		disabled_plugins = g_strsplit(item->value, ",", -1);
5362 	/* Open the shared objects */
5363 	struct dirent *pluginent = NULL;
5364 	char pluginpath[1024];
5365 	while((pluginent = readdir(dir))) {
5366 		int len = strlen(pluginent->d_name);
5367 		if (len < 4) {
5368 			continue;
5369 		}
5370 		if (strcasecmp(pluginent->d_name+len-strlen(SHLIB_EXT), SHLIB_EXT)) {
5371 			continue;
5372 		}
5373 		/* Check if this plugins has been disabled in the configuration file */
5374 		if(disabled_plugins != NULL) {
5375 			gchar *index = disabled_plugins[0];
5376 			if(index != NULL) {
5377 				int i=0;
5378 				gboolean skip = FALSE;
5379 				while(index != NULL) {
5380 					while(isspace(*index))
5381 						index++;
5382 					if(strlen(index) && !strcmp(index, pluginent->d_name)) {
5383 						JANUS_LOG(LOG_WARN, "Plugin '%s' has been disabled, skipping...\n", pluginent->d_name);
5384 						skip = TRUE;
5385 						break;
5386 					}
5387 					i++;
5388 					index = disabled_plugins[i];
5389 				}
5390 				if(skip)
5391 					continue;
5392 			}
5393 		}
5394 		JANUS_LOG(LOG_INFO, "Loading plugin '%s'...\n", pluginent->d_name);
5395 		memset(pluginpath, 0, 1024);
5396 		g_snprintf(pluginpath, 1024, "%s/%s", path, pluginent->d_name);
5397 		void *plugin = dlopen(pluginpath, RTLD_NOW | RTLD_GLOBAL);
5398 		if (!plugin) {
5399 			JANUS_LOG(exit_on_dl_error ? LOG_FATAL : LOG_ERR, "\tCouldn't load plugin '%s': %s\n", pluginent->d_name, dlerror());
5400 			if (exit_on_dl_error)
5401 				exit(1);
5402 		} else {
5403 			create_p *create = (create_p*) dlsym(plugin, "create");
5404 			const char *dlsym_error = dlerror();
5405 			if (dlsym_error) {
5406 				JANUS_LOG(exit_on_dl_error ? LOG_FATAL : LOG_ERR, "\tCouldn't load symbol 'create': %s\n", dlsym_error);
5407 				if (exit_on_dl_error)
5408 					exit(1);
5409 				continue;
5410 			}
5411 			janus_plugin *janus_plugin = create();
5412 			if(!janus_plugin) {
5413 				JANUS_LOG(LOG_ERR, "\tCouldn't use function 'create'...\n");
5414 				continue;
5415 			}
5416 			/* Are all the mandatory methods and callbacks implemented? */
5417 			if(!janus_plugin->init || !janus_plugin->destroy ||
5418 					!janus_plugin->get_api_compatibility ||
5419 					!janus_plugin->get_version ||
5420 					!janus_plugin->get_version_string ||
5421 					!janus_plugin->get_description ||
5422 					!janus_plugin->get_package ||
5423 					!janus_plugin->get_name ||
5424 					!janus_plugin->create_session ||
5425 					!janus_plugin->query_session ||
5426 					!janus_plugin->destroy_session ||
5427 					!janus_plugin->handle_message ||
5428 					!janus_plugin->setup_media ||
5429 					!janus_plugin->hangup_media) {
5430 				JANUS_LOG(LOG_ERR, "\tMissing some mandatory methods/callbacks, skipping this plugin...\n");
5431 				continue;
5432 			}
5433 			if(janus_plugin->get_api_compatibility() < JANUS_PLUGIN_API_VERSION) {
5434 				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",
5435 					janus_plugin->get_package(), janus_plugin->get_api_compatibility(), JANUS_PLUGIN_API_VERSION);
5436 				continue;
5437 			}
5438 			if(janus_plugin->init(&janus_handler_plugin, configs_folder) < 0) {
5439 				JANUS_LOG(LOG_WARN, "The '%s' plugin could not be initialized\n", janus_plugin->get_package());
5440 				dlclose(plugin);
5441 				continue;
5442 			}
5443 			JANUS_LOG(LOG_VERB, "\tVersion: %d (%s)\n", janus_plugin->get_version(), janus_plugin->get_version_string());
5444 			JANUS_LOG(LOG_VERB, "\t   [%s] %s\n", janus_plugin->get_package(), janus_plugin->get_name());
5445 			JANUS_LOG(LOG_VERB, "\t   %s\n", janus_plugin->get_description());
5446 			JANUS_LOG(LOG_VERB, "\t   Plugin API version: %d\n", janus_plugin->get_api_compatibility());
5447 			if(!janus_plugin->incoming_rtp && !janus_plugin->incoming_rtcp && !janus_plugin->incoming_data) {
5448 				JANUS_LOG(LOG_WARN, "The '%s' plugin doesn't implement any callback for RTP/RTCP/data... is this on purpose?\n",
5449 					janus_plugin->get_package());
5450 			}
5451 			if(!janus_plugin->incoming_rtp && !janus_plugin->incoming_rtcp && janus_plugin->incoming_data) {
5452 				JANUS_LOG(LOG_WARN, "The '%s' plugin will only handle data channels (no RTP/RTCP)... is this on purpose?\n",
5453 					janus_plugin->get_package());
5454 			}
5455 			if(plugins == NULL)
5456 				plugins = g_hash_table_new(g_str_hash, g_str_equal);
5457 			g_hash_table_insert(plugins, (gpointer)janus_plugin->get_package(), janus_plugin);
5458 			if(plugins_so == NULL)
5459 				plugins_so = g_hash_table_new(g_str_hash, g_str_equal);
5460 			g_hash_table_insert(plugins_so, (gpointer)janus_plugin->get_package(), plugin);
5461 		}
5462 	}
5463 	closedir(dir);
5464 	if(disabled_plugins != NULL)
5465 		g_strfreev(disabled_plugins);
5466 	disabled_plugins = NULL;
5467 
5468 	/* Load transports */
5469 	gboolean janus_api_enabled = FALSE, admin_api_enabled = FALSE;
5470 	path = TRANSPORTDIR;
5471 	item = janus_config_get(config, config_general, janus_config_type_item, "transports_folder");
5472 	if(item && item->value)
5473 		path = (char *)item->value;
5474 	JANUS_LOG(LOG_INFO, "Transport plugins folder: %s\n", path);
5475 	dir = opendir(path);
5476 	if(!dir) {
5477 		JANUS_LOG(LOG_FATAL, "\tCouldn't access transport plugins folder...\n");
5478 		exit(1);
5479 	}
5480 	/* Any transport to ignore? */
5481 	gchar **disabled_transports = NULL;
5482 	item = janus_config_get(config, config_transports, janus_config_type_item, "disable");
5483 	if(item && item->value)
5484 		disabled_transports = g_strsplit(item->value, ",", -1);
5485 	/* Open the shared objects */
5486 	struct dirent *transportent = NULL;
5487 	char transportpath[1024];
5488 	while((transportent = readdir(dir))) {
5489 		int len = strlen(transportent->d_name);
5490 		if (len < 4) {
5491 			continue;
5492 		}
5493 		if (strcasecmp(transportent->d_name+len-strlen(SHLIB_EXT), SHLIB_EXT)) {
5494 			continue;
5495 		}
5496 		/* Check if this transports has been disabled in the configuration file */
5497 		if(disabled_transports != NULL) {
5498 			gchar *index = disabled_transports[0];
5499 			if(index != NULL) {
5500 				int i=0;
5501 				gboolean skip = FALSE;
5502 				while(index != NULL) {
5503 					while(isspace(*index))
5504 						index++;
5505 					if(strlen(index) && !strcmp(index, transportent->d_name)) {
5506 						JANUS_LOG(LOG_WARN, "Transport plugin '%s' has been disabled, skipping...\n", transportent->d_name);
5507 						skip = TRUE;
5508 						break;
5509 					}
5510 					i++;
5511 					index = disabled_transports[i];
5512 				}
5513 				if(skip)
5514 					continue;
5515 			}
5516 		}
5517 		JANUS_LOG(LOG_INFO, "Loading transport plugin '%s'...\n", transportent->d_name);
5518 		memset(transportpath, 0, 1024);
5519 		g_snprintf(transportpath, 1024, "%s/%s", path, transportent->d_name);
5520 		void *transport = dlopen(transportpath, RTLD_NOW | RTLD_GLOBAL);
5521 		if (!transport) {
5522 			JANUS_LOG(exit_on_dl_error ? LOG_FATAL : LOG_ERR, "\tCouldn't load transport plugin '%s': %s\n", transportent->d_name, dlerror());
5523 			if (exit_on_dl_error)
5524 				exit(1);
5525 		} else {
5526 			create_t *create = (create_t*) dlsym(transport, "create");
5527 			const char *dlsym_error = dlerror();
5528 			if (dlsym_error) {
5529 				JANUS_LOG(exit_on_dl_error ? LOG_FATAL : LOG_ERR, "\tCouldn't load symbol 'create': %s\n", dlsym_error);
5530 				if (exit_on_dl_error)
5531 					exit(1);
5532 				continue;
5533 			}
5534 			janus_transport *janus_transport = create();
5535 			if(!janus_transport) {
5536 				JANUS_LOG(LOG_ERR, "\tCouldn't use function 'create'...\n");
5537 				continue;
5538 			}
5539 			/* Are all the mandatory methods and callbacks implemented? */
5540 			if(!janus_transport->init || !janus_transport->destroy ||
5541 					!janus_transport->get_api_compatibility ||
5542 					!janus_transport->get_version ||
5543 					!janus_transport->get_version_string ||
5544 					!janus_transport->get_description ||
5545 					!janus_transport->get_package ||
5546 					!janus_transport->get_name ||
5547 					!janus_transport->send_message ||
5548 					!janus_transport->is_janus_api_enabled ||
5549 					!janus_transport->is_admin_api_enabled ||
5550 					!janus_transport->session_created ||
5551 					!janus_transport->session_over ||
5552 					!janus_transport->session_claimed) {
5553 				JANUS_LOG(LOG_ERR, "\tMissing some mandatory methods/callbacks, skipping this transport plugin...\n");
5554 				continue;
5555 			}
5556 			if(janus_transport->get_api_compatibility() < JANUS_TRANSPORT_API_VERSION) {
5557 				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",
5558 					janus_transport->get_package(), janus_transport->get_api_compatibility(), JANUS_TRANSPORT_API_VERSION);
5559 				continue;
5560 			}
5561 			if(janus_transport->init(&janus_handler_transport, configs_folder) < 0) {
5562 				JANUS_LOG(LOG_WARN, "The '%s' plugin could not be initialized\n", janus_transport->get_package());
5563 				dlclose(transport);
5564 				continue;
5565 			}
5566 			JANUS_LOG(LOG_VERB, "\tVersion: %d (%s)\n", janus_transport->get_version(), janus_transport->get_version_string());
5567 			JANUS_LOG(LOG_VERB, "\t   [%s] %s\n", janus_transport->get_package(), janus_transport->get_name());
5568 			JANUS_LOG(LOG_VERB, "\t   %s\n", janus_transport->get_description());
5569 			JANUS_LOG(LOG_VERB, "\t   Plugin API version: %d\n", janus_transport->get_api_compatibility());
5570 			JANUS_LOG(LOG_VERB, "\t   Janus API: %s\n", janus_transport->is_janus_api_enabled() ? "enabled" : "disabled");
5571 			JANUS_LOG(LOG_VERB, "\t   Admin API: %s\n", janus_transport->is_admin_api_enabled() ? "enabled" : "disabled");
5572 			janus_api_enabled = janus_api_enabled || janus_transport->is_janus_api_enabled();
5573 			admin_api_enabled = admin_api_enabled || janus_transport->is_admin_api_enabled();
5574 			if(transports == NULL)
5575 				transports = g_hash_table_new(g_str_hash, g_str_equal);
5576 			g_hash_table_insert(transports, (gpointer)janus_transport->get_package(), janus_transport);
5577 			if(transports_so == NULL)
5578 				transports_so = g_hash_table_new(g_str_hash, g_str_equal);
5579 			g_hash_table_insert(transports_so, (gpointer)janus_transport->get_package(), transport);
5580 		}
5581 	}
5582 	closedir(dir);
5583 	if(disabled_transports != NULL)
5584 		g_strfreev(disabled_transports);
5585 	disabled_transports = NULL;
5586 	/* Make sure at least a Janus API transport is available */
5587 	if(!janus_api_enabled) {
5588 		JANUS_LOG(LOG_FATAL, "No Janus API transport is available... enable at least one and restart Janus\n");
5589 		exit(1);	/* FIXME Should we really give up? */
5590 	}
5591 	/* Make sure at least an admin API transport is available, if the auth mechanism is enabled */
5592 	if(!admin_api_enabled && janus_auth_is_stored_mode()) {
5593 		JANUS_LOG(LOG_FATAL, "No Admin/monitor transport is available, but the stored 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 or set the token auth secret.\n");
5594 		exit(1);	/* FIXME Should we really give up? */
5595 	}
5596 
5597 	/* Make sure libnice is recent enough, otherwise print a warning */
5598 	int libnice_version = 0;
5599 	if(libnice_version_string != NULL && sscanf(libnice_version_string, "%*d.%*d.%d", &libnice_version) == 1) {
5600 		if(libnice_version < 16) {
5601 			JANUS_LOG(LOG_WARN, "libnice version outdated: %s installed, at least 0.1.16 recommended. Notice the installed version was checked at build time: if you updated libnice in the meanwhile, re-configure and recompile to get rid of this warning\n",
5602 				libnice_version_string);
5603 		}
5604 	}
5605 
5606 	/* Ok, Janus has started! Let the parent now about this if we're daemonizing */
5607 	if(daemonize) {
5608 		int code = 0;
5609 		ssize_t res = 0;
5610 		do {
5611 			res = write(pipefd[1], &code, sizeof(int));
5612 		} while(res == -1 && errno == EINTR);
5613 	}
5614 
5615 	/* If the Event Handlers mechanism is enabled, notify handlers that Janus just started */
5616 	if(janus_events_is_enabled()) {
5617 		json_t *info = json_object();
5618 		json_object_set_new(info, "status", json_string("started"));
5619 		json_object_set_new(info, "info", janus_info(NULL));
5620 		janus_events_notify_handlers(JANUS_EVENT_TYPE_CORE, JANUS_EVENT_SUBTYPE_CORE_STARTUP, 0, info);
5621 	}
5622 
5623 	/* Loop until we have to stop */
5624 	mainloop = g_main_loop_new (NULL, TRUE);
5625 	g_main_loop_run(mainloop);
5626 
5627 	/* If the Event Handlers mechanism is enabled, notify handlers that Janus is hanging up */
5628 	if(janus_events_is_enabled()) {
5629 		json_t *info = json_object();
5630 		json_object_set_new(info, "status", json_string("shutdown"));
5631 		json_object_set_new(info, "signum", json_integer(stop_signal));
5632 		janus_events_notify_handlers(JANUS_EVENT_TYPE_CORE, JANUS_EVENT_SUBTYPE_CORE_SHUTDOWN, 0, info);
5633 	}
5634 
5635 	/* Done */
5636 	JANUS_LOG(LOG_INFO, "Ending sessions timeout watchdog...\n");
5637 	g_main_loop_quit(watchdog_loop);
5638 	g_thread_join(watchdog);
5639 	watchdog = NULL;
5640 	g_main_loop_unref(watchdog_loop);
5641 	g_main_context_unref(sessions_watchdog_context);
5642 	sessions_watchdog_context = NULL;
5643 
5644 	if(config)
5645 		janus_config_destroy(config);
5646 
5647 	JANUS_LOG(LOG_INFO, "Closing transport plugins:\n");
5648 	if(transports != NULL && g_hash_table_size(transports) > 0) {
5649 		g_hash_table_foreach(transports, janus_transport_close, NULL);
5650 		g_hash_table_destroy(transports);
5651 	}
5652 	if(transports_so != NULL && g_hash_table_size(transports_so) > 0) {
5653 		g_hash_table_foreach(transports_so, janus_transportso_close, NULL);
5654 		g_hash_table_destroy(transports_so);
5655 	}
5656 	/* Get rid of requests tasks and thread too */
5657 	g_thread_pool_free(tasks, FALSE, FALSE);
5658 	JANUS_LOG(LOG_INFO, "Ending requests thread...\n");
5659 	g_async_queue_push(requests, &exit_message);
5660 	g_thread_join(requests_thread);
5661 	requests_thread = NULL;
5662 	g_async_queue_unref(requests);
5663 
5664 	JANUS_LOG(LOG_INFO, "Destroying sessions...\n");
5665 	g_clear_pointer(&sessions, g_hash_table_destroy);
5666 	janus_ice_deinit();
5667 	JANUS_LOG(LOG_INFO, "Freeing crypto resources...\n");
5668 	janus_dtls_srtp_cleanup();
5669 	EVP_cleanup();
5670 	ERR_free_strings();
5671 #ifdef HAVE_SCTP
5672 	JANUS_LOG(LOG_INFO, "De-initializing SCTP...\n");
5673 	janus_sctp_deinit();
5674 #endif
5675 	janus_auth_deinit();
5676 
5677 	JANUS_LOG(LOG_INFO, "Closing plugins:\n");
5678 	if(plugins != NULL && g_hash_table_size(plugins) > 0) {
5679 		g_hash_table_foreach(plugins, janus_plugin_close, NULL);
5680 		g_hash_table_destroy(plugins);
5681 	}
5682 	if(plugins_so != NULL && g_hash_table_size(plugins_so) > 0) {
5683 		g_hash_table_foreach(plugins_so, janus_pluginso_close, NULL);
5684 		g_hash_table_destroy(plugins_so);
5685 	}
5686 
5687 	JANUS_LOG(LOG_INFO, "Closing event handlers:\n");
5688 	janus_events_deinit();
5689 	if(eventhandlers != NULL && g_hash_table_size(eventhandlers) > 0) {
5690 		g_hash_table_foreach(eventhandlers, janus_eventhandler_close, NULL);
5691 		g_hash_table_destroy(eventhandlers);
5692 	}
5693 	if(eventhandlers_so != NULL && g_hash_table_size(eventhandlers_so) > 0) {
5694 		g_hash_table_foreach(eventhandlers_so, janus_eventhandlerso_close, NULL);
5695 		g_hash_table_destroy(eventhandlers_so);
5696 	}
5697 
5698 	janus_recorder_deinit();
5699 	g_free(local_ip);
5700 	if (public_ips) {
5701 		g_list_free(public_ips);
5702 	}
5703 	if (public_ips_table) {
5704 		g_hash_table_destroy(public_ips_table);
5705 	}
5706 
5707 	if(janus_ice_get_static_event_loops() > 0)
5708 		janus_ice_stop_static_event_loops();
5709 
5710 	janus_protected_folders_clear();
5711 
5712 #ifdef REFCOUNT_DEBUG
5713 	/* Any reference counters that are still up while we're leaving? (debug-mode only) */
5714 	janus_mutex_lock(&counters_mutex);
5715 	if(counters && g_hash_table_size(counters) > 0) {
5716 		JANUS_PRINT("Debugging reference counters: %d still allocated\n", g_hash_table_size(counters));
5717 		GHashTableIter iter;
5718 		gpointer value;
5719 		g_hash_table_iter_init(&iter, counters);
5720 		while(g_hash_table_iter_next(&iter, NULL, &value)) {
5721 			JANUS_PRINT("  -- %p\n", value);
5722 		}
5723 	} else {
5724 		JANUS_PRINT("Debugging reference counters: 0 still allocated\n");
5725 	}
5726 	janus_mutex_unlock(&counters_mutex);
5727 #endif
5728 	g_clear_pointer(&janus_log_global_prefix, g_free);
5729 
5730 	JANUS_PRINT("Bye!\n");
5731 
5732 	exit(0);
5733 }
5734