Statistics
| Branch: | Revision:

janus-gateway / janus.c @ 5e9e29e0

History | View | Annotate | Download (55 KB)

1
/*! \file   janus.c
2
 * \author Lorenzo Miniero <lorenzo@meetecho.com>
3
 * \copyright GNU Affero General Public License v3
4
 * \brief  Janus core
5
 * \details Implementation of the gateway core. This code takes care of
6
 * the gateway initialization (command line/configuration) and setup,
7
 * and implements the web server (based on libmicrohttpd) and Janus protocol
8
 * (a JSON protocol implemented with Jansson) to interact with the web
9
 * applications. The core also takes care of bridging peers and plugins
10
 * accordingly. 
11
 * 
12
 * \ingroup core
13
 * \ref core
14
 */
15
 
16
#include <dlfcn.h>
17
#include <dirent.h>
18
#include <ifaddrs.h>
19
#include <net/if.h>
20
#include <signal.h>
21
#include <getopt.h>
22
#include <sys/resource.h>
23

    
24
#include "janus.h"
25
#include "cmdline.h"
26
#include "config.h"
27
#include "apierror.h"
28
#include "rtcp.h"
29
#include "sdp.h"
30
#include "utils.h"
31

    
32

    
33
static janus_config *config = NULL;
34
static char *config_file = NULL;
35
static char *configs_folder = NULL;
36

    
37
static GHashTable *plugins = NULL;
38
static GHashTable *plugins_so = NULL;
39

    
40
static struct MHD_Daemon *ws = NULL, *sws = NULL;
41
static char *ws_path = NULL;
42

    
43
/* Certificates */
44
static char *server_pem = NULL;
45
gchar *janus_get_server_pem() {
46
        return server_pem;
47
}
48
static char *server_key = NULL;
49
gchar *janus_get_server_key() {
50
        return server_key;
51
}
52

    
53

    
54
/* Information */
55
static gchar *local_ip = NULL;
56
gchar *janus_get_local_ip() {
57
        return local_ip;
58
}
59
static gchar *public_ip = NULL;
60
gchar *janus_get_public_ip() {
61
        /* Fallback to the local IP, if we have no public one */
62
        return public_ip ? public_ip : local_ip;
63
}
64
void janus_set_public_ip(const char *ip) {
65
        if(ip == NULL)
66
                return;
67
        if(public_ip != NULL)
68
                g_free(public_ip);
69
        public_ip = g_strdup(ip);
70
}
71
static gint stop = 0;
72
gint janus_is_stopping() {
73
        return stop;
74
}
75

    
76

    
77
/*! \brief Signal handler (just used to intercept CTRL+C) */
78
void janus_handle_signal(int signum);
79
void janus_handle_signal(int signum)
80
{
81
        JANUS_PRINT("Stopping gateway...\n");
82
        stop++;
83
        if(stop > 2)
84
                exit(1);
85
}
86

    
87

    
88
/** @name Plugin callback interface
89
 * These are the callbacks implemented by the gateway core, as part of
90
 * the janus_callbacks interface. Everything the plugins send the
91
 * gateway is handled here.
92
 */
93
///@{
94
int janus_push_event(janus_pluginession *handle, janus_plugin *plugin, char *transaction, char *message, char *sdp_type, char *sdp);
95
json_t *janus_handle_sdp(janus_pluginession *handle, janus_plugin *plugin, char *sdp_type, char *sdp);
96
void janus_relay_rtp(janus_pluginession *handle, int video, char *buf, int len);
97
void janus_relay_rtcp(janus_pluginession *handle, int video, char *buf, int len);
98
static janus_callbacks janus_handler_plugin =
99
        {
100
                .push_event = janus_push_event,
101
                .relay_rtp = janus_relay_rtp,
102
                .relay_rtcp = janus_relay_rtcp,
103
        }; 
104
///@}
105

    
106

    
107
/* Gateway Sessions */
108
static GHashTable *sessions = NULL;
109
janus_session *janus_session_create(void) {
110
        guint64 session_id = 0;
111
        while(session_id == 0) {
112
                session_id = g_random_int();
113
                if(janus_session_find(session_id) != NULL) {
114
                        /* Session ID already taken, try another one */
115
                        session_id = 0;
116
                }
117
        }
118
        JANUS_PRINT("Creating new session: %"SCNu64"\n", session_id);
119
        janus_session *session = (janus_session *)calloc(1, sizeof(janus_session));
120
        if(session == NULL) {
121
                JANUS_DEBUG("Memory error!\n");
122
                return NULL;
123
        }
124
        session->session_id = session_id;
125
        session->messages = g_queue_new();
126
        session->destroy = 0;
127
        janus_mutex_init(&session->mutex);
128
        g_hash_table_insert(sessions, GUINT_TO_POINTER(session_id), session);
129
        return session;
130
}
131

    
132
janus_session *janus_session_find(guint64 session_id) {
133
        return g_hash_table_lookup(sessions, GUINT_TO_POINTER(session_id));
134
}
135

    
136
gint janus_session_destroy(guint64 session_id) {
137
        janus_session *session = janus_session_find(session_id);
138
        if(session == NULL)
139
                return -1;
140
        session->destroy = 1;
141
        /* TODO Remove all handles */
142
        //~ if(handle->app == NULL) {
143
                //~ ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_PLUGIN_NOT_FOUND, "No plugin to detach from");
144
                //~ goto jsondone;
145
        //~ }
146
        //~ janus_plugin *plugin_t = (janus_plugin *)handle->app;
147
        //~ JANUS_PRINT("Detaching handle from %s\n", plugin_t->get_name());
148
        //~ /* TODO Actually detach session... */
149
        //~ int error = 0;
150
        //~ plugin_t->destroy_session(handle, &error);
151
        //~ if(error) {        /* TODO Make error struct to pass verbose information */
152
                //~ g_hash_table_remove(session->ice_handles, GUINT_TO_POINTER(handle_id));
153
                //~ ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_PLUGIN_DETACH, "Couldn't detach from plugin: error '%d'", error);
154
                //~ /* TODO Delete handle instance */
155
                //~ goto jsondone;
156
        //~ }
157
        //~ g_hash_table_remove(session, handle);
158
        g_hash_table_remove(sessions, GUINT_TO_POINTER(session_id));
159
        /* TODO Actually destroy session */
160
        return 0;
161
}
162

    
163

    
164
/* WebServer requests handler */
165
int janus_ws_handler(void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **ptr)
166
{
167
        char *payload = NULL;
168
        struct MHD_Response *response = NULL;
169
        int ret = MHD_NO;
170

    
171
        JANUS_PRINT("Got a HTTP %s request on %s...\n", method, url);
172
        /* Is this the first round? */
173
        int firstround = 0;
174
        janus_http_msg *msg = (janus_http_msg *)*ptr;
175
        if (msg == NULL) {
176
                firstround = 1;
177
                JANUS_PRINT(" ... Just parsing headers for now...\n");
178
                msg = calloc(1, sizeof(janus_http_msg));
179
                if(msg == NULL) {
180
                        JANUS_DEBUG("Memory error!\n");
181
                        ret = MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response);
182
                        MHD_destroy_response(response);
183
                        goto done;
184
                }
185
                msg->acrh = NULL;
186
                msg->acrm = NULL;
187
                msg->payload = NULL;
188
                msg->len = 0;
189
                msg->session_id = 0;
190
                *ptr = msg;
191
                MHD_get_connection_values(connection, MHD_HEADER_KIND, &janus_ws_headers, msg);
192
                ret = MHD_YES;
193
        }
194
        /* Parse request */
195
        if (strcasecmp(method, "GET") && strcasecmp(method, "POST") && strcasecmp(method, "OPTIONS")) {
196
                JANUS_DEBUG("Unsupported method...\n");
197
                response = MHD_create_response_from_data(0, NULL, MHD_NO, MHD_NO);
198
                MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
199
                if(msg->acrm)
200
                        MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
201
                if(msg->acrh)
202
                        MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
203
                ret = MHD_queue_response(connection, MHD_HTTP_NOT_IMPLEMENTED, response);
204
                MHD_destroy_response(response);
205
                return ret;
206
        }
207
        if (!strcasecmp(method, "OPTIONS")) {
208
                response = MHD_create_response_from_data(0, NULL, MHD_NO, MHD_NO); 
209
                MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
210
                if(msg->acrm)
211
                        MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
212
                if(msg->acrh)
213
                        MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
214
                ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
215
                MHD_destroy_response(response);
216
        }
217
        /* Get path components */
218
        gchar **basepath = NULL, **path = NULL;
