1 /*
2 * mod_v8 for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3 * Copyright (C) 2013-2014, Peter Olsson <peter@olssononline.se>
4 *
5 * Version: MPL 1.1
6 *
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
11 *
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
16 *
17 * The Original Code is mod_v8 for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18 *
19 * The Initial Developer of the Original Code is
20 * Peter Olsson <peter@olssononline.se>
21 * Portions created by the Initial Developer are Copyright (C)
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 * Peter Olsson <peter@olssononline.se>
26 *
27 * fseventhandler.cpp -- JavaScript EventHandler class
28 *
29 */
30
31 #include "fseventhandler.hpp"
32 #include "fsevent.hpp"
33 #include "fssession.hpp"
34
35 #define MAX_QUEUE_LEN 100000
36
37 using namespace std;
38 using namespace v8;
39
40 typedef struct {
41 char *cmd;
42 char *arg;
43 char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1];
44 int ack;
45 switch_memory_pool_t *pool;
46 } api_command_struct_t;
47
48 static const char js_class_name[] = "EventHandler";
49
~FSEventHandler(void)50 FSEventHandler::~FSEventHandler(void)
51 {
52 v8_remove_event_handler(this);
53
54 if (_event_hash) switch_core_hash_destroy(&_event_hash);
55
56 if (_event_queue) {
57 void *pop;
58
59 while (switch_queue_trypop(_event_queue, &pop) == SWITCH_STATUS_SUCCESS) {
60 switch_event_t *pevent = (switch_event_t *) pop;
61 if (pevent) {
62 switch_event_destroy(&pevent);
63 }
64 }
65 }
66
67 if (_filters) switch_event_destroy(&_filters);
68 if (_mutex) switch_mutex_destroy(_mutex);
69 if (_pool) switch_core_destroy_memory_pool(&_pool);
70 }
71
Init()72 void FSEventHandler::Init()
73 {
74 if (switch_core_new_memory_pool(&_pool) != SWITCH_STATUS_SUCCESS) {
75 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "OH OH no pool\n");
76 return;
77 }
78
79 switch_mutex_init(&_mutex, SWITCH_MUTEX_NESTED, _pool);
80 switch_core_hash_init(&_event_hash);
81 switch_queue_create(&_event_queue, MAX_QUEUE_LEN, _pool);
82
83 _filters = NULL;
84 memset(&_event_list, 0, sizeof(_event_list));
85
86 v8_add_event_handler(this);
87 }
88
GetJSClassName()89 string FSEventHandler::GetJSClassName()
90 {
91 return js_class_name;
92 }
93
QueueEvent(switch_event_t * event)94 void FSEventHandler::QueueEvent(switch_event_t *event)
95 {
96 switch_event_t *clone;
97 int send = 0;
98
99 switch_mutex_lock(_mutex);
100
101 if (_event_list[SWITCH_EVENT_ALL]) {
102 send = 1;
103 } else if ((_event_list[event->event_id])) {
104 if (event->event_id != SWITCH_EVENT_CUSTOM || !event->subclass_name || (switch_core_hash_find(_event_hash, event->subclass_name))) {
105 send = 1;
106 }
107 }
108
109 if (send) {
110 if (_filters && _filters->headers) {
111 switch_event_header_t *hp;
112 const char *hval;
113
114 send = 0;
115
116 for (hp = _filters->headers; hp; hp = hp->next) {
117 if ((hval = switch_event_get_header(event, hp->name))) {
118 const char *comp_to = hp->value;
119 int pos = 1, cmp = 0;
120
121 while (comp_to && *comp_to) {
122 if (*comp_to == '+') {
123 pos = 1;
124 } else if (*comp_to == '-') {
125 pos = 0;
126 } else if (*comp_to != ' ') {
127 break;
128 }
129 comp_to++;
130 }
131
132 if (send && pos) {
133 continue;
134 }
135
136 if (!comp_to) {
137 continue;
138 }
139
140 if (*hp->value == '/') {
141 switch_regex_t *re = NULL;
142 int ovector[30];
143 cmp = !!switch_regex_perform(hval, comp_to, &re, ovector, sizeof(ovector) / sizeof(ovector[0]));
144 switch_regex_safe_free(re);
145 } else {
146 cmp = !strcasecmp(hval, comp_to);
147 }
148
149 if (cmp) {
150 if (pos) {
151 send = 1;
152 } else {
153 send = 0;
154 break;
155 }
156 }
157 }
158 }
159 }
160 }
161
162 switch_mutex_unlock(_mutex);
163
164 if (send) {
165 if (switch_event_dup(&clone, event) == SWITCH_STATUS_SUCCESS) {
166 if (switch_queue_trypush(_event_queue, clone) != SWITCH_STATUS_SUCCESS) {
167 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Lost event to JS EventHandler, you must read the events faster!\n");
168 switch_event_destroy(&clone);
169 }
170 } else {
171 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n");
172 }
173 }
174 }
175
176 static const char *MARKER = "1";
177
DoSubscribe(const v8::FunctionCallbackInfo<v8::Value> & info)178 void FSEventHandler::DoSubscribe(const v8::FunctionCallbackInfo<v8::Value>& info)
179 {
180 int i, custom = 0;
181 bool ret = false;
182
183 for (i = 0; i < info.Length(); i++) {
184 String::Utf8Value str(info[i]);
185 switch_event_types_t etype;
186
187 if (custom) {
188 switch_mutex_lock(_mutex);
189 switch_core_hash_insert(_event_hash, js_safe_str(*str), MARKER);
190 switch_mutex_unlock(_mutex);
191 } else if (switch_name_event(js_safe_str(*str), &etype) == SWITCH_STATUS_SUCCESS) {
192 ret = true;
193
194 if (etype == SWITCH_EVENT_ALL) {
195 uint32_t x = 0;
196 for (x = 0; x < SWITCH_EVENT_ALL; x++) {
197 _event_list[x] = 1;
198 }
199 }
200
201 if (etype <= SWITCH_EVENT_ALL) {
202 _event_list[etype] = 1;
203 }
204
205 if (etype == SWITCH_EVENT_CUSTOM) {
206 custom++;
207 }
208 }
209 }
210
211 info.GetReturnValue().Set(ret);
212 }
213
Construct(const v8::FunctionCallbackInfo<v8::Value> & info)214 void *FSEventHandler::Construct(const v8::FunctionCallbackInfo<v8::Value>& info)
215 {
216 FSEventHandler *obj = new FSEventHandler(info);
217 obj->DoSubscribe(info);
218 return obj;
219 }
220
JS_EVENTHANDLER_FUNCTION_IMPL(Subscribe)221 JS_EVENTHANDLER_FUNCTION_IMPL(Subscribe)
222 {
223 DoSubscribe(info);
224 }
225
JS_EVENTHANDLER_FUNCTION_IMPL(UnSubscribe)226 JS_EVENTHANDLER_FUNCTION_IMPL(UnSubscribe)
227 {
228 int i, custom = 0;
229 bool ret = false;
230
231 for (i = 0; i < info.Length(); i++) {
232 String::Utf8Value str(info[i]);
233 switch_event_types_t etype;
234
235 if (custom) {
236 switch_mutex_lock(_mutex);
237 switch_core_hash_delete(_event_hash, js_safe_str(*str));
238 switch_mutex_unlock(_mutex);
239 } else if (switch_name_event(js_safe_str(*str), &etype) == SWITCH_STATUS_SUCCESS) {
240 uint32_t x = 0;
241 ret = true;
242
243 if (etype == SWITCH_EVENT_CUSTOM) {
244 custom++;
245 } else if (etype == SWITCH_EVENT_ALL) {
246 for (x = 0; x <= SWITCH_EVENT_ALL; x++) {
247 _event_list[x] = 0;
248 }
249 } else {
250 if (_event_list[SWITCH_EVENT_ALL]) {
251 _event_list[SWITCH_EVENT_ALL] = 0;
252 for (x = 0; x < SWITCH_EVENT_ALL; x++) {
253 _event_list[x] = 1;
254 }
255 }
256 _event_list[etype] = 0;
257 }
258 }
259 }
260
261 info.GetReturnValue().Set(ret);
262 }
263
JS_EVENTHANDLER_FUNCTION_IMPL(DeleteFilter)264 JS_EVENTHANDLER_FUNCTION_IMPL(DeleteFilter)
265 {
266 if (info.Length() < 1) {
267 info.GetReturnValue().Set(false);
268 } else {
269 String::Utf8Value str(info[0]);
270 const char *headerName = js_safe_str(*str);
271
272 if (zstr(headerName)) {
273 info.GetReturnValue().Set(false);
274 return;
275 }
276
277 switch_mutex_lock(_mutex);
278
279 if (!_filters) {
280 switch_event_create_plain(&_filters, SWITCH_EVENT_CLONE);
281 }
282
283 if (!strcasecmp(headerName, "all")) {
284 switch_event_destroy(&_filters);
285 switch_event_create_plain(&_filters, SWITCH_EVENT_CLONE);
286 } else {
287 switch_event_del_header(_filters, headerName);
288 }
289
290 info.GetReturnValue().Set(true);
291
292 switch_mutex_unlock(_mutex);
293 }
294 }
295
JS_EVENTHANDLER_FUNCTION_IMPL(AddFilter)296 JS_EVENTHANDLER_FUNCTION_IMPL(AddFilter)
297 {
298 if (info.Length() < 2) {
299 info.GetReturnValue().Set(false);
300 } else {
301 String::Utf8Value str1(info[0]);
302 String::Utf8Value str2(info[1]);
303 const char *headerName = js_safe_str(*str1);
304 const char *headerVal = js_safe_str(*str2);
305
306 if (zstr(headerName) || zstr(headerVal)) {
307 info.GetReturnValue().Set(false);
308 return;
309 }
310
311 switch_mutex_lock(_mutex);
312
313 if (!_filters) {
314 switch_event_create_plain(&_filters, SWITCH_EVENT_CLONE);
315 }
316
317 switch_event_add_header_string(_filters, SWITCH_STACK_BOTTOM, headerName, headerVal);
318
319 info.GetReturnValue().Set(true);
320
321 switch_mutex_unlock(_mutex);
322 }
323 }
324
JS_EVENTHANDLER_FUNCTION_IMPL(GetEvent)325 JS_EVENTHANDLER_FUNCTION_IMPL(GetEvent)
326 {
327 void *pop = NULL;
328 int timeout = 0;
329 switch_event_t *pevent = NULL;
330
331 if (info.Length() > 0 && !info[0].IsEmpty()) {
332 timeout = info[0]->Int32Value();
333 }
334
335 if (timeout > 0) {
336 if (switch_queue_pop_timeout(_event_queue, &pop, (switch_interval_time_t) timeout * 1000) == SWITCH_STATUS_SUCCESS && pop) {
337 pevent = (switch_event_t *) pop;
338 }
339 } else if (timeout < 0) {
340 if (switch_queue_pop(_event_queue, &pop) == SWITCH_STATUS_SUCCESS && pop) {
341 pevent = (switch_event_t *) pop;
342 }
343 } else {
344 if (switch_queue_trypop(_event_queue, &pop) == SWITCH_STATUS_SUCCESS && pop) {
345 pevent = (switch_event_t *) pop;
346 }
347 }
348
349 if (pevent) {
350 FSEvent *evt = new FSEvent(info);
351 evt->SetEvent(pevent, 0);
352 evt->RegisterInstance(info.GetIsolate(), "", true);
353 info.GetReturnValue().Set(evt->GetJavaScriptObject());
354 } else {
355 info.GetReturnValue().Set(Null(info.GetIsolate()));
356 }
357 }
358
JS_EVENTHANDLER_FUNCTION_IMPL(SendEvent)359 JS_EVENTHANDLER_FUNCTION_IMPL(SendEvent)
360 {
361 if (info.Length() == 0) {
362 info.GetReturnValue().Set(false);
363 } else {
364 if (!info[0].IsEmpty() && info[0]->IsObject()) {
365 FSEvent *evt = JSBase::GetInstance<FSEvent>(info[0]->ToObject());
366 switch_event_t **event;
367
368 if (!evt || !(event = evt->GetEvent())) {
369 info.GetReturnValue().Set(false);
370 } else {
371 string session_uuid;
372
373 if (info.Length() > 1) {
374 if (!info[1].IsEmpty() && info[1]->IsObject()) {
375 /* The second argument is a session object */
376 FSSession *sess = JSBase::GetInstance<FSSession>(info[1]->ToObject());
377 switch_core_session_t *tmp;
378
379 if (sess && (tmp = sess->GetSession())) {
380 session_uuid = switch_core_session_get_uuid(tmp);
381 }
382 } else {
383 /* The second argument is a session uuid string */
384 String::Utf8Value str(info[1]);
385 session_uuid = js_safe_str(*str);
386 }
387 }
388
389 if (session_uuid.length() > 0) {
390 /* This is a session event */
391 switch_core_session_t *session;
392 switch_status_t status = SWITCH_STATUS_FALSE;
393
394 if ((session = switch_core_session_locate(session_uuid.c_str()))) {
395 if ((status = switch_core_session_queue_private_event(session, event, SWITCH_FALSE)) == SWITCH_STATUS_SUCCESS) {
396 info.GetReturnValue().Set(true);
397 } else {
398 info.GetReturnValue().Set(false);
399 }
400 switch_core_session_rwunlock(session);
401 } else {
402 info.GetReturnValue().Set(false);
403 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid session id [%s]\n", switch_str_nil(session_uuid.c_str()));
404 }
405 } else {
406 /* "Normal" event */
407 switch_event_fire(event);
408 }
409 }
410 }
411 }
412 }
413
JS_EVENTHANDLER_FUNCTION_IMPL(ExecuteApi)414 JS_EVENTHANDLER_FUNCTION_IMPL(ExecuteApi)
415 {
416 if (info.Length() > 0) {
417 String::Utf8Value str(info[0]);
418 const char *cmd = js_safe_str(*str);
419 string arg;
420 switch_stream_handle_t stream = { 0 };
421
422 if (!strcasecmp(cmd, "jsapi")) {
423 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Possible recursive API Call is not allowed\n");
424 info.GetReturnValue().Set(false);
425 return;
426 }
427
428 if (info.Length() > 1) {
429 String::Utf8Value str2(info[1]);
430 arg = js_safe_str(*str2);
431 }
432
433 SWITCH_STANDARD_STREAM(stream);
434 switch_api_execute(cmd, arg.c_str(), NULL, &stream);
435
436 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), switch_str_nil((char *) stream.data)));
437 switch_safe_free(stream.data);
438 } else {
439 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), "-ERR"));
440 }
441 }
442
api_exec(switch_thread_t * thread,void * obj)443 static void *SWITCH_THREAD_FUNC api_exec(switch_thread_t *thread, void *obj)
444 {
445 api_command_struct_t *acs = (api_command_struct_t *) obj;
446 switch_stream_handle_t stream = { 0 };
447 char *reply, *freply = NULL;
448 switch_status_t status;
449 switch_event_t *event;
450
451 if (!acs) {
452 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Internal error.\n");
453 return NULL;
454 }
455
456 acs->ack = 1;
457
458 SWITCH_STANDARD_STREAM(stream);
459
460 status = switch_api_execute(acs->cmd, acs->arg, NULL, &stream);
461
462 if (status == SWITCH_STATUS_SUCCESS) {
463 reply = (char *)stream.data;
464 } else {
465 freply = switch_mprintf("-ERR %s Command not found!\n", acs->cmd);
466 reply = freply;
467 }
468
469 if (!reply) {
470 reply = (char *)"Command returned no output!";
471 }
472
473 if (switch_event_create(&event, SWITCH_EVENT_BACKGROUND_JOB) == SWITCH_STATUS_SUCCESS) {
474 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Job-UUID", acs->uuid_str);
475 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Job-Command", acs->cmd);
476 if (acs->arg) {
477 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Job-Command-Arg", acs->arg);
478 }
479 switch_event_add_body(event, "%s", reply);
480 switch_event_fire(&event);
481 }
482
483 switch_safe_free(stream.data);
484 switch_safe_free(freply);
485
486 switch_memory_pool_t *pool = acs->pool;
487 if (acs->ack == -1) {
488 int sanity = 2000;
489 while (acs->ack == -1) {
490 switch_cond_next();
491 if (--sanity <= 0)
492 break;
493 }
494 }
495
496 acs = NULL;
497 switch_core_destroy_memory_pool(&pool);
498 pool = NULL;
499
500 return NULL;
501 }
502
JS_EVENTHANDLER_FUNCTION_IMPL(ExecuteBgApi)503 JS_EVENTHANDLER_FUNCTION_IMPL(ExecuteBgApi)
504 {
505 string cmd;
506 string arg;
507 string jobuuid;
508 api_command_struct_t *acs = NULL;
509 switch_memory_pool_t *pool;
510 switch_thread_t *thread;
511 switch_threadattr_t *thd_attr = NULL;
512 switch_uuid_t uuid;
513 int sanity = 2000;
514
515 if (info.Length() > 0) {
516 String::Utf8Value str(info[0]);
517 cmd = js_safe_str(*str);
518
519 if (info.Length() > 1) {
520 String::Utf8Value str2(info[1]);
521 arg = js_safe_str(*str2);
522 }
523
524 if (info.Length() > 2) {
525 String::Utf8Value str2(info[2]);
526 jobuuid = js_safe_str(*str2);
527 }
528 } else {
529 info.GetReturnValue().Set(false);
530 return;
531 }
532
533 if (cmd.length() == 0) {
534 info.GetReturnValue().Set(false);
535 return;
536 }
537
538 switch_core_new_memory_pool(&pool);
539 acs = (api_command_struct_t *)switch_core_alloc(pool, sizeof(*acs));
540 switch_assert(acs);
541 acs->pool = pool;
542
543 acs->cmd = switch_core_strdup(acs->pool, cmd.c_str());
544
545 if (arg.c_str()) {
546 acs->arg = switch_core_strdup(acs->pool, arg.c_str());
547 }
548
549 switch_threadattr_create(&thd_attr, acs->pool);
550 switch_threadattr_detach_set(thd_attr, 1);
551 switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
552
553 if (jobuuid.length() > 0) {
554 switch_copy_string(acs->uuid_str, jobuuid.c_str(), sizeof(acs->uuid_str));
555 } else {
556 switch_uuid_get(&uuid);
557 switch_uuid_format(acs->uuid_str, &uuid);
558 }
559
560 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), acs->uuid_str));
561
562 switch_thread_create(&thread, thd_attr, api_exec, acs, acs->pool);
563
564 while (!acs->ack) {
565 switch_cond_next();
566 if (--sanity <= 0)
567 break;
568 }
569
570 if (acs->ack == -1) {
571 acs->ack--;
572 }
573 }
574
JS_EVENTHANDLER_FUNCTION_IMPL_STATIC(Destroy)575 JS_EVENTHANDLER_FUNCTION_IMPL_STATIC(Destroy)
576 {
577 JS_CHECK_SCRIPT_STATE();
578
579 FSEventHandler *obj = JSBase::GetInstance<FSEventHandler>(info.Holder());
580
581 if (obj) {
582 delete obj;
583 info.GetReturnValue().Set(true);
584 } else {
585 info.GetReturnValue().Set(false);
586 }
587 }
588
JS_EVENTHANDLER_GET_PROPERTY_IMPL_STATIC(GetReadyProperty)589 JS_EVENTHANDLER_GET_PROPERTY_IMPL_STATIC(GetReadyProperty)
590 {
591 JS_CHECK_SCRIPT_STATE();
592
593 FSEventHandler *obj = JSBase::GetInstance<FSEventHandler>(info.Holder());
594
595 if (obj) {
596 info.GetReturnValue().Set(true);
597 } else {
598 info.GetReturnValue().Set(false);
599 }
600 }
601
602 static const js_function_t eventhandler_methods[] = {
603 {"subscribe", FSEventHandler::Subscribe},
604 {"unSubscribe", FSEventHandler::UnSubscribe},
605 {"addFilter", FSEventHandler::AddFilter},
606 {"deleteFilter", FSEventHandler::DeleteFilter},
607 {"getEvent", FSEventHandler::GetEvent},
608 {"sendEvent", FSEventHandler::SendEvent},
609 {"executeApi", FSEventHandler::ExecuteApi},
610 {"executeBgApi", FSEventHandler::ExecuteBgApi},
611 {"destroy", FSEventHandler::Destroy},
612 {0}
613 };
614
615 static const js_property_t eventhandler_props[] = {
616 {"ready", FSEventHandler::GetReadyProperty, JSBase::DefaultSetProperty},
617 {0}
618 };
619
620 static const js_class_definition_t eventhandler_desc = {
621 js_class_name,
622 FSEventHandler::Construct,
623 eventhandler_methods,
624 eventhandler_props
625 };
626
eventhandler_load(const v8::FunctionCallbackInfo<Value> & info)627 static switch_status_t eventhandler_load(const v8::FunctionCallbackInfo<Value>& info)
628 {
629 JSBase::Register(info.GetIsolate(), &eventhandler_desc);
630 return SWITCH_STATUS_SUCCESS;
631 }
632
633 static const v8_mod_interface_t eventhandler_module_interface = {
634 /*.name = */ js_class_name,
635 /*.js_mod_load */ eventhandler_load
636 };
637
GetModuleInterface()638 const v8_mod_interface_t *FSEventHandler::GetModuleInterface()
639 {
640 return &eventhandler_module_interface;
641 }
642
643 /* For Emacs:
644 * Local Variables:
645 * mode:c
646 * indent-tabs-mode:t
647 * tab-width:4
648 * c-basic-offset:4
649 * End:
650 * For VIM:
651 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
652 */
653