Statistics
| Branch: | Revision:

janus-gateway / transports / janus_websockets.c @ 88b5da7b

History | View | Annotate | Download (52.4 KB)

1
/*! \file   janus_websockets.c
2
 * \author Lorenzo Miniero <lorenzo@meetecho.com>
3
 * \copyright GNU General Public License v3
4
 * \brief  Janus WebSockets transport plugin
5
 * \details  This is an implementation of a WebSockets transport for the
6
 * Janus API, using the libwebsockets library (http://libwebsockets.org).
7
 * This means that, with the help of this module, browsers or applications
8
 * (e.g., nodejs server side implementations) can also make use of
9
 * WebSockets to make requests to the gateway. In that case, the same
10
 * WebSocket can be used for both sending requests and receiving
11
 * notifications, without the need for long polls. At the same time,
12
 * without the concept of a REST path, requests sent through the
13
 * WebSockets interface will need to include, when needed, additional
14
 * pieces of information like \c session_id and \c handle_id. That is,
15
 * where you'd send a Janus request related to a specific session to the
16
 * \c /janus/<session> path, with WebSockets you'd have to send the same
17
 * request with an additional \c session_id field in the JSON payload.
18
 * The same applies for the handle. The JavaScript library (janus.js)
19
 * implements all of this on the client side automatically.
20
 * \note When you create a session using WebSockets, a subscription to
21
 * the events related to it is done automatically, so no need for an
22
 * explicit request as the GET in the plain HTTP API. Closing a WebSocket
23
 * will also destroy all the sessions it created.
24
 *
25
 * \ingroup transports
26
 * \ref transports
27
 */
28

    
29
#include "transport.h"
30

    
31
#include <libwebsockets.h>
32

    
33
#include "../debug.h"
34
#include "../apierror.h"
35
#include "../config.h"
36
#include "../mutex.h"
37
#include "../utils.h"
38

    
39

    
40
/* Transport plugin information */
41
#define JANUS_WEBSOCKETS_VERSION                        1
42
#define JANUS_WEBSOCKETS_VERSION_STRING                "0.0.1"
43
#define JANUS_WEBSOCKETS_DESCRIPTION                "This transport plugin adds WebSockets support to the Janus API via libwebsockets."
44
#define JANUS_WEBSOCKETS_NAME                                "JANUS WebSockets transport plugin"
45
#define JANUS_WEBSOCKETS_AUTHOR                                "Meetecho s.r.l."
46
#define JANUS_WEBSOCKETS_PACKAGE                        "janus.transport.websockets"
47

    
48
/* Transport methods */
49
janus_transport *create(void);
50
int janus_websockets_init(janus_transport_callbacks *callback, const char *config_path);
51
void janus_websockets_destroy(void);
52
int janus_websockets_get_api_compatibility(void);
53
int janus_websockets_get_version(void);
54
const char *janus_websockets_get_version_string(void);
55
const char *janus_websockets_get_description(void);
56
const char *janus_websockets_get_name(void);
57
const char *janus_websockets_get_author(void);
58
const char *janus_websockets_get_package(void);
59
gboolean janus_websockets_is_janus_api_enabled(void);
60
gboolean janus_websockets_is_admin_api_enabled(void);
61
int janus_websockets_send_message(void *transport, void *request_id, gboolean admin, json_t *message);
62
void janus_websockets_session_created(void *transport, guint64 session_id);
63
void janus_websockets_session_over(void *transport, guint64 session_id, gboolean timeout);
64

    
65

    
66
/* Transport setup */
67
static janus_transport janus_websockets_transport =
68
        JANUS_TRANSPORT_INIT (
69
                .init = janus_websockets_init,
70
                .destroy = janus_websockets_destroy,
71

    
72
                .get_api_compatibility = janus_websockets_get_api_compatibility,
73
                .get_version = janus_websockets_get_version,
74
                .get_version_string = janus_websockets_get_version_string,
75
                .get_description = janus_websockets_get_description,
76
                .get_name = janus_websockets_get_name,
77
                .get_author = janus_websockets_get_author,
78
                .get_package = janus_websockets_get_package,
79

    
80
                .is_janus_api_enabled = janus_websockets_is_janus_api_enabled,
81
                .is_admin_api_enabled = janus_websockets_is_admin_api_enabled,
82

    
83
                .send_message = janus_websockets_send_message,
84
                .session_created = janus_websockets_session_created,
85
                .session_over = janus_websockets_session_over,
86
        );
87

    
88
/* Transport creator */
89
janus_transport *create(void) {
90
        JANUS_LOG(LOG_VERB, "%s created!\n", JANUS_WEBSOCKETS_NAME);
91
        return &janus_websockets_transport;
92
}
93

    
94

    
95
/* Useful stuff */
96
static gint initialized = 0, stopping = 0;
97
static janus_transport_callbacks *gateway = NULL;
98
static gboolean wss_janus_api_enabled = FALSE;
99
static gboolean wss_admin_api_enabled = FALSE;
100

    
101
/* JSON serialization options */
102
static size_t json_format = JSON_INDENT(3) | JSON_PRESERVE_ORDER;
103

    
104

    
105
/* Logging */
106
static int ws_log_level = 0;
107

    
108
/* WebSockets per-service thread */
109
static GThread *wss_thread = NULL, *swss_thread = NULL,
110
                *admin_wss_thread = NULL, *admin_swss_thread = NULL;
111
void *janus_websockets_thread(void *data);
112

    
113

    
114
/* WebSocket client session */
115
typedef struct janus_websockets_client {
116
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
117
        struct lws *wsi;                                                /* The libwebsockets client instance */
118
#else
119
        struct libwebsocket_context *context;        /* The libwebsockets client context */
120
        struct libwebsocket *wsi;                                /* The libwebsockets client instance */
121
#endif
122
        GAsyncQueue *messages;                                        /* Queue of outgoing messages to push */
123
        char *incoming;                                                        /* Buffer containing the incoming message to process (in case there are fragments) */
124
        unsigned char *buffer;                                        /* Buffer containing the message to send */
125
        int buflen;                                                                /* Length of the buffer (may be resized after re-allocations) */
126
        int bufpending;                                                        /* Data an interrupted previous write couldn't send */
127
        int bufoffset;                                                        /* Offset from where the interrupted previous write should resume */
128
        janus_mutex mutex;                                                /* Mutex to lock/unlock this session */
129
        gint session_timeout:1;                                        /* Whether a Janus session timeout occurred in the core */
130
        gint destroy:1;                                                        /* Flag to trigger a lazy session destruction */
131
} janus_websockets_client;
132

    
133

    
134
/* libwebsockets WS context(s) */
135
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
136
static struct lws_context *wss = NULL, *swss = NULL,
137
        *admin_wss = NULL, *admin_swss = NULL;
138
#else
139
static struct libwebsocket_context *wss = NULL, *swss = NULL,
140
        *admin_wss = NULL, *admin_swss = NULL;
141
#endif
142
/* libwebsockets sessions that have been closed */
143
static GList *old_wss;
144
static janus_mutex old_wss_mutex;
145
/* Callbacks for HTTP-related events (automatically rejected) */
146
static int janus_websockets_callback_http(
147
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
148
                struct lws *wsi,
149
                enum lws_callback_reasons reason,
150
#else
151
                struct libwebsocket_context *this,
152
                struct libwebsocket *wsi,
153
                enum libwebsocket_callback_reasons reason,
154
#endif
155
                void *user, void *in, size_t len);
156
static int janus_websockets_callback_https(
157
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
158
                struct lws *wsi,
159
                enum lws_callback_reasons reason,
160
#else
161
                struct libwebsocket_context *this,
162
                struct libwebsocket *wsi,
163
                enum libwebsocket_callback_reasons reason,
164
#endif
165
                void *user, void *in, size_t len);
166
/* Callbacks for WebSockets-related events */
167
static int janus_websockets_callback(
168
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
169
                struct lws *wsi,
170
                enum lws_callback_reasons reason,
171
#else
172
                struct libwebsocket_context *this,
173
                struct libwebsocket *wsi,
174
                enum libwebsocket_callback_reasons reason,
175
#endif
176
                void *user, void *in, size_t len);
177
static int janus_websockets_callback_secure(
178
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
179
                struct lws *wsi,
180
                enum lws_callback_reasons reason,
181
#else
182
                struct libwebsocket_context *this,
183
                struct libwebsocket *wsi,
184
                enum libwebsocket_callback_reasons reason,
185
#endif
186
                void *user, void *in, size_t len);
187
static int janus_websockets_admin_callback(
188
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
189
                struct lws *wsi,
190
                enum lws_callback_reasons reason,
191
#else
192
                struct libwebsocket_context *this,
193
                struct libwebsocket *wsi,
194
                enum libwebsocket_callback_reasons reason,
195
#endif
196
                void *user, void *in, size_t len);