219
        if(strcasecmp(url, ws_path)) {
220
                basepath = g_strsplit(url, ws_path, -1);
221
                if(basepath[1] == NULL || basepath[1][0] != '/') {
222
                        JANUS_DEBUG("Invalid url %s (%s)\n", url, basepath[1]);
223
                        response = MHD_create_response_from_data(0, NULL, MHD_NO, MHD_NO);
224
                        MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
225
                        if(msg->acrm)
226
                                MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
227
                        if(msg->acrh)
228
                                MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
229
                        ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
230
                        MHD_destroy_response(response);
231
                }
232
                if(firstround)
233
                        return ret;
234
                path = g_strsplit(basepath[1], "/", -1);
235
                if(path == NULL || path[1] == NULL) {
236
                        JANUS_DEBUG("Invalid path %s (%s)\n", basepath[1], path[1]);
237
                        response = MHD_create_response_from_data(0, NULL, MHD_NO, MHD_NO);
238
                        MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
239
                        if(msg->acrm)
240
                                MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
241
                        if(msg->acrh)
242
                                MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
243
                        ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
244
                        MHD_destroy_response(response);
245
                }
246
        }
247
        if(firstround)
248
                return ret;
249
        JANUS_PRINT(" ... parsing request...\n");
250
        gchar *session_path = NULL, *handle_path = NULL;
251
        if(path != NULL && path[1] != NULL && strlen(path[1]) > 0) {
252
                session_path = g_strdup(path[1]);
253
                if(session_path == NULL) {
254
                        JANUS_DEBUG("Memory error!\n");
255
                        ret = MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response);
256
                        MHD_destroy_response(response);
257
                        goto done;
258
                }
259
                JANUS_PRINT("Session: %s\n", session_path);
260
        }
261
        if(session_path != NULL && path[2] != NULL && strlen(path[2]) > 0) {
262
                handle_path = g_strdup(path[2]);
263
                if(handle_path == NULL) {
264
                        JANUS_DEBUG("Memory error!\n");
265
                        ret = MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response);
266
                        MHD_destroy_response(response);
267
                        goto done;
268
                }
269
                JANUS_PRINT("Handle: %s\n", handle_path);
270
        }
271
        if(session_path != NULL && handle_path != NULL && path[3] != NULL && strlen(path[3]) > 0) {
272
                JANUS_DEBUG("Too many components...\n");
273
                response = MHD_create_response_from_data(0, NULL, MHD_NO, MHD_NO);
274
                MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
275
                if(msg->acrm)
276
                        MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
277
                if(msg->acrh)
278
                        MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
279
                ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
280
                MHD_destroy_response(response);
281
                goto done;
282
        }
283
        /* Get payload, if any */
284
        if(!strcasecmp(method, "POST")) {
285
                JANUS_PRINT("Processing POST data (%s)...\n", msg->contenttype);
286
                if(*upload_data_size != 0) {
287
                        //~ JANUS_PRINT("  -- Uploaded data (%s, %zu bytes):\n%s\n", msg->contenttype, *upload_data_size, upload_data);
288
                        JANUS_PRINT("  -- Uploaded data (%zu bytes)\n", *upload_data_size);
289
                        if(msg->payload == NULL)
290
                                msg->payload = calloc(1, *upload_data_size+1);
291
                        else
292
                                msg->payload = realloc(msg->payload, msg->len+*upload_data_size+1);
293
                        if(msg->payload == NULL) {
294
                                JANUS_DEBUG("Memory error!\n");
295
                                ret = MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response);
296
                                MHD_destroy_response(response);
297
                                goto done;
298
                        }
299
                        memcpy(msg->payload+msg->len, upload_data, *upload_data_size);
300
                        memset(msg->payload+msg->len+*upload_data_size, '\0', 1);
301
                        msg->len += *upload_data_size;
302
                        //~ JANUS_PRINT("  -- Data we have now (%zu bytes):\n%s\n", msg->len, msg->payload);
303
                        JANUS_PRINT("  -- Data we have now (%zu bytes)\n", msg->len);
304
                        *upload_data_size = 0;        /* Go on */
305
                        ret = MHD_YES;
306
                        goto done;
307
                }
308
                JANUS_PRINT("Done getting payload, we can answer\n");
309
                if(msg->payload == NULL) {
310
                        JANUS_DEBUG("No payload :-(\n");
311
                        ret = MHD_NO;
312
                        goto done;
313
                }
314
                payload = msg->payload;
315
                JANUS_DEBUG("%s\n", payload);
316
        }
317
        if(session_path == NULL && handle_path == NULL) {
318
                /* Can only be a 'Create new session' request */
319
                if(!strcasecmp(method, "GET")) {
320
                        ret = janus_ws_error(connection, msg, NULL, JANUS_ERROR_USE_POST, "Use POST to create a session");
321
                        goto done;
322
                }
323
                if(!strcasecmp(method, "POST") && !payload) {
324
                        ret = janus_ws_error(connection, msg, NULL, JANUS_ERROR_MISSING_REQUEST, "JSON error: missing request");
325
                        goto done;
326
                }
327
                json_error_t error;
328
                json_t *root = json_loads(payload, 0, &error);
329
                if(!root) {
330
                        ret = janus_ws_error(connection, msg, NULL, JANUS_ERROR_INVALID_JSON, "JSON error: on line %d: %s", error.line, error.text);
331
                        goto done;
332
                }
333
                if(!json_is_object(root)) {
334
                        ret = janus_ws_error(connection, msg, NULL, JANUS_ERROR_INVALID_JSON_OBJECT, "JSON error: not an object");
335
                        json_decref(root);
336
                        goto done;
337
                }
338
                json_t *transaction = json_object_get(root, "transaction");
339
                if(!transaction || !json_is_string(transaction)) {
340
                        ret = janus_ws_error(connection, msg, NULL, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "JSON error: missing mandatory element (transaction)");
341
                        goto done;
342
                }
343
                const gchar *transaction_text = json_string_value(transaction);
344
                json_t *message = json_object_get(root, "janus");
345
                if(!message || !json_is_string(message)) {
346
                        ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "JSON error: missing mandatory element (janus)");
347
                        json_decref(root);
348
                        goto done;
349
                }
350
                const gchar *message_text = json_string_value(message);
351
                if(strcasecmp(message_text, "create")) {
352
                        ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
353
                        json_decref(root);
354
                        goto done;
355
                }
356
                /* Handle it */
357
                janus_session *session = janus_session_create();
358
                if(session == NULL) {
359
                        ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_UNKNOWN, "Memory error");
360
                        json_decref(root);
361
                        goto done;
362
                }
363
                guint64 session_id = session->session_id;
364
                /* Prepare JSON reply */
365
                json_t *reply = json_object();
366
                json_object_set(reply, "janus", json_string("success"));
367
                json_object_set(reply, "transaction", json_string(transaction_text));
368
                json_t *data = json_object();
369
                json_object_set(data, "id", json_integer(session_id));
370
                json_object_set(reply, "data", data);
371
                /* Convert to a string */
372
                char *reply_text = json_dumps(reply, JSON_INDENT(3));
373
                json_decref(root);
374
                json_decref(data);
375
                json_decref(reply);
376
                /* Send the success reply */
377
                ret = janus_ws_success(connection, msg, "application/json", reply_text);
378
                goto done;
379
        }
380
        guint64 session_id = g_ascii_strtoll(session_path, NULL, 10);
381
        if(session_id < 1) {
382
                JANUS_DEBUG("Invalid session %s\n", session_path);
383
                response = MHD_create_response_from_data(0, NULL, MHD_NO, MHD_NO);
384
                MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
385
                if(msg->acrm)
386
                        MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
387
                if(msg->acrh)
388
                        MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
389
                ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
390
                MHD_destroy_response(response);
391
                goto done;
392
        }
393
        msg->session_id = session_id;
394
        guint64 handle_id = 0;
395
        if(handle_path) {
396
                handle_id = g_ascii_strtoll(handle_path, NULL, 10);
397
                if(handle_id < 1) {
398
                        JANUS_DEBUG("Invalid handle %s\n", handle_path);
399
                        response = MHD_create_response_from_data(0, NULL, MHD_NO, MHD_NO);
400
                        MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
401
                        if(msg->acrm)
402
                                MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
403
                        if(msg->acrh)
404
                                MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
405
                        ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
406
                        MHD_destroy_response(response);
407
                        goto done;
408
                }
409
        }
410
        if(!strcasecmp(method, "GET") || !payload) {
411
                if(handle_path) {
412
                        char location[50];
413
                        g_sprintf(location, "%s/%s", ws_path, session_path);
414
                        JANUS_DEBUG("Invalid GET to %s, redirecting to %s\n", url, location);
415
                        response = MHD_create_response_from_data(0, NULL, MHD_NO, MHD_NO);
416
                        MHD_add_response_header(response, "Location", location);
417
                        MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
418
                        if(msg->acrm)
419
                                MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
420
                        if(msg->acrh)
421
                                MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
422
                        ret = MHD_queue_response(connection, 302, response);
423
                        MHD_destroy_response(response);
424
                        goto done;
425
                }
426
                janus_session *session = janus_session_find(session_id);
427
                if(!session) {
428
                        JANUS_DEBUG("Couldn't find any session %"SCNu64"...\n", session_id);
429
                        response = MHD_create_response_from_data(0, NULL, MHD_NO, MHD_NO);
430
                        MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
431
                        if(msg->acrm)
432
                                MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
433
                        if(msg->acrh)
434
                                MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
435
                        ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
436
                        MHD_destroy_response(response);
437
                        goto done;
438
                }
439
                JANUS_PRINT("Session %"SCNu64" found... returning message\n", session->session_id);
440
                /* Handle GET, taking the first message from the list */
441
                janus_http_event *event = g_queue_pop_head(session->messages);
442
                if(event != NULL) {
443
                        ret = janus_ws_success(connection, msg, "application/json", event->payload);
444
                } else {
445
                        /* Still no message, wait */
446
                        ret = janus_ws_notifier(connection, msg);
447
                }
448
                goto done;
449
        }
450

    
451
        json_error_t error;
452
        json_t *root = json_loads(payload, 0, &error);
453
        if(!root) {
454
                ret = janus_ws_error(connection, msg, NULL, JANUS_ERROR_INVALID_JSON, "JSON error: on line %d: %s", error.line, error.text);
455
                goto done;
456
        }
457
        if(!json_is_object(root)) {
458
                ret = janus_ws_error(connection, msg, NULL, JANUS_ERROR_INVALID_JSON_OBJECT, "JSON error: not an object");
459
                goto jsondone;
460
        }
461
        json_t *transaction = json_object_get(root, "transaction");
462
        if(!transaction || !json_is_string(transaction)) {
463
                ret = janus_ws_error(connection, msg, NULL, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "JSON error: missing mandatory element (transaction)");
464
                goto jsondone;
465
        }
466
        const gchar *transaction_text = json_string_value(transaction);
467
        json_t *message = json_object_get(root, "janus");
468
        if(!message || !json_is_string(message)) {
469
                ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "JSON error: missing mandatory element (janus)");
470
                goto jsondone;
471
        }
472
        const gchar *message_text = json_string_value(message);
473

    
474
        /* If we got here, it's a POST, make sure we have a session (and a handle) */
475
        janus_session *session = janus_session_find(session_id);
476
        if(!session) {
477
                JANUS_DEBUG("Couldn't find any session %"SCNu64"...\n", session_id);
478
                ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, "No such session %"SCNu64"", session_id);
479
                goto done;
480
        }
481
        janus_ice_handle *handle = NULL;
482
        if(handle_id > 0) {
483
                handle = janus_ice_handle_find(session, handle_id);
484
                if(!handle) {
485
                        JANUS_DEBUG("Couldn't find any handle %"SCNu64" in session %"SCNu64"...\n", handle_id, session_id);
486
                        ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_HANDLE_NOT_FOUND, "No such handle %"SCNu64" in session %"SCNu64"", handle_id, session_id);
487
                        goto done;
488
                }
489
        }
490

    
491
        /* What is this? */
492
        if(!strcasecmp(message_text, "attach")) {
493
                if(handle != NULL) {
494
                        /* Attach is a session-level command */
495
                        ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
496
                        goto jsondone;
497
                }
498
                json_t *plugin = json_object_get(root, "plugin");
499
                if(!plugin || !json_is_string(plugin)) {
500
                        ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "JSON error: missing mandatory element (plugin)");
501
                        goto jsondone;
502
                }
503
                const gchar *plugin_text = json_string_value(plugin);
504
                janus_plugin *plugin_t = janus_plugin_find(plugin_text);
505
                if(plugin_t == NULL) {
506
                        ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_PLUGIN_NOT_FOUND, "No such plugin '%s'", plugin_text);
507
                        goto jsondone;
508
                }
509
                /* Create handle */
510
                handle = janus_ice_handle_create(session);
511
                if(handle == NULL) {
512
                        ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_UNKNOWN, "Memory error");
513
                        goto done;
514
                }
515
                handle_id = handle->handle_id;
516
                /* Attach to the plugin */
517
                int error = 0;
518
                if((error = janus_ice_handle_attach_plugin(session, handle_id, plugin_t)) != 0) {
519
                        /* TODO Make error struct to pass verbose information */
520
                        janus_ice_handle_destroy(session, handle_id);
521
                        ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_PLUGIN_ATTACH, "Couldn't attach to plugin: error '%d'", error);
522
                        goto jsondone;
523
                }
524
                /* Prepare JSON reply */
525
                json_t *reply = json_object();
526
                json_object_set(reply, "janus", json_string("success"));
527
                json_object_set(reply, "transaction", json_string(transaction_text));
528
                json_t *data = json_object();
529
                json_object_set(data, "id", json_integer(handle_id));
530
                json_object_set(reply, "data", data);
531
                /* Convert to a string */
532
                char *reply_text = json_dumps(reply, JSON_INDENT(3));
533
                json_decref(data);
534
                json_decref(reply);
535
                /* Send the success reply */
536
                ret = janus_ws_success(connection, msg, "application/json", reply_text);
537
        } else if(!strcasecmp(message_text, "destroy")) {
538
                if(handle != NULL) {
539
                        /* Query is a session-level command */
540
                        ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
541
                        goto jsondone;
542
                }
543
                janus_session_destroy(session_id);        /* FIXME Should we check if this actually succeeded, or can we ignore it? */
544
                /* Prepare JSON reply */
545
                json_t *reply = json_object();
546
                json_object_set(reply, "janus", json_string("success"));
547
                json_object_set(reply, "transaction", json_string(transaction_text));
548
                /* Convert to a string */
549
                char *reply_text = json_dumps(reply, JSON_INDENT(3));
550
                json_decref(reply);
551
                /* Send the success reply */
552
                ret = janus_ws_success(connection, msg, "application/json", reply_text);
553
        } else if(!strcasecmp(message_text, "detach")) {
554
                if(handle == NULL) {
555
                        /* Query is an handle-level command */
556
                        ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
557
                        goto jsondone;
558
                }
559
                if(handle->app == NULL || handle->app_handle == NULL) {
560
                        ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_PLUGIN_DETACH, "No plugin to detach from");
561
                        goto jsondone;
562
                }
563
                int error = 0;
564
                if((error = janus_ice_handle_destroy(session, handle_id)) != 0) {
565
                        /* TODO Make error struct to pass verbose information */
566
                        ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_PLUGIN_DETACH, "Couldn't detach from plugin: error '%d'", error);
567
                        /* TODO Delete handle instance */
568
                        goto jsondone;
569
                }
570
                /* Prepare JSON reply */
571
                json_t *reply = json_object();
572
                json_object_set(reply, "janus", json_string("success"));
573
                json_object_set(reply, "transaction", json_string(transaction_text));
574
                /* Convert to a string */
575
                char *reply_text = json_dumps(reply, JSON_INDENT(3));
576
                json_decref(reply);
577
                /* Send the success reply */
578
                ret = janus_ws_success(connection, msg, "application/json", reply_text);
579
        } else if(!strcasecmp(message_text, "message")) {
580
                if(handle == NULL) {
581
                        /* Query is an handle-level command */
582
                        ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
583
                        goto jsondone;
584
                }
585
                if(handle->app == NULL || handle->app_handle == NULL) {
586
                        ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "No plugin to handle this message");
587
                        goto jsondone;
588
                }
589
                janus_plugin *plugin_t = (janus_plugin *)handle->app;
590
                JANUS_PRINT("There's a message for %s\n", plugin_t->get_name());
591
                json_t *body = json_object_get(root, "body");
592
                if(body == NULL) {
593
                        ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_INVALID_JSON, "JSON error: missing mandatory element (body)");
594
                        goto jsondone;
595
                }
596
                if(!json_is_object(body)) {
597
                        ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_INVALID_JSON_OBJECT, "Invalid body object");
598
                        //~ json_decref(body);
599
                        goto jsondone;
600
                }
601
                /* Is there an SDP attached? */
602
                json_t *jsep = json_object_get(root, "jsep");
603
                char *jsep_type = NULL;
604
                char *jsep_sdp = NULL, *jsep_sdp_stripped = NULL;
605
                if(jsep != NULL) {
606
                        if(!json_is_object(jsep)) {
607
                                ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_INVALID_JSON_OBJECT, "Invalid jsep object");
608
                                goto jsondone;
609
                        }
610
                        json_t *type = json_object_get(jsep, "type");
611
                        if(!type || !json_is_string(type)) {
612
                                ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "JSEP error: missing mandatory element (type)");
613
                                goto jsondone;
614
                        }
615
                        jsep_type = g_strdup(json_string_value(type));