197
static int janus_websockets_admin_callback_secure(
198
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
199
                struct lws *wsi,
200
                enum lws_callback_reasons reason,
201
#else
202
                struct libwebsocket_context *this,
203
                struct libwebsocket *wsi,
204
                enum libwebsocket_callback_reasons reason,
205
#endif
206
                void *user, void *in, size_t len);
207
/* Protocol mappings */
208
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
209
static struct lws_protocols wss_protocols[] = {
210
#else
211
static struct libwebsocket_protocols wss_protocols[] = {
212
#endif
213
        { "http-only", janus_websockets_callback_http, 0, 0 },
214
        { "janus-protocol", janus_websockets_callback, sizeof(janus_websockets_client), 0 },
215
        { NULL, NULL, 0 }
216
};
217
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
218
static struct lws_protocols swss_protocols[] = {
219
#else
220
static struct libwebsocket_protocols swss_protocols[] = {
221
#endif
222
        { "http-only", janus_websockets_callback_https, 0, 0 },
223
        { "janus-protocol", janus_websockets_callback_secure, sizeof(janus_websockets_client), 0 },
224
        { NULL, NULL, 0 }
225
};
226
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
227
static struct lws_protocols admin_wss_protocols[] = {
228
#else
229
static struct libwebsocket_protocols admin_wss_protocols[] = {
230
#endif
231
        { "http-only", janus_websockets_callback_http, 0, 0 },
232
        { "janus-admin-protocol", janus_websockets_admin_callback, sizeof(janus_websockets_client), 0 },
233
        { NULL, NULL, 0 }
234
};
235
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
236
static struct lws_protocols admin_swss_protocols[] = {
237
#else
238
static struct libwebsocket_protocols admin_swss_protocols[] = {
239
#endif
240
        { "http-only", janus_websockets_callback_https, 0, 0 },
241
        { "janus-admin-protocol", janus_websockets_admin_callback_secure, sizeof(janus_websockets_client), 0 },
242
        { NULL, NULL, 0 }
243
};
244
/* Helper for debugging reasons */
245
#define CASE_STR(name) case name: return #name
246
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
247
static const char *janus_websockets_reason_string(enum lws_callback_reasons reason) {
248
#else
249
static const char *janus_websockets_reason_string(enum libwebsocket_callback_reasons reason) {
250
#endif
251
        switch(reason) {
252
                CASE_STR(LWS_CALLBACK_ESTABLISHED);
253
                CASE_STR(LWS_CALLBACK_CLIENT_CONNECTION_ERROR);
254
                CASE_STR(LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH);
255
                CASE_STR(LWS_CALLBACK_CLIENT_ESTABLISHED);
256
                CASE_STR(LWS_CALLBACK_CLOSED);
257
                CASE_STR(LWS_CALLBACK_CLOSED_HTTP);
258
                CASE_STR(LWS_CALLBACK_RECEIVE);
259
                CASE_STR(LWS_CALLBACK_CLIENT_RECEIVE);
260
                CASE_STR(LWS_CALLBACK_CLIENT_RECEIVE_PONG);
261
                CASE_STR(LWS_CALLBACK_CLIENT_WRITEABLE);
262
                CASE_STR(LWS_CALLBACK_SERVER_WRITEABLE);
263
                CASE_STR(LWS_CALLBACK_HTTP);
264
                CASE_STR(LWS_CALLBACK_HTTP_BODY);
265
                CASE_STR(LWS_CALLBACK_HTTP_BODY_COMPLETION);
266
                CASE_STR(LWS_CALLBACK_HTTP_FILE_COMPLETION);
267
                CASE_STR(LWS_CALLBACK_HTTP_WRITEABLE);
268
                CASE_STR(LWS_CALLBACK_FILTER_NETWORK_CONNECTION);
269
                CASE_STR(LWS_CALLBACK_FILTER_HTTP_CONNECTION);
270
                CASE_STR(LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED);
271
                CASE_STR(LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION);
272
                CASE_STR(LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS);
273
                CASE_STR(LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS);
274
                CASE_STR(LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION);
275
                CASE_STR(LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER);
276
                CASE_STR(LWS_CALLBACK_CONFIRM_EXTENSION_OKAY);
277
                CASE_STR(LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED);
278
                CASE_STR(LWS_CALLBACK_PROTOCOL_INIT);
279
                CASE_STR(LWS_CALLBACK_PROTOCOL_DESTROY);
280
                CASE_STR(LWS_CALLBACK_WSI_CREATE);
281
                CASE_STR(LWS_CALLBACK_WSI_DESTROY);
282
                CASE_STR(LWS_CALLBACK_GET_THREAD_ID);
283
                CASE_STR(LWS_CALLBACK_ADD_POLL_FD);
284
                CASE_STR(LWS_CALLBACK_DEL_POLL_FD);
285
                CASE_STR(LWS_CALLBACK_CHANGE_MODE_POLL_FD);
286
                CASE_STR(LWS_CALLBACK_LOCK_POLL);
287
                CASE_STR(LWS_CALLBACK_UNLOCK_POLL);
288
                CASE_STR(LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY);
289
                CASE_STR(LWS_CALLBACK_USER);
290
                default:
291
                        break;
292
        }
293
        return NULL;
294
}
295

    
296
/* WebSockets ACL list for both Janus and Admin API */
297
GList *janus_websockets_access_list = NULL, *janus_websockets_admin_access_list = NULL;
298
janus_mutex access_list_mutex;
299
static void janus_websockets_allow_address(const char *ip, gboolean admin) {
300
        if(ip == NULL)
301
                return;
302
        /* Is this an IP or an interface? */
303
        janus_mutex_lock(&access_list_mutex);
304
        if(!admin)
305
                janus_websockets_access_list = g_list_append(janus_websockets_access_list, (gpointer)ip);
306
        else
307
                janus_websockets_admin_access_list = g_list_append(janus_websockets_admin_access_list, (gpointer)ip);
308
        janus_mutex_unlock(&access_list_mutex);
309
}
310
static gboolean janus_websockets_is_allowed(const char *ip, gboolean admin) {
311
        JANUS_LOG(LOG_VERB, "Checking if %s is allowed to contact %s interface\n", ip, admin ? "admin" : "janus");
312
        if(ip == NULL)
313
                return FALSE;
314
        if(!admin && janus_websockets_access_list == NULL) {
315
                JANUS_LOG(LOG_VERB, "Yep\n");
316
                return TRUE;
317
        }
318
        if(admin && janus_websockets_admin_access_list == NULL) {
319
                JANUS_LOG(LOG_VERB, "Yeah\n");
320
                return TRUE;
321
        }
322
        janus_mutex_lock(&access_list_mutex);
323
        GList *temp = admin ? janus_websockets_admin_access_list : janus_websockets_access_list;
324
        while(temp) {
325
                const char *allowed = (const char *)temp->data;
326
                if(allowed != NULL && strstr(ip, allowed)) {
327
                        janus_mutex_unlock(&access_list_mutex);
328
                        return TRUE;
329
                }
330
                temp = temp->next;
331
        }
332
        janus_mutex_unlock(&access_list_mutex);
333
        JANUS_LOG(LOG_VERB, "Nope...\n");
334
        return FALSE;
335
}
336

    
337
/* Transport implementation */
338
int janus_websockets_init(janus_transport_callbacks *callback, const char *config_path) {
339
        if(g_atomic_int_get(&stopping)) {
340
                /* Still stopping from before */
341
                return -1;
342
        }
343
        if(callback == NULL || config_path == NULL) {
344
                /* Invalid arguments */
345
                return -1;
346
        }
347

    
348
        /* This is the callback we'll need to invoke to contact the gateway */
349
        gateway = callback;
350

    
351
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
352
        JANUS_LOG(LOG_INFO, "libwebsockets >= 1.6 available, using new API\n");
353
#else
354
        JANUS_LOG(LOG_INFO, "libwebsockets < 1.6 available, using old API\n");
355
#endif
356

    
357
        /* Read configuration */
358
        char filename[255];
359
        g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_WEBSOCKETS_PACKAGE);
360
        JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename);
361
        janus_config *config = janus_config_parse(filename);
362
        if(config != NULL) {
363
                janus_config_print(config);
364

    
365
                /* Handle configuration */
366
                janus_config_item *item = janus_config_get_item_drilldown(config, "general", "json");
367
                if(item && item->value) {
368
                        /* Check how we need to format/serialize the JSON output */
369
                        if(!strcasecmp(item->value, "indented")) {
370
                                /* Default: indented, we use three spaces for that */
371
                                json_format = JSON_INDENT(3) | JSON_PRESERVE_ORDER;
372
                        } else if(!strcasecmp(item->value, "plain")) {
373
                                /* Not indented and no new lines, but still readable */
374
                                json_format = JSON_INDENT(0) | JSON_PRESERVE_ORDER;
375
                        } else if(!strcasecmp(item->value, "compact")) {
376
                                /* Compact, so no spaces between separators */
377
                                json_format = JSON_COMPACT | JSON_PRESERVE_ORDER;
378
                        } else {
379
                                JANUS_LOG(LOG_WARN, "Unsupported JSON format option '%s', using default (indented)\n", item->value);
380
                                json_format = JSON_INDENT(3) | JSON_PRESERVE_ORDER;
381
                        }
382
                }
383

    
384
                item = janus_config_get_item_drilldown(config, "general", "ws_logging");
385
                if(item && item->value) {
386
                        ws_log_level = atoi(item->value);
387
                        if(ws_log_level < 0)
388
                                ws_log_level = 0;
389
                }
390
                JANUS_LOG(LOG_VERB, "libwebsockets logging: %d\n", ws_log_level);
391
                lws_set_log_level(ws_log_level, NULL);
392
                old_wss = NULL;
393
                janus_mutex_init(&old_wss_mutex);
394

    
395
                /* Any ACL for either the Janus or Admin API? */
396
                item = janus_config_get_item_drilldown(config, "general", "ws_acl");
397
                if(item && item->value) {
398
                        gchar **list = g_strsplit(item->value, ",", -1);
399
                        gchar *index = list[0];
400
                        if(index != NULL) {
401
                                int i=0;
402
                                while(index != NULL) {
403
                                        if(strlen(index) > 0) {
404
                                                JANUS_LOG(LOG_INFO, "Adding '%s' to the Janus API allowed list...\n", index);
405
                                                janus_websockets_allow_address(g_strdup(index), FALSE);
406
                                        }
407
                                        i++;
408
                                        index = list[i];
409
                                }
410
                        }
411
                        g_strfreev(list);
412
                        list = NULL;
413
                }
414
                item = janus_config_get_item_drilldown(config, "admin", "admin_ws_acl");
415
                if(item && item->value) {
416
                        gchar **list = g_strsplit(item->value, ",", -1);
417
                        gchar *index = list[0];
418
                        if(index != NULL) {
419
                                int i=0;
420
                                while(index != NULL) {
421
                                        if(strlen(index) > 0) {
422
                                                JANUS_LOG(LOG_INFO, "Adding '%s' to the Admin/monitor allowed list...\n", index);
423
                                                janus_websockets_allow_address(g_strdup(index), TRUE);
424
                                        }
425
                                        i++;
426
                                        index = list[i];
427
                                }
428
                        }
429
                        g_strfreev(list);
430
                        list = NULL;
431
                }
432

    
433
                /* Setup the Janus API WebSockets server(s) */
434
                item = janus_config_get_item_drilldown(config, "general", "ws");
435
                if(!item || !item->value || !janus_is_true(item->value)) {
436
                        JANUS_LOG(LOG_WARN, "WebSockets server disabled\n");
437
                } else {
438
                        int wsport = 8188;
439
                        item = janus_config_get_item_drilldown(config, "general", "ws_port");
440
                        if(item && item->value)
441
                                wsport = atoi(item->value);
442
                        char *interface = NULL;
443
                        item = janus_config_get_item_drilldown(config, "general", "ws_interface");
444
                        if(item && item->value)
445
                                interface = (char *)item->value;
446
                        char *ip = NULL;
447
                        item = janus_config_get_item_drilldown(config, "general", "ws_ip");
448
                        if(item && item->value)
449
                                ip = (char *)item->value;
450
                        /* Prepare context */
451
                        struct lws_context_creation_info info;
452
                        memset(&info, 0, sizeof info);
453
                        info.port = wsport;
454
                        info.iface = ip ? ip : interface;
455
                        info.protocols = wss_protocols;
456
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
457
                        info.extensions = NULL;
458
#else
459
                        info.extensions = libwebsocket_get_internal_extensions();
460
#endif
461
                        info.ssl_cert_filepath = NULL;
462
                        info.ssl_private_key_filepath = NULL;
463
                        info.gid = -1;
464
                        info.uid = -1;
465
                        info.options = 0;
466
                        /* Create the WebSocket context */
467
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
468
                        wss = lws_create_context(&info);
469
#else
470
                        wss = libwebsocket_create_context(&info);
471
#endif
472
                        if(wss == NULL) {
473
                                JANUS_LOG(LOG_FATAL, "Error initializing libwebsockets...\n");
474
                        } else {
475
                                JANUS_LOG(LOG_INFO, "WebSockets server started (port %d)...\n", wsport);
476
                        }
477
                }
478
                item = janus_config_get_item_drilldown(config, "general", "wss");
479
                if(!item || !item->value || !janus_is_true(item->value)) {
480
                        JANUS_LOG(LOG_WARN, "Secure WebSockets server disabled\n");
481
                } else {
482
                        int wsport = 8989;
483
                        item = janus_config_get_item_drilldown(config, "general", "wss_port");
484
                        if(item && item->value)
485
                                wsport = atoi(item->value);
486
                        char *interface = NULL;
487
                        item = janus_config_get_item_drilldown(config, "general", "wss_interface");
488
                        if(item && item->value)
489
                                interface = (char *)item->value;
490
                        char *ip = NULL;
491
                        item = janus_config_get_item_drilldown(config, "general", "wss_ip");
492
                        if(item && item->value)
493
                                ip = (char *)item->value;
494
                        item = janus_config_get_item_drilldown(config, "certificates", "cert_pem");
495
                        if(!item || !item->value) {
496
                                JANUS_LOG(LOG_FATAL, "Missing certificate/key path\n");
497
                        } else {
498
                                char *server_pem = (char *)item->value;
499
                                char *server_key = (char *)item->value;
500
                                item = janus_config_get_item_drilldown(config, "certificates", "cert_key");
501
                                if(item && item->value)
502
                                        server_key = (char *)item->value;
503
                                JANUS_LOG(LOG_VERB, "Using certificates:\n\t%s\n\t%s\n", server_pem, server_key);
504
                                /* Prepare secure context */
505
                                struct lws_context_creation_info info;
506
                                memset(&info, 0, sizeof info);
507
                                info.port = wsport;
508
                                info.iface = ip ? ip : interface;
509
                                info.protocols = swss_protocols;
510
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
511
                                info.extensions = NULL;
512
#else
513
                                info.extensions = libwebsocket_get_internal_extensions();
514
#endif
515
                                info.ssl_cert_filepath = server_pem;
516
                                info.ssl_private_key_filepath = server_key;
517
                                info.gid = -1;
518
                                info.uid = -1;
519
                                info.options = 0;
520
                                /* Create the secure WebSocket context */
521
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
522
                                swss = lws_create_context(&info);
523
#else
524
                                swss = libwebsocket_create_context(&info);
525
#endif
526
                                if(swss == NULL) {
527
                                        JANUS_LOG(LOG_FATAL, "Error initializing libwebsockets...\n");
528
                                } else {
529
                                        JANUS_LOG(LOG_INFO, "Secure WebSockets server started (port %d)...\n", wsport);
530
                                }
531
                        }
532
                }
533
                /* Do the same for the Admin API, if enabled */
534
                item = janus_config_get_item_drilldown(config, "admin", "admin_ws");
535
                if(!item || !item->value || !janus_is_true(item->value)) {
536
                        JANUS_LOG(LOG_WARN, "Admin WebSockets server disabled\n");
537
                } else {
538
                        int wsport = 7188;
539
                        item = janus_config_get_item_drilldown(config, "admin", "admin_ws_port");
540
                        if(item && item->value)
541
                                wsport = atoi(item->value);
542
                        char *interface = NULL;
543
                        item = janus_config_get_item_drilldown(config, "admin", "admin_ws_interface");
544
                        if(item && item->value)
545
                                interface = (char *)item->value;
546
                        char *ip = NULL;
547
                        item = janus_config_get_item_drilldown(config, "admin", "admin_ws_ip");
548
                        if(item && item->value)
549
                                ip = (char *)item->value;
550
                        /* Prepare context */
551
                        struct lws_context_creation_info info;
552
                        memset(&info, 0, sizeof info);
553
                        info.port = wsport;
554
                        info.iface = ip ? ip : interface;
555
                        info.protocols = admin_wss_protocols;
556
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
557
                        info.extensions = NULL;
558
#else
559
                        info.extensions = libwebsocket_get_internal_extensions();
560
#endif
561
                        info.ssl_cert_filepath = NULL;
562
                        info.ssl_private_key_filepath = NULL;
563
                        info.gid = -1;
564
                        info.uid = -1;
565
                        info.options = 0;
566
                        /* Create the WebSocket context */
567
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
568
                        admin_wss = lws_create_context(&info);
569
#else
570
                        admin_wss = libwebsocket_create_context(&info);
571
#endif
572
                        if(admin_wss == NULL) {
573
                                JANUS_LOG(LOG_FATAL, "Error initializing libwebsockets...\n");
574
                        } else {
575
                                JANUS_LOG(LOG_INFO, "Admin WebSockets server started (port %d)...\n", wsport);
576
                        }
577
                }
578
                item = janus_config_get_item_drilldown(config, "admin", "admin_wss");
579
                if(!item || !item->value || !janus_is_true(item->value)) {
580
                        JANUS_LOG(LOG_WARN, "Secure Admin WebSockets server disabled\n");
581
                } else {
582
                        int wsport = 7989;
583
                        item = janus_config_get_item_drilldown(config, "admin", "admin_wss_port");
584
                        if(item && item->value)
585
                                wsport = atoi(item->value);
586
                        char *interface = NULL;
587
                        item = janus_config_get_item_drilldown(config, "admin", "admin_wss_interface");
588
                        if(item && item->value)
589
                                interface = (char *)item->value;
590
                        char *ip = NULL;
591
                        item = janus_config_get_item_drilldown(config, "admin", "admin_wss_ip");
592
                        if(item && item->value)
593
                                ip = (char *)item->value;
594
                        item = janus_config_get_item_drilldown(config, "certificates", "cert_pem");
595
                        if(!item || !item->value) {
596
                                JANUS_LOG(LOG_FATAL, "Missing certificate/key path\n");
597
                        } else {
598
                                char *server_pem = (char *)item->value;
599
                                char *server_key = (char *)item->value;
600
                                item = janus_config_get_item_drilldown(config, "certificates", "cert_key");
601
                                if(item && item->value)
602
                                        server_key = (char *)item->value;
603
                                JANUS_LOG(LOG_VERB, "Using certificates:\n\t%s\n\t%s\n", server_pem, server_key);
604
                                /* Prepare secure context */
605
                                struct lws_context_creation_info info;
606
                                memset(&info, 0, sizeof info);
607
                                info.port = wsport;
608
                                info.iface = ip ? ip : interface;
609
                                info.protocols = admin_swss_protocols;
610
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
611
                                info.extensions = NULL;
612
#else
613
                                info.extensions = libwebsocket_get_internal_extensions();
614
#endif
615
                                info.ssl_cert_filepath = server_pem;
616
                                info.ssl_private_key_filepath = server_key;
617
                                info.gid = -1;
618
                                info.uid = -1;
619
                                info.options = 0;
620
                                /* Create the secure WebSocket context */
621
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
622
                                admin_swss = lws_create_context(&info);
623
#else
624
                                admin_swss = libwebsocket_create_context(&info);
625
#endif
626
                                if(admin_swss == NULL) {
627
                                        JANUS_LOG(LOG_FATAL, "Error initializing libwebsockets...\n");
628
                                } else {
629
                                        JANUS_LOG(LOG_INFO, "Secure Admin WebSockets server started (port %d)...\n", wsport);
630
                                }
631
                        }
632
                }
633
        }
634
        janus_config_destroy(config);
635
        config = NULL;
636
        if(!wss && !swss && !admin_wss && !admin_swss) {
637
                JANUS_LOG(LOG_FATAL, "No WebSockets server started, giving up...\n");
638
                return -1;        /* No point in keeping the plugin loaded */
639
        }
640
        wss_janus_api_enabled = wss || swss;
641
        wss_admin_api_enabled = admin_wss || admin_swss;
642

    
643
        GError *error = NULL;
644
        /* Start the WebSocket service threads */
645
        if(wss != NULL) {
646
                wss_thread = g_thread_try_new("ws thread", &janus_websockets_thread, wss, &error);
647
                if(!wss_thread) {
648
                        g_atomic_int_set(&initialized, 0);
649
                        JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the WebSockets thread...\n", error->code, error->message ? error->message : "??");
650
                        return -1;
651
                }
652
        }
653
        if(swss != NULL) {
654
                swss_thread = g_thread_try_new("sws thread", &janus_websockets_thread, swss, &error);
655
                if(!swss_thread) {
656
                        g_atomic_int_set(&initialized, 0);
657
                        JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Secure WebSockets thread...\n", error->code, error->message ? error->message : "??");
658
                        return -1;
659
                }
660
        }
661
        if(admin_wss != NULL) {
662
                admin_wss_thread = g_thread_try_new("admin ws thread", &janus_websockets_thread, admin_wss, &error);
663
                if(!admin_wss_thread) {
664
                        g_atomic_int_set(&initialized, 0);
665
                        JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Admin WebSockets thread...\n", error->code, error->message ? error->message : "??");
666
                        return -1;
667
                }
668
        }
669
        if(admin_swss != NULL) {
670
                admin_swss_thread = g_thread_try_new("admin sws thread", &janus_websockets_thread, admin_swss, &error);
671
                if(!admin_swss_thread) {
672
                        g_atomic_int_set(&initialized, 0);
673
                        JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Secure Admin WebSockets thread...\n", error->code, error->message ? error->message : "??");
674
                        return -1;
675
                }
676
        }
677

    
678
        /* Done */
679
        g_atomic_int_set(&initialized, 1);
680
        JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_WEBSOCKETS_NAME);
681
        return 0;
682
}
683

    
684
void janus_websockets_destroy(void) {
685
        if(!g_atomic_int_get(&initialized))
686
                return;
687
        g_atomic_int_set(&stopping, 1);
688

    
689
        /* Stop the service threads */
690
        if(wss_thread != NULL) {
691
                g_thread_join(wss_thread);
692
                wss_thread = NULL;
693
        }
694
        if(swss_thread != NULL) {
695
                g_thread_join(swss_thread);
696
                swss_thread = NULL;
697
        }
698
        if(admin_wss_thread != NULL) {
699
                g_thread_join(admin_wss_thread);
700
                admin_wss_thread = NULL;
701
        }
702
        if(admin_swss_thread != NULL) {
703
                g_thread_join(admin_swss_thread);
704
                admin_swss_thread = NULL;
705
        }
706

    
707
        /* Destroy the contexts */
708
        if(wss != NULL) {
709
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
710
                lws_context_destroy(wss);
711
#else
712
                libwebsocket_context_destroy(wss);
713
#endif
714
                wss = NULL;
715
        }
716
        if(swss != NULL) {
717
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
718
                lws_context_destroy(swss);
719
#else
720
                libwebsocket_context_destroy(swss);
721
#endif
722
                swss = NULL;
723
        }
724
        if(admin_wss != NULL) {
725
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
726
                lws_context_destroy(admin_wss);
727
#else
728
                libwebsocket_context_destroy(admin_wss);
729
#endif
730
                admin_wss = NULL;
731
        }
732
        if(admin_swss != NULL) {
733
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
734
                lws_context_destroy(admin_swss);
735
#else
736
                libwebsocket_context_destroy(admin_swss);
737
#endif
738
                admin_swss = NULL;
739
        }
740

    
741
        g_atomic_int_set(&initialized, 0);
742
        g_atomic_int_set(&stopping, 0);
743
        JANUS_LOG(LOG_INFO, "%s destroyed!\n", JANUS_WEBSOCKETS_NAME);
744
}
745

    
746
int janus_websockets_get_api_compatibility(void) {
747
        /* Important! This is what your plugin MUST always return: don't lie here or bad things will happen */
748
        return JANUS_TRANSPORT_API_VERSION;
749
}
750

    
751
int janus_websockets_get_version(void) {
752
        return JANUS_WEBSOCKETS_VERSION;
753
}
754

    
755
const char *janus_websockets_get_version_string(void) {
756
        return JANUS_WEBSOCKETS_VERSION_STRING;
757
}
758

    
759
const char *janus_websockets_get_description(void) {
760
        return JANUS_WEBSOCKETS_DESCRIPTION;
761
}
762

    
763
const char *janus_websockets_get_name(void) {
764
        return JANUS_WEBSOCKETS_NAME;
765
}
766

    
767
const char *janus_websockets_get_author(void) {
768
        return JANUS_WEBSOCKETS_AUTHOR;
769
}
770

    
771
const char *janus_websockets_get_package(void) {
772
        return JANUS_WEBSOCKETS_PACKAGE;
773
}
774

    
775
gboolean janus_websockets_is_janus_api_enabled(void) {
776
        return wss_janus_api_enabled;
777
}
778

    
779
gboolean janus_websockets_is_admin_api_enabled(void) {
780
        return wss_admin_api_enabled;
781
}
782

    
783
int janus_websockets_send_message(void *transport, void *request_id, gboolean admin, json_t *message) {
784
        if(message == NULL)
785
                return -1;
786
        if(transport == NULL) {
787
                g_free(message);
788
                return -1;
789
        }
790
        /* Make sure this is not related to a closed /freed WebSocket session */
791
        janus_mutex_lock(&old_wss_mutex);
792
        janus_websockets_client *client = (janus_websockets_client *)transport;
793
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
794
        if(g_list_find(old_wss, client) != NULL || !client->wsi) {
795
#else
796
        if(g_list_find(old_wss, client) != NULL || !client->context || !client->wsi) {
797
#endif
798
                g_free(message);
799
                message = NULL;
800
                transport = NULL;
801
                janus_mutex_unlock(&old_wss_mutex);
802
                return -1;
803
        }
804
        janus_mutex_lock(&client->mutex);
805
        /* Convert to string and enqueue */
806
        char *payload = json_dumps(message, json_format);
807
        g_async_queue_push(client->messages, payload);
808
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
809
        lws_callback_on_writable(client->wsi);
810
#else
811
        libwebsocket_callback_on_writable(client->context, client->wsi);
812
#endif
813
        janus_mutex_unlock(&client->mutex);
814
        janus_mutex_unlock(&old_wss_mutex);
815
        json_decref(message);
816
        return 0;
817
}
818

    
819
void janus_websockets_session_created(void *transport, guint64 session_id) {
820
        /* We don't care */
821
}
822

    
823
void janus_websockets_session_over(void *transport, guint64 session_id, gboolean timeout) {
824
        if(transport == NULL || !timeout)
825
                return;
826
        /* We only care if it's a timeout: if so, close the connection */
827
        janus_websockets_client *client = (janus_websockets_client *)transport;
828
        /* Make sure this is not related to a closed WebSocket session */
829
        janus_mutex_lock(&old_wss_mutex);
830
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
831
        if(g_list_find(old_wss, client) == NULL && client->wsi){
832
#else
833
        if(g_list_find(old_wss, client) == NULL && client->context && client->wsi){
834
#endif
835
                janus_mutex_lock(&client->mutex);
836
                client->session_timeout = 1;
837
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
838
                lws_callback_on_writable(client->wsi);
839
#else
840
                libwebsocket_callback_on_writable(client->context, client->wsi);
841
#endif
842
                janus_mutex_unlock(&client->mutex);
843
        }
844
        janus_mutex_unlock(&old_wss_mutex);
845
}
846

    
847

    
848
/* Thread */
849
void *janus_websockets_thread(void *data) {
850
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
851
        struct lws_context *service = (struct lws_context *)data;
852
#else
853
        struct libwebsocket_context *service = (struct libwebsocket_context *)data;
854
#endif
855
        if(service == NULL) {
856
                JANUS_LOG(LOG_ERR, "Invalid service\n");
857
                return NULL;
858
        }
859

    
860
        const char *type = NULL;
861
        if(service == wss)
862
                type = "WebSocket (Janus API)";
863
        else if(service == swss)
864
                type = "Secure WebSocket (Janus API)";
865
        else if(service == admin_wss)
866
                type = "WebSocket (Admin API)";
867
        else if(service == admin_swss)
868
                type = "Secure WebSocket (Admin API)";
869

    
870
        JANUS_LOG(LOG_INFO, "%s thread started\n", type);
871

    
872
        while(g_atomic_int_get(&initialized) && !g_atomic_int_get(&stopping)) {
873
                /* libwebsockets is single thread, we cycle through events here */
874
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
875
                lws_service(service, 50);
876
#else
877
                libwebsocket_service(service, 50);
878
#endif
879
        }
880

    
881
        /* Get rid of the WebSockets server */
882
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
883
        lws_cancel_service(service);
884
#else
885
        libwebsocket_cancel_service(service);
886
#endif
887
        /* Done */
888
        JANUS_LOG(LOG_INFO, "%s thread ended\n", type);
889
        return NULL;
890
}
891

    
892

    
893
/* WebSockets */
894
static int janus_websockets_callback_http(
895
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
896
                struct lws *wsi,
897
                enum lws_callback_reasons reason,
898
#else
899
                struct libwebsocket_context *this,
900
                struct libwebsocket *wsi,
901
                enum libwebsocket_callback_reasons reason,
902
#endif
903
                void *user, void *in, size_t len)
904
{
905
        /* This endpoint cannot be used for HTTP */
906
        switch(reason) {
907
                case LWS_CALLBACK_HTTP:
908
                        JANUS_LOG(LOG_VERB, "Rejecting incoming HTTP request on WebSockets endpoint\n");
909
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
910
                        lws_return_http_status(wsi, 403, NULL);
911
#else
912
                        libwebsockets_return_http_status(this, wsi, 403, NULL);
913
#endif
914
                        /* Close and free connection */
915
                        return -1;
916
                case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
917
                        if (!in) {
918
                                JANUS_LOG(LOG_VERB, "Rejecting incoming HTTP request on WebSockets endpoint: no sub-protocol specified\n");
919
                                return -1;
920
                        }
921
                        break;
922
                default:
923
                        break;
924
        }
925
        return 0;
926
}
927

    
928
static int janus_websockets_callback_https(
929
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
930
                struct lws *wsi,
931
                enum lws_callback_reasons reason,
932
#else
933
                struct libwebsocket_context *this,
934
                struct libwebsocket *wsi,
935
                enum libwebsocket_callback_reasons reason,
936
#endif
937
                void *user, void *in, size_t len)
938
{
939
        /* We just forward the event to the HTTP handler */
940
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
941
        return janus_websockets_callback_http(wsi, reason, user, in, len);
942
#else
943
        return janus_websockets_callback_http(this, wsi, reason, user, in, len);
944
#endif
945
}
946

    
947
/* This callback handles Janus API requests */
948
static int janus_websockets_callback(
949
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
950
                struct lws *wsi,
951
                enum lws_callback_reasons reason,
952
#else
953
                struct libwebsocket_context *this,
954
                struct libwebsocket *wsi,
955
                enum libwebsocket_callback_reasons reason,
956
#endif
957
                void *user, void *in, size_t len)
958
{
959
        janus_websockets_client *ws_client = (janus_websockets_client *)user;
960
        switch(reason) {
961
                case LWS_CALLBACK_ESTABLISHED: {
962
                        /* Is there any filtering we should apply? */
963
                        char name[256], ip[256];
964
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
965
                        lws_get_peer_addresses(wsi, lws_get_socket_fd(wsi), name, 256, ip, 256);
966
#else
967
                        libwebsockets_get_peer_addresses(this, wsi, libwebsocket_get_socket_fd(wsi), name, 256, ip, 256);
968
#endif
969
                        JANUS_LOG(LOG_VERB, "[WSS-%p] WebSocket connection opened from %s by %s\n", wsi, ip, name);
970
                        if(!janus_websockets_is_allowed(ip, FALSE)) {
971
                                JANUS_LOG(LOG_ERR, "[WSS-%p] IP %s is unauthorized to connect to the WebSockets Janus API interface\n", wsi, ip);
972
                                /* Close the connection */
973
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
974
                                lws_callback_on_writable(wsi);
975
#else
976
                                libwebsocket_callback_on_writable(this, wsi);
977
#endif
978
                                return -1;
979
                        }
980
                        JANUS_LOG(LOG_VERB, "[WSS-%p] WebSocket connection accepted\n", wsi);
981
                        if(ws_client == NULL) {
982
                                JANUS_LOG(LOG_ERR, "[WSS-%p] Invalid WebSocket client instance...\n", wsi);
983
                                return -1;
984
                        }
985
                        /* Clean the old sessions list, in case this pointer was used before */
986
                        janus_mutex_lock(&old_wss_mutex);
987
                        if(g_list_find(old_wss, ws_client) != NULL)
988
                                old_wss = g_list_remove(old_wss, ws_client);
989
                        janus_mutex_unlock(&old_wss_mutex);
990
                        /* Prepare the session */
991
#ifndef HAVE_LIBWEBSOCKETS_NEWAPI
992
                        ws_client->context = this;
993
#endif
994
                        ws_client->wsi = wsi;
995
                        ws_client->messages = g_async_queue_new();
996
                        ws_client->buffer = NULL;
997
                        ws_client->buflen = 0;
998
                        ws_client->bufpending = 0;
999
                        ws_client->bufoffset = 0;
1000
                        ws_client->session_timeout = 0;
1001
                        ws_client->destroy = 0;
1002
                        janus_mutex_init(&ws_client->mutex);
1003
                        /* Let us know when the WebSocket channel becomes writeable */
1004
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
1005
                        lws_callback_on_writable(wsi);
1006
#else
1007
                        libwebsocket_callback_on_writable(this, wsi);
1008
#endif
1009
                        JANUS_LOG(LOG_VERB, "[WSS-%p]   -- Ready to be used!\n", wsi);
1010
                        return 0;
1011
                }
1012
                case LWS_CALLBACK_RECEIVE: {
1013
                        JANUS_LOG(LOG_VERB, "[WSS-%p] Got %zu bytes:\n", wsi, len);
1014
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
1015
                        if(ws_client == NULL || ws_client->wsi == NULL) {
1016
#else
1017
                        if(ws_client == NULL || ws_client->context == NULL || ws_client->wsi == NULL) {
1018
#endif
1019
                                JANUS_LOG(LOG_ERR, "[WSS-%p] Invalid WebSocket client instance...\n", wsi);
1020
                                return -1;
1021
                        }
1022
                        /* Is this a new message, or part of a fragmented one? */
1023
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
1024
                        const size_t remaining = lws_remaining_packet_payload(wsi);
1025
#else
1026
                        const size_t remaining = libwebsockets_remaining_packet_payload(wsi);
1027
#endif
1028
                        if(ws_client->incoming == NULL) {
1029
                                JANUS_LOG(LOG_VERB, "[WSS-%p] First fragment: %zu bytes, %zu remaining\n", wsi, len, remaining);
1030
                                ws_client->incoming = g_malloc0(len+1);
1031
                                memcpy(ws_client->incoming, in, len);
1032
                                ws_client->incoming[len] = '\0';
1033
                                JANUS_LOG(LOG_HUGE, "%s\n", ws_client->incoming);
1034
                        } else {
1035
                                size_t offset = strlen(ws_client->incoming);
1036
                                JANUS_LOG(LOG_VERB, "[WSS-%p] Appending fragment: offset %zu, %zu bytes, %zu remaining\n", wsi, offset, len, remaining);
1037
                                ws_client->incoming = g_realloc(ws_client->incoming, offset+len+1);
1038
                                memcpy(ws_client->incoming+offset, in, len);
1039
                                ws_client->incoming[offset+len] = '\0';
1040
                                JANUS_LOG(LOG_HUGE, "%s\n", ws_client->incoming+offset);
1041
                        }
1042
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
1043
                        if(remaining > 0 || !lws_is_final_fragment(wsi)) {
1044
#else
1045
                        if(remaining > 0 || !libwebsocket_is_final_fragment(wsi)) {
1046
#endif
1047
                                /* Still waiting for some more fragments */
1048
                                JANUS_LOG(LOG_VERB, "[WSS-%p] Waiting for more fragments\n", wsi);
1049
                                return 0;
1050
                        }
1051
                        JANUS_LOG(LOG_VERB, "[WSS-%p] Done, parsing message: %zu bytes\n", wsi, strlen(ws_client->incoming));
1052
                        /* If we got here, the message is complete: parse the JSON payload */
1053
                        json_error_t error;
1054
                        json_t *root = json_loads(ws_client->incoming, 0, &error);
1055
                        g_free(ws_client->incoming);
1056
                        ws_client->incoming = NULL;
1057
                        /* Notify the core, passing both the object and, since it may be needed, the error */
1058
                        gateway->incoming_request(&janus_websockets_transport, ws_client, NULL, FALSE, root, &error);
1059
                        return 0;
1060
                }
1061
                case LWS_CALLBACK_SERVER_WRITEABLE: {
1062
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
1063
                        if(ws_client == NULL || ws_client->wsi == NULL) {
1064
#else
1065
                        if(ws_client == NULL || ws_client->context == NULL || ws_client->wsi == NULL) {
1066
#endif
1067
                                JANUS_LOG(LOG_ERR, "[WSS-%p] Invalid WebSocket client instance...\n", wsi);
1068
                                return -1;
1069
                        }
1070
                        if(!ws_client->destroy && !g_atomic_int_get(&stopping)) {
1071
                                janus_mutex_lock(&ws_client->mutex);
1072
                                /* Check if we have a pending/partial write to complete first */
1073
                                if(ws_client->buffer && ws_client->bufpending > 0 && ws_client->bufoffset > 0
1074
                                                && !ws_client->destroy && !g_atomic_int_get(&stopping)) {
1075
                                        JANUS_LOG(LOG_VERB, "[WSS-%p] Completing pending WebSocket write (still need to write last %d bytes)...\n",
1076
                                                wsi, ws_client->bufpending);
1077
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
1078
                                        int sent = lws_write(wsi, ws_client->buffer + ws_client->bufoffset, ws_client->bufpending, LWS_WRITE_TEXT);
1079
#else
1080
                                        int sent = libwebsocket_write(wsi, ws_client->buffer + ws_client->bufoffset, ws_client->bufpending, LWS_WRITE_TEXT);
1081
#endif
1082
                                        JANUS_LOG(LOG_VERB, "[WSS-%p]   -- Sent %d/%d bytes\n", wsi, sent, ws_client->bufpending);
1083
                                        if(sent > -1 && sent < ws_client->bufpending) {
1084
                                                /* We still couldn't send everything that was left, we'll try and complete this in the next round */
1085
                                                ws_client->bufpending -= sent;
1086
                                                ws_client->bufoffset += sent;
1087
                                        } else {
1088
                                                /* Clear the pending/partial write queue */
1089
                                                ws_client->bufpending = 0;
1090
                                                ws_client->bufoffset = 0;
1091
                                        }
1092
                                        /* Done for this round, check the next response/notification later */
1093
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
1094
                                        lws_callback_on_writable(wsi);
1095
#else
1096
                                        libwebsocket_callback_on_writable(this, wsi);
1097
#endif
1098
                                        janus_mutex_unlock(&ws_client->mutex);
1099
                                        return 0;
1100
                                }
1101
                                /* Shoot all the pending messages */
1102
                                char *response = g_async_queue_try_pop(ws_client->messages);
1103
                                if(response && !ws_client->destroy && !g_atomic_int_get(&stopping)) {
1104
                                        /* Gotcha! */
1105
                                        int buflen = LWS_SEND_BUFFER_PRE_PADDING + strlen(response) + LWS_SEND_BUFFER_POST_PADDING;
1106
                                        if(ws_client->buffer == NULL) {
1107
                                                /* Let's allocate a shared buffer */
1108
                                                JANUS_LOG(LOG_VERB, "[WSS-%p] Allocating %d bytes (response is %zu bytes)\n", wsi, buflen, strlen(response));
1109
                                                ws_client->buflen = buflen;
1110
                                                ws_client->buffer = g_malloc0(buflen);
1111
                                        } else if(buflen > ws_client->buflen) {
1112
                                                /* We need a larger shared buffer */
1113
                                                JANUS_LOG(LOG_VERB, "[WSS-%p] Re-allocating to %d bytes (was %d, response is %zu bytes)\n", wsi, buflen, ws_client->buflen, strlen(response));
1114
                                                ws_client->buflen = buflen;
1115
                                                ws_client->buffer = g_realloc(ws_client->buffer, buflen);
1116
                                        }
1117
                                        memcpy(ws_client->buffer + LWS_SEND_BUFFER_PRE_PADDING, response, strlen(response));
1118
                                        JANUS_LOG(LOG_VERB, "[WSS-%p] Sending WebSocket message (%zu bytes)...\n", wsi, strlen(response));
1119
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
1120
                                        int sent = lws_write(wsi, ws_client->buffer + LWS_SEND_BUFFER_PRE_PADDING, strlen(response), LWS_WRITE_TEXT);
1121
#else
1122
                                        int sent = libwebsocket_write(wsi, ws_client->buffer + LWS_SEND_BUFFER_PRE_PADDING, strlen(response), LWS_WRITE_TEXT);
1123
#endif
1124
                                        JANUS_LOG(LOG_VERB, "[WSS-%p]   -- Sent %d/%zu bytes\n", wsi, sent, strlen(response));
1125
                                        if(sent > -1 && sent < (int)strlen(response)) {
1126
                                                /* We couldn't send everything in a single write, we'll complete this in the next round */
1127
                                                ws_client->bufpending = strlen(response) - sent;
1128
                                                ws_client->bufoffset = LWS_SEND_BUFFER_PRE_PADDING + sent;
1129
                                                JANUS_LOG(LOG_VERB, "[WSS-%p]   -- Couldn't write all bytes (%d missing), setting offset %d\n",
1130
                                                        wsi, ws_client->bufpending, ws_client->bufoffset);
1131
                                        }
1132
                                        /* We can get rid of the message */
1133
                                        g_free(response);
1134
                                        /* Done for this round, check the next response/notification later */
1135
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
1136
                                        lws_callback_on_writable(wsi);
1137
#else
1138
                                        libwebsocket_callback_on_writable(this, wsi);
1139
#endif
1140
                                        janus_mutex_unlock(&ws_client->mutex);
1141
                                        return 0;
1142
                                }
1143
                                janus_mutex_unlock(&ws_client->mutex);
1144
                        }
1145
                        return 0;
1146
                }
1147
                case LWS_CALLBACK_CLOSED: {
1148
                        JANUS_LOG(LOG_VERB, "[WSS-%p] WS connection closed\n", wsi);
1149
                        if(ws_client != NULL) {
1150
                                /* Notify core */
1151
                                gateway->transport_gone(&janus_websockets_transport, ws_client);
1152
                                /* Mark the session as closed */
1153
                                janus_mutex_lock(&old_wss_mutex);
1154
                                old_wss = g_list_append(old_wss, ws_client);
1155
                                janus_mutex_unlock(&old_wss_mutex);
1156
                                /* Cleanup */
1157
                                janus_mutex_lock(&ws_client->mutex);
1158
                                JANUS_LOG(LOG_INFO, "[WSS-%p] Destroying WebSocket client\n", wsi);
1159
                                ws_client->destroy = 1;
1160
#ifndef HAVE_LIBWEBSOCKETS_NEWAPI
1161
                                ws_client->context = NULL;
1162
#endif
1163
                                ws_client->wsi = NULL;
1164
                                /* Remove messages queue too, if needed */
1165
                                if(ws_client->messages != NULL) {
1166
                                        char *response = NULL;
1167
                                        while((response = g_async_queue_try_pop(ws_client->messages)) != NULL) {
1168
                                                g_free(response);
1169
                                        }
1170
                                        g_async_queue_unref(ws_client->messages);
1171
                                }
1172
                                /* ... and the shared buffers */
1173
                                g_free(ws_client->incoming);
1174
                                ws_client->incoming = NULL;
1175
                                g_free(ws_client->buffer);
1176
                                ws_client->buffer = NULL;
1177
                                ws_client->buflen = 0;
1178
                                ws_client->bufpending = 0;
1179
                                ws_client->bufoffset = 0;
1180
                                janus_mutex_unlock(&ws_client->mutex);
1181
                        }
1182
                        JANUS_LOG(LOG_VERB, "[WSS-%p]   -- closed\n", wsi);
1183
                        return 0;
1184
                }
1185
                default:
1186
                        if(wsi != NULL) {
1187
                                JANUS_LOG(LOG_VERB, "[WSS-%p] %d (%s)\n", wsi, reason, janus_websockets_reason_string(reason));
1188
                        } else {
1189
                                JANUS_LOG(LOG_VERB, "[WSS] %d (%s)\n", reason, janus_websockets_reason_string(reason));
1190
                        }
1191
                        break;
1192
        }
1193
        return 0;
1194
}
1195

    
1196
static int janus_websockets_callback_secure(
1197
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
1198
                struct lws *wsi,
1199
                enum lws_callback_reasons reason,
1200
#else
1201
                struct libwebsocket_context *this,
1202
                struct libwebsocket *wsi,
1203
                enum libwebsocket_callback_reasons reason,
1204
#endif
1205
                void *user, void *in, size_t len)
1206
{
1207
        /* We just forward the event to the Janus API handler */
1208
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
1209
        return janus_websockets_callback(wsi, reason, user, in, len);
1210
#else
1211
        return janus_websockets_callback(this, wsi, reason, user, in, len);
1212
#endif
1213
}
1214

    
1215
/* This callback handles Admin API requests */
1216
static int janus_websockets_admin_callback(
1217
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
1218
                struct lws *wsi,
1219
                enum lws_callback_reasons reason,
1220
#else
1221
                struct libwebsocket_context *this,
1222
                struct libwebsocket *wsi,
1223
                enum libwebsocket_callback_reasons reason,
1224
#endif
1225
                void *user, void *in, size_t len)
1226
{
1227
        janus_websockets_client *ws_client = (janus_websockets_client *)user;
1228
        switch(reason) {
1229
                case LWS_CALLBACK_ESTABLISHED: {
1230
                        /* Is there any filtering we should apply? */
1231
                        char name[256], ip[256];
1232
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
1233
                        lws_get_peer_addresses(wsi, lws_get_socket_fd(wsi), name, 256, ip, 256);
1234
#else
1235
                        libwebsockets_get_peer_addresses(this, wsi, libwebsocket_get_socket_fd(wsi), name, 256, ip, 256);
1236
#endif
1237
                        JANUS_LOG(LOG_VERB, "[AdminWSS-%p] WebSocket connection opened from %s by %s\n", wsi, ip, name);
1238
                        if(!janus_websockets_is_allowed(ip, TRUE)) {
1239
                                JANUS_LOG(LOG_ERR, "[AdminWSS-%p] IP %s is unauthorized to connect to the WebSockets Admin API interface\n", wsi, ip);
1240
                                /* Close the connection */
1241
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
1242
                                lws_callback_on_writable(wsi);
1243
#else
1244
                                libwebsocket_callback_on_writable(this, wsi);
1245
#endif
1246
                                return -1;
1247
                        }
1248
                        JANUS_LOG(LOG_VERB, "[AdminWSS-%p] WebSocket connection accepted\n", wsi);
1249
                        if(ws_client == NULL) {
1250
                                JANUS_LOG(LOG_ERR, "[AdminWSS-%p] Invalid WebSocket client instance...\n", wsi);
1251
                                return -1;
1252
                        }
1253
                        /* Clean the old sessions list, in case this pointer was used before */
1254
                        janus_mutex_lock(&old_wss_mutex);
1255
                        if(g_list_find(old_wss, ws_client) != NULL)
1256
                                old_wss = g_list_remove(old_wss, ws_client);
1257
                        janus_mutex_unlock(&old_wss_mutex);
1258
                        /* Prepare the session */
1259
#ifndef HAVE_LIBWEBSOCKETS_NEWAPI
1260
                        ws_client->context = this;
1261
#endif
1262
                        ws_client->wsi = wsi;
1263
                        ws_client->messages = g_async_queue_new();
1264
                        ws_client->buffer = NULL;
1265
                        ws_client->buflen = 0;
1266
                        ws_client->bufpending = 0;
1267
                        ws_client->bufoffset = 0;
1268
                        ws_client->session_timeout = 0;
1269
                        ws_client->destroy = 0;
1270
                        janus_mutex_init(&ws_client->mutex);
1271
                        /* Let us know when the WebSocket channel becomes writeable */
1272
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
1273
                        lws_callback_on_writable(wsi);
1274
#else
1275
                        libwebsocket_callback_on_writable(this, wsi);
1276
#endif
1277
                        JANUS_LOG(LOG_VERB, "[AdminWSS-%p]   -- Ready to be used!\n", wsi);
1278
                        return 0;
1279
                }
1280
                case LWS_CALLBACK_RECEIVE: {
1281
                        JANUS_LOG(LOG_VERB, "[AdminWSS-%p] Got %zu bytes:\n", wsi, len);
1282
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
1283
                        if(ws_client == NULL || ws_client->wsi == NULL) {
1284
#else
1285
                        if(ws_client == NULL || ws_client->context == NULL || ws_client->wsi == NULL) {
1286
#endif
1287
                                JANUS_LOG(LOG_ERR, "[AdminWSS-%p] Invalid WebSocket client instance...\n", wsi);
1288
                                return -1;
1289
                        }
1290
                        /* Is this a new message, or part of a fragmented one? */
1291
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
1292
                        const size_t remaining = lws_remaining_packet_payload(wsi);
1293
#else
1294
                        const size_t remaining = libwebsockets_remaining_packet_payload(wsi);
1295
#endif
1296
                        if(ws_client->incoming == NULL) {
1297
                                JANUS_LOG(LOG_VERB, "[AdminWSS-%p] First fragment: %zu bytes, %zu remaining\n", wsi, len, remaining);
1298
                                ws_client->incoming = g_malloc0(len+1);
1299
                                memcpy(ws_client->incoming, in, len);
1300
                                ws_client->incoming[len] = '\0';
1301
                                JANUS_LOG(LOG_HUGE, "%s\n", ws_client->incoming);
1302
                        } else {
1303
                                size_t offset = strlen(ws_client->incoming);
1304
                                JANUS_LOG(LOG_VERB, "[AdminWSS-%p] Appending fragment: offset %zu, %zu bytes, %zu remaining\n", wsi, offset, len, remaining);
1305
                                ws_client->incoming = g_realloc(ws_client->incoming, offset+len+1);
1306
                                memcpy(ws_client->incoming+offset, in, len);
1307
                                ws_client->incoming[offset+len] = '\0';
1308
                                JANUS_LOG(LOG_HUGE, "%s\n", ws_client->incoming+offset);
1309
                        }
1310
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
1311
                        if(remaining > 0 || !lws_is_final_fragment(wsi)) {
1312
#else
1313
                        if(remaining > 0 || !libwebsocket_is_final_fragment(wsi)) {
1314
#endif
1315
                                /* Still waiting for some more fragments */
1316
                                JANUS_LOG(LOG_VERB, "[AdminWSS-%p] Waiting for more fragments\n", wsi);
1317
                                return 0;
1318
                        }
1319
                        JANUS_LOG(LOG_VERB, "[AdminWSS-%p] Done, parsing message: %zu bytes\n", wsi, strlen(ws_client->incoming));
1320
                        /* If we got here, the message is complete: parse the JSON payload */
1321
                        json_error_t error;
1322
                        json_t *root = json_loads(ws_client->incoming, 0, &error);
1323
                        g_free(ws_client->incoming);
1324
                        ws_client->incoming = NULL;
1325
                        /* Notify the core, passing both the object and, since it may be needed, the error */
1326
                        gateway->incoming_request(&janus_websockets_transport, ws_client, NULL, TRUE, root, &error);
1327
                        return 0;
1328
                }
1329
                case LWS_CALLBACK_SERVER_WRITEABLE: {
1330
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
1331
                        if(ws_client == NULL || ws_client->wsi == NULL) {
1332
#else
1333
                        if(ws_client == NULL || ws_client->context == NULL || ws_client->wsi == NULL) {
1334
#endif
1335
                                JANUS_LOG(LOG_ERR, "[AdminWSS-%p] Invalid WebSocket client instance...\n", wsi);
1336
                                return -1;
1337
                        }
1338
                        if(!ws_client->destroy && !g_atomic_int_get(&stopping)) {
1339
                                janus_mutex_lock(&ws_client->mutex);
1340
                                /* Check if we have a pending/partial write to complete first */
1341
                                if(ws_client->buffer && ws_client->bufpending > 0 && ws_client->bufoffset > 0
1342
                                                && !ws_client->destroy && !g_atomic_int_get(&stopping)) {
1343
                                        JANUS_LOG(LOG_VERB, "[AdminWSS-%p] Completing pending WebSocket write (still need to write last %d bytes)...\n",
1344
                                                wsi, ws_client->bufpending);
1345
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
1346
                                        int sent = lws_write(wsi, ws_client->buffer + ws_client->bufoffset, ws_client->bufpending, LWS_WRITE_TEXT);
1347
#else
1348
                                        int sent = libwebsocket_write(wsi, ws_client->buffer + ws_client->bufoffset, ws_client->bufpending, LWS_WRITE_TEXT);
1349
#endif
1350
                                        JANUS_LOG(LOG_VERB, "[AdminWSS-%p]   -- Sent %d/%d bytes\n", wsi, sent, ws_client->bufpending);
1351
                                        if(sent > -1 && sent < ws_client->bufpending) {
1352
                                                /* We still couldn't send everything that was left, we'll try and complete this in the next round */
1353
                                                ws_client->bufpending -= sent;
1354
                                                ws_client->bufoffset += sent;
1355
                                        } else {
1356
                                                /* Clear the pending/partial write queue */
1357
                                                ws_client->bufpending = 0;
1358
                                                ws_client->bufoffset = 0;
1359
                                        }
1360
                                        /* Done for this round, check the next response/notification later */
1361
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
1362
                                        lws_callback_on_writable(wsi);
1363
#else
1364
                                        libwebsocket_callback_on_writable(this, wsi);
1365
#endif
1366
                                        janus_mutex_unlock(&ws_client->mutex);
1367
                                        return 0;
1368
                                }
1369
                                /* Shoot all the pending messages */
1370
                                char *response = g_async_queue_try_pop(ws_client->messages);
1371
                                if(response && !ws_client->destroy && !g_atomic_int_get(&stopping)) {
1372
                                        /* Gotcha! */
1373
                                        int buflen = LWS_SEND_BUFFER_PRE_PADDING + strlen(response) + LWS_SEND_BUFFER_POST_PADDING;
1374
                                        if(ws_client->buffer == NULL) {
1375
                                                /* Let's allocate a shared buffer */
1376
                                                JANUS_LOG(LOG_VERB, "[AdminWSS-%p] Allocating %d bytes (response is %zu bytes)\n", wsi, buflen, strlen(response));
1377
                                                ws_client->buflen = buflen;
1378
                                                ws_client->buffer = g_malloc0(buflen);
1379
                                        } else if(buflen > ws_client->buflen) {
1380
                                                /* We need a larger shared buffer */
1381
                                                JANUS_LOG(LOG_VERB, "[AdminWSS-%p] Re-allocating to %d bytes (was %d, response is %zu bytes)\n", wsi, buflen, ws_client->buflen, strlen(response));
1382
                                                ws_client->buflen = buflen;
1383
                                                ws_client->buffer = g_realloc(ws_client->buffer, buflen);
1384
                                        }
1385
                                        memcpy(ws_client->buffer + LWS_SEND_BUFFER_PRE_PADDING, response, strlen(response));
1386
                                        JANUS_LOG(LOG_VERB, "[AdminWSS-%p] Sending WebSocket message (%zu bytes)...\n", wsi, strlen(response));
1387
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
1388
                                        int sent = lws_write(wsi, ws_client->buffer + LWS_SEND_BUFFER_PRE_PADDING, strlen(response), LWS_WRITE_TEXT);
1389
#else
1390
                                        int sent = libwebsocket_write(wsi, ws_client->buffer + LWS_SEND_BUFFER_PRE_PADDING, strlen(response), LWS_WRITE_TEXT);
1391
#endif
1392
                                        JANUS_LOG(LOG_VERB, "[AdminWSS-%p]   -- Sent %d/%zu bytes\n", wsi, sent, strlen(response));
1393
                                        if(sent > -1 && sent < (int)strlen(response)) {
1394
                                                /* We couldn't send everything in a single write, we'll complete this in the next round */
1395
                                                ws_client->bufpending = strlen(response) - sent;
1396
                                                ws_client->bufoffset = LWS_SEND_BUFFER_PRE_PADDING + sent;
1397
                                                JANUS_LOG(LOG_VERB, "[AdminWSS-%p]   -- Couldn't write all bytes (%d missing), setting offset %d\n",
1398
                                                        wsi, ws_client->bufpending, ws_client->bufoffset);
1399
                                        }
1400
                                        /* We can get rid of the message */
1401
                                        g_free(response);
1402
                                        /* Done for this round, check the next response/notification later */
1403
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
1404
                                        lws_callback_on_writable(wsi);
1405
#else
1406
                                        libwebsocket_callback_on_writable(this, wsi);
1407
#endif
1408
                                        janus_mutex_unlock(&ws_client->mutex);
1409
                                        return 0;
1410
                                }
1411
                                janus_mutex_unlock(&ws_client->mutex);
1412
                        }
1413
                        return 0;
1414
                }
1415
                case LWS_CALLBACK_CLOSED: {
1416
                        JANUS_LOG(LOG_VERB, "[AdminWSS-%p] WS connection closed\n", wsi);
1417
                        if(ws_client != NULL) {
1418
                                /* Notify core */
1419
                                gateway->transport_gone(&janus_websockets_transport, ws_client);
1420
                                /* Mark the session as closed */
1421
                                janus_mutex_lock(&old_wss_mutex);
1422
                                old_wss = g_list_append(old_wss, ws_client);
1423
                                janus_mutex_unlock(&old_wss_mutex);
1424
                                /* Cleanup */
1425
                                janus_mutex_lock(&ws_client->mutex);
1426
                                JANUS_LOG(LOG_INFO, "[AdminWSS-%p] Destroying WebSocket client\n", wsi);
1427
                                ws_client->destroy = 1;
1428
#ifndef HAVE_LIBWEBSOCKETS_NEWAPI
1429
                                ws_client->context = NULL;
1430
#endif
1431
                                ws_client->wsi = NULL;
1432
                                /* Remove messages queue too, if needed */
1433
                                if(ws_client->messages != NULL) {
1434
                                        char *response = NULL;
1435
                                        while((response = g_async_queue_try_pop(ws_client->messages)) != NULL) {
1436
                                                g_free(response);
1437
                                        }
1438
                                        g_async_queue_unref(ws_client->messages);
1439
                                }
1440
                                /* ... and the shared buffers */
1441
                                g_free(ws_client->incoming);
1442
                                ws_client->incoming = NULL;
1443
                                g_free(ws_client->buffer);
1444
                                ws_client->buffer = NULL;
1445
                                ws_client->buflen = 0;
1446
                                ws_client->bufpending = 0;
1447
                                ws_client->bufoffset = 0;
1448
                                janus_mutex_unlock(&ws_client->mutex);
1449
                        }
1450
                        JANUS_LOG(LOG_VERB, "[AdminWSS-%p]   -- closed\n", wsi);
1451
                        return 0;
1452
                }
1453
                default:
1454
                        if(wsi != NULL) {
1455
                                JANUS_LOG(LOG_VERB, "[AdminWSS-%p] %d (%s)\n", wsi, reason, janus_websockets_reason_string(reason));
1456
                        } else {
1457
                                JANUS_LOG(LOG_VERB, "[AdminWSS] %d (%s)\n", reason, janus_websockets_reason_string(reason));
1458
                        }
1459
                        break;
1460
        }
1461
        return 0;
1462
}
1463

    
1464
static int janus_websockets_admin_callback_secure(
1465
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
1466
                struct lws *wsi,
1467
                enum lws_callback_reasons reason,
1468
#else
1469
                struct libwebsocket_context *this,
1470
                struct libwebsocket *wsi,
1471
                enum libwebsocket_callback_reasons reason,
1472
#endif
1473
                void *user, void *in, size_t len)
1474
{
1475
        /* We just forward the event to the Admin API handler */
1476
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
1477
        return janus_websockets_admin_callback(wsi, reason, user, in, len);
1478
#else
1479
        return janus_websockets_admin_callback(this, wsi, reason, user, in, len);
1480
#endif
1481
}