616
                        if(jsep_type == NULL) {
617
                                JANUS_DEBUG("Memory error!\n");
618
                                ret = MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response);
619
                                MHD_destroy_response(response);
620
                                goto done;
621
                        }
622
                        type = NULL;
623
                        /* Check the JSEP type */
624
                        int offer = 0;
625
                        if(!strcasecmp(jsep_type, "offer")) {
626
                                offer = 1;
627
                        } else if(!strcasecmp(jsep_type, "answer")) {
628
                                offer = 0;
629
                        } else {
630
                                /* TODO Handle other message types as well */
631
                                ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_JSEP_UNKNOWN_TYPE, "JSEP error: unknown message type '%s'", jsep_type);
632
                                g_free(jsep_type);
633
                                goto jsondone;
634
                        }
635
                        json_t *sdp = json_object_get(jsep, "sdp");
636
                        if(!sdp || !json_is_string(sdp)) {
637
                                ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "JSEP error: missing mandatory element (sdp)");
638
                                g_free(jsep_type);
639
                                goto jsondone;
640
                        }
641
                        jsep_sdp = (char *)json_string_value(sdp);
642
                        JANUS_PRINT("Remote SDP:\n%s", jsep_sdp);
643
                        /* Is this valid SDP? */
644
                        int audio = 0, video = 0;
645
                        janus_sdp *parsed_sdp = janus_sdp_preparse(jsep_sdp, &audio, &video);
646
                        if(parsed_sdp == NULL) {
647
                                /* Invalid SDP */
648
                                ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_JSEP_INVALID_SDP, "JSEP error: invalid SDP");
649
                                //~ json_decref(body);
650
                                g_free(jsep_type);
651
                                //~ g_free(jsep_sdp);
652
                                goto jsondone;
653
                        }
654
                        /* FIXME We're only handling single audio/video lines for now... */
655
                        if(audio > 1)
656
                                JANUS_DEBUG("More than one audio line? only going to negotiate one...\n");
657
                        if(video > 1)
658
                                JANUS_DEBUG("More than one video line? only going to negotiate one...\n");
659
                        if(offer) {
660
                                /* Setup ICE locally (we received an offer) */
661
                                janus_ice_setup_local(handle, offer, audio, video);
662
                        }
663
                        janus_sdp_parse(handle, parsed_sdp);
664
                        janus_sdp_free(parsed_sdp);
665
                        if(!offer) {
666
                                JANUS_PRINT("Done! Sending connectivity checks...\n");
667
                                /* Set remote candidates now */
668
                                if(handle->audio_id > 0) {
669
                                        janus_ice_setup_remote_candidate(handle, handle->audio_id, 1);
670
                                        janus_ice_setup_remote_candidate(handle, handle->audio_id, 2);
671
                                }
672
                                if(handle->video_id > 0) {
673
                                        janus_ice_setup_remote_candidate(handle, handle->video_id, 1);
674
                                        janus_ice_setup_remote_candidate(handle, handle->video_id, 2);
675
                                }
676
                        }
677
                        /* Anonymize SDP */
678
                        jsep_sdp_stripped = janus_sdp_anonymize(jsep_sdp);
679
                        if(jsep_sdp_stripped == NULL) {
680
                                /* Invalid SDP */
681
                                ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_JSEP_INVALID_SDP, "JSEP error: invalid SDP");
682
                                //~ json_decref(body);
683
                                g_free(jsep_type);
684
                                //~ g_free(jsep_sdp);
685
                                goto jsondone;
686
                        }
687
                        sdp = NULL;
688
                }
689
                char *body_text = json_dumps(body, JSON_INDENT(3));
690
                //~ json_decref(body);
691
                plugin_t->handle_message(handle->app_handle, (char *)transaction_text, body_text, jsep_type, jsep_sdp_stripped);
692
                /* We reply right away, not to block the web server... */
693
                json_t *reply = json_object();
694
                json_object_set(reply, "janus", json_string("ack"));
695
                json_object_set(reply, "transaction", json_string(transaction_text));
696
                /* Convert to a string */
697
                char *reply_text = json_dumps(reply, JSON_INDENT(3));
698
                json_decref(reply);
699
                /* Send the success reply */
700
                ret = janus_ws_success(connection, msg, "application/json", reply_text);
701
        } else {
702
                ret = janus_ws_error(connection, msg, transaction_text, JANUS_ERROR_UNKNOWN_REQUEST, "Unknown request '%s'", message_text);
703
        }
704

    
705
jsondone:
706
        json_decref(root);
707
        
708
done:
709
        g_strfreev(path);
710
        g_free(session_path);
711
        g_free(handle_path);
712
        return ret;
713
}
714

    
715
int janus_ws_headers(void *cls, enum MHD_ValueKind kind, const char *key, const char *value) {
716
        janus_http_msg *request = cls;
717
        JANUS_PRINT("%s: %s\n", key, value);
718
        if(!strcasecmp(key, MHD_HTTP_HEADER_CONTENT_TYPE)) {
719
                if(request)
720
                        request->contenttype = strdup(value);
721
        } else if(!strcasecmp(key, "Access-Control-Request-Method")) {
722
                if(request)
723
                        request->acrm = strdup(value);
724
        } else if(!strcasecmp(key, "Access-Control-Request-Headers")) {
725
                if(request)
726
                        request->acrh = strdup(value);
727
        }
728
        return MHD_YES;
729
}
730

    
731
void janus_ws_request_completed(void *cls, struct MHD_Connection *connection, void **con_cls, enum MHD_RequestTerminationCode toe) {
732
        JANUS_PRINT("Request completed, freeing data\n");
733
        janus_http_msg *request = *con_cls;
734
        if(!request)
735
                return;
736
        if(request->payload != NULL)
737
                free(request->payload);
738
        if(request->contenttype != NULL)
739
                free(request->contenttype);
740
        if(request->acrh != NULL)
741
                free(request->acrh);
742
        if(request->acrm != NULL)
743
                free(request->acrm);
744
        free(request);
745
        *con_cls = NULL;   
746
}
747

    
748
/* Worker to handle notifications */
749
int janus_ws_notifier(struct MHD_Connection *connection, janus_http_msg *msg) {
750
        if(!connection || !msg)
751
                return MHD_NO;
752
        JANUS_PRINT("... handling long poll...\n");
753
        janus_http_event *event = NULL;
754
        struct MHD_Response *response = NULL;
755
        int ret = MHD_NO;
756
        guint64 session_id = msg->session_id;
757
        janus_session *session = janus_session_find(session_id);
758
        if(!session) {
759
                JANUS_DEBUG("Couldn't find any session %"SCNu64"...\n", session_id);
760
                response = MHD_create_response_from_data(0, NULL, MHD_NO, MHD_NO);
761
                MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
762
                if(msg->acrm)
763
                        MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
764
                if(msg->acrh)
765
                        MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
766
                ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
767
                MHD_destroy_response(response);
768
                return ret;
769
        }
770
        gint64 start = janus_get_monotonic_time();
771
        gint64 end = 0;
772
        /* We have a timeout for the long poll: 30 seconds */
773
        while(end-start < 30*G_USEC_PER_SEC) {
774
                event = g_queue_pop_head(session->messages);
775
                if(stop || event != NULL) {
776
                        /* Gotcha! */
777
                        break;
778
                }
779
                /* Sleep 100ms */
780
                g_usleep(100000);
781
                end = janus_get_monotonic_time();
782
        }
783
        if(event == NULL || event->payload == NULL) {
784
                JANUS_PRINT("Long poll time out for session %"SCNu64"...\n", session_id);
785
                event = (janus_http_event *)calloc(1, sizeof(janus_http_event));
786
                if(event == NULL) {
787
                        JANUS_DEBUG("Memory error!\n");
788
                        ret = MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response);
789
                        MHD_destroy_response(response);
790
                        return ret;
791
                }
792
                event->code = 200;
793
                /*! \todo Improve the Janus protocol keep-alive mechanism in JavaScript */
794
                event->payload = "{\"janus\" : \"keepalive\"}";
795
                event->allocated = 0;
796
        }
797
        /* Finish the request by sending the response */
798
        JANUS_PRINT("We have a message to serve...\n\t%s\n", event->payload);
799
        //~ if(session->destroy) {
800
                //~ JANUS_PRINT("Destroying session %"SCNu64" as well\n", session->session_id);
801
                //~ g_hash_table_remove(sessions, GUINT_TO_POINTER(session->session_id));
802
                //~ /* TODO Actually remove session */
803
        //~ }
804
        /* Send event */
805
        char *payload = g_strdup(event->payload);
806
        if(payload == NULL) {
807
                JANUS_DEBUG("Memory error!\n");
808
                ret = MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response);
809
                MHD_destroy_response(response);
810
                return ret;
811
        }
812
        ret = janus_ws_success(connection, msg, NULL, payload);
813
        if(event->payload && event->allocated) {
814
                g_free(event->payload);
815
                event->payload = NULL;
816
        }
817
        g_free(event);
818
        return ret;
819
}
820

    
821
int janus_ws_success(struct MHD_Connection *connection, janus_http_msg *msg, const char *transaction, char *payload)
822
{
823
        if(!connection || !msg || !payload)
824
                return MHD_NO;
825
        /* Send the reply */
826
        struct MHD_Response *response = MHD_create_response_from_data(
827
                strlen(payload),
828
                (void*) payload,
829
                MHD_YES,
830
                MHD_NO);
831
        MHD_add_response_header(response, "Content-Type", "application/json");
832
        MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
833
        if(msg->acrm)
834
                MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
835
        if(msg->acrh)
836
                MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
837
        int ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
838
        MHD_destroy_response(response);
839
        return ret;
840
}
841

    
842
int janus_ws_error(struct MHD_Connection *connection, janus_http_msg *msg, const char *transaction, gint error, const char *format, ...)
843
{
844
        if(!connection || !msg)
845
                return MHD_NO;
846
        gchar *error_string = NULL;
847
        if(format == NULL) {
848
                /* No error string provided, use the default one */
849
                error_string = (gchar *)janus_get_api_error(error);
850
        } else {
851
                /* This callback has variable arguments (error string) */
852
                va_list ap;
853
                va_start(ap, format);
854
                /* FIXME 512 should be enough, but anyway... */
855
                error_string = calloc(512, sizeof(char));
856
                if(error_string == NULL) {
857
                        JANUS_DEBUG("Memory error!\n");
858
                        struct MHD_Response *response = MHD_create_response_from_data(0, NULL, MHD_NO, MHD_NO);
859
                        int ret = MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response);
860
                        MHD_destroy_response(response);
861
                        return ret;
862
                }
863
                vsprintf(error_string, format, ap);
864
                va_end(ap);
865
        }
866
        /* Done preparing error */
867
        JANUS_PRINT("[ws][%s] Returning error %d (%s)\n", transaction, error, error_string ? error_string : "no text");
868
        /* Prepare JSON error */
869
        json_t *reply = json_object();
870
        json_object_set(reply, "janus", json_string("error"));
871
        if(transaction != NULL)
872
                json_object_set(reply, "transaction", json_string(transaction));
873
        json_t *error_data = json_object();
874
        json_object_set(error_data, "code", json_integer(error));
875
        json_object_set(error_data, "reason", json_string(error_string ? error_string : "no text"));
876
        json_object_set(reply, "error", error_data);
877
        /* Convert to a string */
878
        char *reply_text = json_dumps(reply, JSON_INDENT(3));
879
        json_decref(reply);
880
        if(format != NULL && error_string != NULL)
881
                free(error_string);
882
        /* Send the error */
883
        struct MHD_Response *response = MHD_create_response_from_data(
884
                strlen(reply_text),
885
                (void*)reply_text,
886
                MHD_YES,
887
                MHD_NO);
888
        MHD_add_response_header(response, "Content-Type", "application/json");
889
        MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
890
        if(msg->acrm)
891
                MHD_add_response_header(response, "Access-Control-Allow-Methods", msg->acrm);
892
        if(msg->acrh)
893
                MHD_add_response_header(response, "Access-Control-Allow-Headers", msg->acrh);
894
        int ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
895
        MHD_destroy_response(response);
896
        return ret;
897
}
898

    
899

    
900
/* Plugins */
901
void janus_plugin_close(gpointer key, gpointer value, gpointer user_data) {
902
        janus_plugin *plugin = (janus_plugin *)value;
903
        if(!plugin)
904
                return;
905
        plugin->destroy();
906
}
907

    
908
void janus_pluginso_close(gpointer key, gpointer value, gpointer user_data) {
909
        void *plugin = (janus_plugin *)value;
910
        if(!plugin)
911
                return;
912
        dlclose(plugin);
913
}
914

    
915
janus_plugin *janus_plugin_find(const gchar *package) {
916
        if(package != NULL && plugins != NULL)        /* FIXME Do we need to fix the key pointer? */
917
                return g_hash_table_lookup(plugins, package);
918
        return NULL;
919
}
920

    
921

    
922
/* Plugin callback interface */
923
int janus_push_event(janus_pluginession *handle, janus_plugin *plugin, char *transaction, char *message, char *sdp_type, char *sdp) {
924
        if(!handle || !plugin || !message)
925
                return -1;
926
        janus_ice_handle *ice_handle = (janus_ice_handle *)handle->gateway_handle;
927
        if(!ice_handle)
928
                return JANUS_ERROR_SESSION_NOT_FOUND;
929
        janus_session *session = ice_handle->session;
930
        if(!session)
931
                return JANUS_ERROR_SESSION_NOT_FOUND;
932
        /* Make sure this is JSON */
933
        json_error_t error;
934
        json_t *event = json_loads(message, 0, &error);
935
        if(!event) {
936
                JANUS_DEBUG("[%"SCNu64"] Cannot push event (JSON error: on line %d: %s)\n", ice_handle->handle_id, error.line, error.text);
937
                return JANUS_ERROR_INVALID_JSON;
938
        }
939
        if(!json_is_object(event)) {
940
                JANUS_DEBUG("[%"SCNu64"] Cannot push event (JSON error: not an object)\n", ice_handle->handle_id);
941
                return JANUS_ERROR_INVALID_JSON_OBJECT;
942
        }
943
        /* Attach JSEP if possible? */
944
        json_t *jsep = NULL;
945
        if(sdp_type != NULL && sdp != NULL) {
946
                jsep = janus_handle_sdp(handle, plugin, sdp_type, sdp);
947
                if(jsep == NULL) {
948
                        JANUS_DEBUG("[%"SCNu64"] Cannot push event (JSON error: problem with the SDP)\n", ice_handle->handle_id);
949
                        return JANUS_ERROR_JSEP_INVALID_SDP;
950
                }
951
        }
952
        /* Prepare JSON event */
953
        json_t *reply = json_object();
954
        json_object_set(reply, "janus", json_string("event"));
955
        json_object_set(reply, "sender", json_integer(ice_handle->handle_id));
956
        if(transaction != NULL)
957
                json_object_set(reply, "transaction", json_string(transaction));
958
        json_t *plugin_data = json_object();
959
        json_object_set(plugin_data, "plugin", json_string(plugin->get_package()));
960
        json_object_set(plugin_data, "data", event);
961
        json_object_set(reply, "plugindata", plugin_data);
962
        if(jsep != NULL)
963
                json_object_set(reply, "jsep", jsep);
964
        /* Convert to a string */
965
        char *reply_text = json_dumps(reply, JSON_INDENT(3));
966
        json_decref(event);
967
        json_decref(plugin_data);
968
        if(jsep != NULL)
969
                json_decref(jsep);
970
        json_decref(reply);
971
        /* Send the event */
972
        JANUS_PRINT("[%"SCNu64"] Adding event to queue of messages...\n", ice_handle->handle_id);
973
        janus_http_event *notification = (janus_http_event *)calloc(1, sizeof(janus_http_event));
974
        if(notification == NULL) {
975
                JANUS_DEBUG("Memory error!\n");
976
                return JANUS_ERROR_UNKNOWN;        /* FIXME Do we need something like "Internal Server Error"? */
977
        }
978
        notification->code = 200;
979
        notification->payload = reply_text;
980
        notification->allocated = 1;
981
        g_queue_push_tail(session->messages, notification);
982
        return JANUS_OK;
983
}
984

    
985
json_t *janus_handle_sdp(janus_pluginession *handle, janus_plugin *plugin, char *sdp_type, char *sdp) {
986
        if(handle == NULL || plugin == NULL || sdp_type == NULL || sdp == NULL)
987
                return NULL;
988
        int offer = 0;
989
        if(!strcasecmp(sdp_type, "offer")) {
990
                /* This is an offer from a plugin */
991
                offer = 1;
992
        } else if(!strcasecmp(sdp_type, "answer")) {
993
                /* This is an answer from a plugin */
994
        } else {
995
                /* TODO Handle other messages */
996
                return NULL;
997
        }
998
        janus_ice_handle *ice_handle = (janus_ice_handle *)handle->gateway_handle;
999
        /* Is this valid SDP? */
1000
        int audio = 0, video = 0;
1001
        if(janus_sdp_preparse(sdp, &audio, &video) < 0) {
1002
                return NULL;
1003
        }
1004
        if(offer) {
1005
                /* We still don't have a local ICE setup */
1006
                if(audio > 1)
1007
                        JANUS_DEBUG("[%"SCNu64"] More than one audio line? only going to negotiate one...\n", ice_handle->handle_id);
1008
                if(video > 1)
1009
                        JANUS_DEBUG("[%"SCNu64"] More than one video line? only going to negotiate one...\n", ice_handle->handle_id);
1010
                /* Process SDP in order to setup ICE locally (this is going to result in an answer from the browser) */
1011
                janus_ice_setup_local(ice_handle, 0, audio, video);
1012
        }
1013
        /* Wait for candidates-done callback */
1014
        while(ice_handle->cdone < ice_handle->streams_num) {
1015
                JANUS_PRINT("[%"SCNu64"] Waiting for candidates-done callback...\n", ice_handle->handle_id);
1016
                g_usleep(100000);
1017
                if(ice_handle->cdone < 0) {
1018
                        JANUS_DEBUG("[%"SCNu64"] Error gathering candidates!\n", ice_handle->handle_id);
1019
                        return NULL;
1020
                }
1021
        }
1022
        /* Anonymize SDP */
1023
        char *sdp_stripped = janus_sdp_anonymize(sdp);
1024
        if(sdp_stripped == NULL) {
1025
                /* Invalid SDP */
1026
                return NULL;
1027
        }
1028
        /* Add our details */
1029
        char *sdp_merged = janus_sdp_merge(ice_handle, sdp_stripped);
1030
        if(sdp_merged == NULL) {
1031
                /* Couldn't merge SDP */
1032
                g_free(sdp_stripped);
1033
                return NULL;
1034
        }
1035

    
1036
        if(!offer) {
1037
                JANUS_PRINT("[%"SCNu64"] Done! Ready to setup remote candidates and send connectivity checks...\n", ice_handle->handle_id);
1038
                /* Set remote candidates now */
1039
                if(ice_handle->audio_id > 0) {
1040
                        janus_ice_setup_remote_candidate(ice_handle, ice_handle->audio_id, 1);
1041
                        janus_ice_setup_remote_candidate(ice_handle, ice_handle->audio_id, 2);
1042
                }
1043
                if(ice_handle->video_id > 0) {
1044
                        janus_ice_setup_remote_candidate(ice_handle, ice_handle->video_id, 1);
1045
                        janus_ice_setup_remote_candidate(ice_handle, ice_handle->video_id, 2);
1046
                }
1047
        }
1048
        
1049
        /* Prepare JSON event */
1050
        json_t *jsep = json_object();
1051
        json_object_set(jsep, "type", json_string(sdp_type));
1052
        json_object_set(jsep, "sdp", json_string(sdp_merged));
1053
        g_free(sdp_stripped);
1054
        g_free(sdp_merged);
1055
        return jsep;
1056
}
1057

    
1058
void janus_relay_rtp(janus_pluginession *handle, int video, char *buf, int len) {
1059
        if(!handle)
1060
                return;
1061
        janus_ice_handle *session = (janus_ice_handle *)handle->gateway_handle;
1062
        if(!session)
1063
                return;
1064
        janus_ice_relay_rtp(session, video, buf, len);
1065
}
1066

    
1067
void janus_relay_rtcp(janus_pluginession *handle, int video, char *buf, int len) {
1068
        if(!handle)
1069
                return;
1070
        janus_ice_handle *session = (janus_ice_handle *)handle->gateway_handle;
1071
        if(!session)
1072
                return;
1073
        janus_ice_relay_rtcp(session, video, buf, len);
1074
}
1075

    
1076

    
1077
/* Main */
1078
gint main(int argc, char *argv[])
1079
{
1080
        /* Core dumps may be disallowed by parent of this process; change that */
1081
        struct rlimit core_limits;
1082
        core_limits.rlim_cur = core_limits.rlim_max = RLIM_INFINITY;
1083
        setrlimit(RLIMIT_CORE, &core_limits);
1084

    
1085
        struct gengetopt_args_info args_info;
1086
        /* Let's call our cmdline parser */
1087
        if(cmdline_parser(argc, argv, &args_info) != 0)
1088
                exit(1);
1089
        
1090
        JANUS_PRINT("----------------------------------------\n");
1091
        JANUS_PRINT("Starting Meetecho Janus (WebRTC Gateway)\n");
1092
        JANUS_PRINT("----------------------------------------\n\n");
1093
        
1094
        /* Handle SIGINT */
1095
        signal(SIGINT, janus_handle_signal);
1096

    
1097
        /* Setup Glib */
1098
        g_type_init();
1099

    
1100
        /* Any configuration to open? */
1101
        if(args_info.config_given) {
1102
                config_file = g_strdup(args_info.config_arg);
1103
                if(config_file == NULL) {
1104
                        JANUS_DEBUG("Memory error!\n");
1105
                        exit(1);
1106
                }
1107
        }
1108
        if(args_info.configs_folder_given) {
1109
                configs_folder = g_strdup(args_info.configs_folder_arg);
1110
                if(configs_folder == NULL) {
1111
                        JANUS_DEBUG("Memory error!\n");
1112
                        exit(1);
1113
                }
1114
        } else {
1115
                configs_folder = "./conf";        /* FIXME This is a relative path to where the executable is, not from where it was started... */
1116
        }
1117
        if(config_file == NULL) {
1118
                char file[255];
1119
                sprintf(file, "%s/janus.cfg", configs_folder);
1120
                config_file = g_strdup(file);
1121
                if(config_file == NULL) {
1122
                        JANUS_DEBUG("Memory error!\n");
1123
                        exit(1);
1124
                }
1125
        }
1126
        JANUS_PRINT("Reading configuration from %s\n", config_file);
1127
        if((config = janus_config_parse(config_file)) == NULL) {
1128
                if(args_info.config_given) {
1129
                        /* We only give up if the configuration file was explicitly provided */
1130
                        exit(1);
1131
                }
1132
                JANUS_DEBUG("Error reading/parsing the configuration file, going on with the defaults and the command line arguments\n");
1133
                config = janus_config_create("janus.cfg");
1134
                if(config == NULL) {
1135
                        /* If we can't even create an empty configuration, something's definitely wrong */
1136
                        exit(1);
1137
                }
1138
        }
1139
        janus_config_print(config);
1140
        /* Any command line argument that should overwrite the configuration? */
1141
        JANUS_PRINT("Checking command line arguments...\n");
1142
        if(args_info.interface_given) {
1143
                janus_config_add_item(config, "general", "interface", args_info.interface_arg);
1144
        }
1145
        if(args_info.configs_folder_given) {
1146
                janus_config_add_item(config, "general", "configs_folder", args_info.configs_folder_arg);
1147
        }
1148
        if(args_info.plugins_folder_given) {
1149
                janus_config_add_item(config, "general", "plugins_folder", args_info.plugins_folder_arg);
1150
        }
1151
        if(args_info.no_http_given) {
1152
                janus_config_add_item(config, "webserver", "http", "no");
1153
        }
1154
        if(args_info.port_given) {
1155
                char port[20];
1156
                sprintf(port, "%d", args_info.port_arg);
1157
                janus_config_add_item(config, "webserver", "port", port);
1158
        }
1159
        if(args_info.secure_port_given) {
1160
                janus_config_add_item(config, "webserver", "https", "yes");
1161
                char port[20];
1162
                sprintf(port, "%d", args_info.secure_port_arg);
1163
                janus_config_add_item(config, "webserver", "secure_port", port);
1164
        }
1165
        if(args_info.base_path_given) {
1166
                janus_config_add_item(config, "webserver", "base_path", args_info.base_path_arg);
1167
        }
1168
        if(args_info.cert_pem_given) {
1169
                janus_config_add_item(config, "certificates", "cert_pem", args_info.cert_pem_arg);
1170
        }
1171
        if(args_info.cert_key_given) {
1172
                janus_config_add_item(config, "certificates", "cert_key", args_info.cert_key_arg);
1173
        }
1174
        if(args_info.stun_server_given) {
1175
                /* Split in server and port (if port missing, use 3478 as default) */
1176
                char *stunport = strrchr(args_info.stun_server_arg, ':');
1177
                if(stunport != NULL) {
1178
                        *stunport = '\0';
1179
                        stunport++;
1180
                        janus_config_add_item(config, "nat", "stun_server", args_info.stun_server_arg);
1181
                        janus_config_add_item(config, "nat", "stun_port", stunport);
1182
                } else {
1183
                        janus_config_add_item(config, "nat", "stun_server", args_info.stun_server_arg);
1184
                        janus_config_add_item(config, "nat", "stun_port", "3478");
1185
                }
1186
        }
1187
        if(args_info.public_ip_given) {
1188
                janus_config_add_item(config, "nat", "public_ip", args_info.public_ip_arg);
1189
        }
1190
        if(args_info.rtp_port_range_given) {
1191
                janus_config_add_item(config, "media", "rtp_port_range", args_info.rtp_port_range_arg);
1192
        }
1193
        janus_config_print(config);
1194

    
1195
        /* What is the local public IP? */
1196
        JANUS_PRINT("Available interfaces:\n");
1197
        janus_config_item *item = janus_config_get_item_drilldown(config, "general", "interface");
1198
        if(item && item->value)
1199
                JANUS_PRINT("  -- Will try to use %s\n", item->value);
1200
        struct ifaddrs *myaddrs, *ifa;
1201
        int status = getifaddrs(&myaddrs);
1202
        char *tmp = NULL;
1203
        if (status == 0) {
1204
                for (ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next) {
1205
                        if(ifa->ifa_addr == NULL) {
1206
                                continue;
1207
                        }
1208
                        if((ifa->ifa_flags & IFF_UP) == 0) {
1209
                                continue;
1210
                        }
1211
                        if(ifa->ifa_addr->sa_family == AF_INET) {
1212
                                struct sockaddr_in *ip = (struct sockaddr_in *)(ifa->ifa_addr);
1213
                                char buf[16];
1214
                                if(inet_ntop(ifa->ifa_addr->sa_family, (void *)&(ip->sin_addr), buf, sizeof(buf)) == NULL) {
1215
                                        JANUS_DEBUG("\t%s:\tinet_ntop failed!\n", ifa->ifa_name);
1216
                                } else {
1217
                                        JANUS_PRINT("\t%s:\t%s\n", ifa->ifa_name, buf);
1218
                                        if(item && item->value && !strcasecmp(buf, item->value)) {
1219
                                                local_ip = strdup(buf);
1220
                                                if(local_ip == NULL) {
1221
                                                        JANUS_DEBUG("Memory error!\n");
1222
                                                        exit(1);
1223
                                                }
1224
                                        } else if(strcasecmp(buf, "127.0.0.1")) {        /* FIXME Check private IP addresses as well */
1225
                                                if(tmp == NULL)        /* FIXME Take note of the first IP we find, we'll use it as a backup */
1226
                                                        tmp = strdup(buf);
1227
                                        }
1228
                                }
1229
                        }
1230
                        /* TODO IPv6! */
1231
                }
1232
                freeifaddrs(myaddrs);
1233
        }
1234
        if(local_ip == NULL) {
1235
                if(tmp != NULL) {
1236
                        local_ip = tmp;
1237
                } else {
1238
                        JANUS_DEBUG("Couldn't find any address! using 127.0.0.1 as local IP... (which is NOT going to work out of your machine)\n");
1239
                        local_ip = g_strdup("127.0.0.1");
1240
                }
1241
        }
1242
        JANUS_PRINT("Using %s as local IP...\n", local_ip);
1243

    
1244
        /* Pre-parse the web server path, if any */
1245
        ws_path = "/janus";
1246
        item = janus_config_get_item_drilldown(config, "webserver", "base_path");
1247
        if(item && item->value) {
1248
                if(item->value[0] != '/') {
1249
                        JANUS_DEBUG("Invalid base path %s (it should start with a /, e.g., /janus\n", item->value);
1250
                        exit(1);
1251
                }
1252
                ws_path = g_strdup(item->value);
1253
                if(ws_path[strlen(ws_path)-1] == '/') {
1254
                        /* Remove the trailing slash, it makes things harder when we parse requests later */
1255
                        ws_path[strlen(ws_path)-1] = '\0';
1256
                }
1257
        }
1258
        
1259
        /* Setup ICE stuff (e.g., checking if the provided STUN server is correct) */
1260
        char *stun_server = NULL;
1261
        uint16_t stun_port = 0;
1262
        uint16_t rtp_min_port = 0, rtp_max_port = 0;
1263
        item = janus_config_get_item_drilldown(config, "media", "rtp_port_range");
1264
        if(item && item->value) {
1265
                /* Split in min and max port */
1266
                char *maxport = strrchr(item->value, '-');
1267
                if(maxport != NULL) {
1268
                        *maxport = '\0';
1269
                        maxport++;
1270
                        rtp_min_port = atoi(item->value);
1271
                        rtp_max_port = atoi(maxport);
1272
                        maxport--;
1273
                        maxport = '-';
1274
                }
1275
                if(rtp_min_port > rtp_max_port) {
1276
                        int temp_port = rtp_min_port;
1277
                        rtp_min_port = rtp_max_port;
1278
                        rtp_max_port = temp_port;
1279
                }
1280
                if(rtp_max_port == 0)
1281
                        rtp_max_port = 65535;
1282
                JANUS_PRINT("RTP port range: %u -- %u\n", rtp_min_port, rtp_max_port);
1283
        }
1284
        item = janus_config_get_item_drilldown(config, "nat", "stun_server");
1285
        if(item && item->value)
1286
                stun_server = (char *)item->value;
1287
        item = janus_config_get_item_drilldown(config, "nat", "stun_port");
1288
        if(item && item->value)
1289
                stun_port = atoi(item->value);
1290
        if(janus_ice_init(stun_server, stun_port, rtp_min_port, rtp_max_port) < 0) {
1291
                JANUS_DEBUG("Invalid STUN address %s:%u\n", stun_server, stun_port);
1292
                exit(1);
1293
        }
1294

    
1295
        /* Is there a public_ip value to be used for NAT traversal instead? */
1296
        item = janus_config_get_item_drilldown(config, "nat", "public_ip");
1297
        if(item && item->value) {
1298
                if(public_ip != NULL)
1299
                        g_free(public_ip);
1300
                public_ip = g_strdup((char *)item->value);
1301
                if(public_ip == NULL) {
1302
                        JANUS_DEBUG("Memory error\n");
1303
                        exit(1);
1304
                }
1305
                JANUS_PRINT("Using %s as our public IP in SDP\n", public_ip);
1306
        }
1307
        
1308
        /* Setup OpenSSL stuff */
1309
        item = janus_config_get_item_drilldown(config, "certificates", "cert_pem");
1310
        if(!item || !item->value) {
1311
                JANUS_DEBUG("Missing certificate/key path, use the command line or the configuration to provide one\n");
1312
                exit(1);
1313
        }
1314
        server_pem = (char *)item->value;
1315
        server_key = (char *)item->value;
1316
        item = janus_config_get_item_drilldown(config, "certificates", "cert_key");
1317
        if(item && item->value)
1318
                server_key = (char *)item->value;
1319
        JANUS_PRINT("Using certificates:\n\t%s\n\t%s\n", server_pem, server_key);
1320
        SSL_library_init();
1321
        SSL_load_error_strings();
1322
        OpenSSL_add_all_algorithms();
1323
        /* ... and DTLS-SRTP in particular */
1324
        if(janus_dtls_srtp_init(server_pem, server_key) < 0) {
1325
                exit(1);
1326
        }
1327

    
1328
        /* Initialize Sofia-SDP */
1329
        if(janus_sdp_init() < 0) {
1330
                exit(1);
1331
        }
1332

    
1333
        /* Load plugins */
1334
        char *path = "./plugins";        /* FIXME This is a relative path to where the executable is, not from where it was started... */
1335
        item = janus_config_get_item_drilldown(config, "general", "plugins_folder");
1336
        if(item && item->value)
1337
                path = (char *)item->value;
1338
        JANUS_PRINT("Plugins folder: %s\n", path);
1339
        DIR *dir = opendir(path);
1340
        if(!dir) {
1341
                JANUS_DEBUG("\tCouldn't access plugins folder...\n");
1342
                exit(1);
1343
        }
1344
        struct dirent *pluginent = NULL;
1345
        char pluginpath[255];
1346
        while((pluginent = readdir(dir))) {
1347
                int len = strlen(pluginent->d_name);
1348
                if (len < 4) {
1349
                        //~ JANUS_DEBUG("\tSkipping %s (filename too short)\n", pluginent->d_name);
1350
                        continue;
1351
                }
1352
                if (strcasecmp(pluginent->d_name+len-3, ".so")) {
1353
                        //~ JANUS_DEBUG("\tSkipping %s (not a shared object)\n", pluginent->d_name);
1354
                        continue;
1355
                }
1356
                JANUS_PRINT("Loading plugin '%s'...\n", pluginent->d_name);
1357
                memset(pluginpath, 0, 255);
1358
                sprintf(pluginpath, "%s/%s", path, pluginent->d_name);
1359
                void *plugin = dlopen(pluginpath, RTLD_LAZY);
1360
                if (!plugin) {
1361
                        JANUS_DEBUG("\tCouldn't load plugin '%s': %s\n", pluginent->d_name, dlerror());
1362
                } else {
1363
                        create_p *create = (create_p*) dlsym(plugin, "create");
1364
                        const char *dlsym_error = dlerror();
1365
                        if (dlsym_error) {
1366
                                JANUS_DEBUG("\tCouldn't load symbol 'create': %s\n", dlsym_error);
1367
                                continue;
1368
                        }
1369
                        janus_plugin *janus_plugin = create();
1370
                        if(!janus_plugin) {
1371
                                JANUS_DEBUG("\tCouldn't use function 'create'...\n");
1372
                                continue;
1373
                        }
1374
                        /* Are all methods and callbacks implemented? */
1375
                        if(!janus_plugin->init || !janus_plugin->destroy ||
1376
                                        !janus_plugin->get_version ||
1377
                                        !janus_plugin->get_version_string ||
1378
                                        !janus_plugin->get_description ||
1379
                                        !janus_plugin->get_package ||
1380
                                        !janus_plugin->get_name ||
1381
                                        !janus_plugin->get_name ||
1382
                                        !janus_plugin->create_session ||
1383
                                        !janus_plugin->handle_message ||
1384
                                        !janus_plugin->setup_media ||
1385
                                        !janus_plugin->incoming_rtp ||        /* FIXME Does this have to be mandatory? (e.g., sendonly plugins) */
1386
                                        !janus_plugin->incoming_rtcp ||
1387
                                        !janus_plugin->hangup_media) {
1388
                                JANUS_DEBUG("\tMissing some methods/callbacks, skipping this plugin...\n");
1389
                                continue;
1390
                        }
1391
                        janus_plugin->init(&janus_handler_plugin, configs_folder);
1392
                        JANUS_PRINT("\tVersion: %d (%s)\n", janus_plugin->get_version(), janus_plugin->get_version_string());
1393
                        JANUS_PRINT("\t   [%s] %s\n", janus_plugin->get_package(), janus_plugin->get_name());
1394
                        JANUS_PRINT("\t   %s\n", janus_plugin->get_description());
1395
                        if(plugins == NULL)
1396
                                plugins = g_hash_table_new(g_str_hash, g_str_equal);
1397
                        g_hash_table_insert(plugins, (gpointer)janus_plugin->get_package(), janus_plugin);
1398
                        if(plugins_so == NULL)
1399
                                plugins_so = g_hash_table_new(g_str_hash, g_str_equal);
1400
                        g_hash_table_insert(plugins_so, (gpointer)janus_plugin->get_package(), plugin);
1401
                }
1402
        }
1403
        closedir(dir);
1404

    
1405
        /* Start web server */
1406
        sessions = g_hash_table_new(NULL, NULL);
1407
        item = janus_config_get_item_drilldown(config, "webserver", "http");
1408
        if(item && item->value && !strcasecmp(item->value, "no")) {
1409
                JANUS_PRINT("HTTP webserver disabled\n");
1410
        } else {
1411
                int wsport = 8088;
1412
                item = janus_config_get_item_drilldown(config, "webserver", "port");
1413
                if(item && item->value)
1414
                        wsport = atoi(item->value);
1415
                ws = MHD_start_daemon(
1416
                        MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL,
1417
                        wsport,
1418
                        NULL,
1419
                        NULL,
1420
                        &janus_ws_handler,
1421
                        ws_path,
1422
                        MHD_OPTION_NOTIFY_COMPLETED, &janus_ws_request_completed, NULL,
1423
                        MHD_OPTION_END);
1424
                if(ws == NULL) {
1425
                        JANUS_DEBUG("Couldn't start webserver on port %d...\n", wsport);
1426
                        exit(1);        /* FIXME Should we really give up? */
1427
                }
1428
                JANUS_PRINT("HTTP webserver started (port %d, %s path listener)...\n", wsport, ws_path);
1429
        }
1430
        /* Do we also have to provide an HTTPS one? */
1431
        char *cert_pem_bytes = NULL, *cert_key_bytes = NULL; 
1432
        item = janus_config_get_item_drilldown(config, "webserver", "https");
1433
        if(item && item->value && !strcasecmp(item->value, "no")) {
1434
                JANUS_PRINT("HTTPS webserver disabled\n");
1435
        } else {
1436
                item = janus_config_get_item_drilldown(config, "webserver", "secure_port");
1437
                if(!item || !item->value) {
1438
                        JANUS_DEBUG("  -- HTTPS port missing\n");
1439
                        exit(1);        /* FIXME Should we really give up? */
1440
                }
1441
                int swsport = atoi(item->value);
1442
                /* Read certificate and key */
1443
                FILE *pem = fopen(server_pem, "rb");
1444
                if(!pem) {
1445
                        JANUS_DEBUG("Could not open certificate file '%s'...\n", server_pem);
1446
                        exit(1);        /* FIXME Should we really give up? */
1447
                }
1448
                fseek(pem, 0L, SEEK_END);
1449
                size_t size = ftell(pem);
1450
                fseek(pem, 0L, SEEK_SET);
1451
                cert_pem_bytes = calloc(size, sizeof(char));
1452
                if(cert_pem_bytes == NULL) {
1453
                        JANUS_DEBUG("Memory error!\n");
1454
                        exit(1);
1455
                }
1456
                char *index = cert_pem_bytes;
1457
                int read = 0, tot = size;
1458
                while((read = fread(index, sizeof(char), tot, pem)) > 0) {
1459
                        tot -= read;
1460
                        index += read;
1461
                }
1462
                fclose(pem);
1463
                FILE *key = fopen(server_key, "rb");
1464
                if(!key) {
1465
                        JANUS_DEBUG("Could not open key file '%s'...\n", server_key);
1466
                        exit(1);        /* FIXME Should we really give up? */
1467
                }
1468
                fseek(key, 0L, SEEK_END);
1469
                size = ftell(key);
1470
                fseek(key, 0L, SEEK_SET);
1471
                cert_key_bytes = calloc(size, sizeof(char));
1472
                if(cert_key_bytes == NULL) {
1473
                        JANUS_DEBUG("Memory error!\n");
1474
                        exit(1);
1475
                }
1476
                index = cert_key_bytes;
1477
                read = 0;
1478
                tot = size;
1479
                while((read = fread(index, sizeof(char), tot, key)) > 0) {
1480
                        tot -= read;
1481
                        index += read;
1482
                }
1483
                fclose(key);
1484
                /* Start webserver */
1485
                sws = MHD_start_daemon(
1486
                        MHD_USE_SSL | MHD_USE_DEBUG | MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL,
1487
                        swsport,
1488
                        NULL,
1489
                        NULL,
1490
                        &janus_ws_handler,
1491
                        ws_path,
1492
                        MHD_OPTION_NOTIFY_COMPLETED, &janus_ws_request_completed, NULL,
1493
                                /* FIXME We're using the same certificates as those for DTLS */
1494
                                MHD_OPTION_HTTPS_MEM_CERT, cert_pem_bytes,
1495
                                MHD_OPTION_HTTPS_MEM_KEY, cert_key_bytes,
1496
                        MHD_OPTION_END);
1497
                if(sws == NULL) {
1498
                        JANUS_DEBUG("Couldn't start secure webserver on port %d...\n", swsport);
1499
                        exit(1);        /* FIXME Should we really give up? */
1500
                } else {
1501
                        JANUS_PRINT("HTTPS webserver started (port %d, %s path listener)...\n", swsport, ws_path);
1502
                }
1503
        }
1504
        if(!ws && !sws) {
1505
                JANUS_DEBUG("No webserver (HTTP/HTTPS) started, giving up...\n"); 
1506
                exit(1);
1507
        }
1508
        while(!stop) {
1509
                /* Loop until we have to stop */
1510
                g_usleep(250000);
1511
        }
1512

    
1513
        /* Done */
1514
        if(config)
1515
                janus_config_destroy(config);
1516
        if(ws)
1517
                MHD_stop_daemon(ws);
1518
        ws = NULL;
1519
        if(sws)
1520
                MHD_stop_daemon(sws);
1521
        sws = NULL;
1522
        if(cert_pem_bytes != NULL)
1523
                g_free((gpointer)cert_pem_bytes);
1524
        cert_pem_bytes = NULL;
1525
        if(cert_key_bytes != NULL)
1526
                g_free((gpointer)cert_key_bytes);
1527
        cert_key_bytes = NULL;
1528
        g_hash_table_destroy(sessions);
1529
        SSL_CTX_free(janus_dtls_get_ssl_ctx());
1530
        EVP_cleanup();
1531
        ERR_free_strings();
1532
        janus_sdp_deinit();
1533
        
1534
        JANUS_PRINT("Closing plugins:\n");
1535
        if(plugins != NULL) {
1536
                g_hash_table_foreach(plugins, janus_plugin_close, NULL);
1537
        }
1538
        g_hash_table_destroy(plugins);
1539
        if(plugins_so != NULL) {
1540
                g_hash_table_foreach(plugins_so, janus_pluginso_close, NULL);
1541
        }
1542
        g_hash_table_destroy(plugins_so);
1543
        JANUS_PRINT("Bye!\n");
1544
        
1545
        exit(0);
1546
}