1 /*
2 * GPAC - Multimedia Framework C SDK
3 *
4 * Authors: Jean Le Feuvre
5 * Copyright (c) Telecom ParisTech 2009-2012
6 * All rights reserved
7 *
8 * This file is part of GPAC / Platinum UPnP module
9 *
10 * GPAC is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2, or (at your option)
13 * any later version.
14 *
15 * GPAC is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; see the file COPYING. If not, write to
22 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 *
25 * ----------------------------------------------------------------------------------
26 * PLATINUM IS LICENSED UNDER GPL or commercial agreement - cf platinum license
27 * ----------------------------------------------------------------------------------
28 *
29 */
30
31
32 #include "GenericDevice.h"
33 #include "GPACPlatinum.h"
34 //#include "PltXmlHelper.h"
35
36 NPT_SET_LOCAL_LOGGER("gpac.genericdevice")
37
GPAC_ServiceItem(GPAC_DeviceItem * device,PLT_Service * service)38 GPAC_ServiceItem::GPAC_ServiceItem(GPAC_DeviceItem *device, PLT_Service *service) : m_device(device), m_service(service)
39 {
40 #ifdef GPAC_HAS_SPIDERMONKEY
41 obj = NULL;
42 on_event = JSVAL_NULL;
43 m_StateListeners = gf_list_new();
44 m_ArgListeners = gf_list_new();
45 subscribed = GF_FALSE;
46 vars=NULL;
47 #endif
48 }
49
~GPAC_ServiceItem()50 GPAC_ServiceItem::~GPAC_ServiceItem()
51 {
52 #ifdef GPAC_HAS_SPIDERMONKEY
53 DetachJS();
54 gf_list_del(m_StateListeners);
55 gf_list_del(m_ArgListeners);
56 #endif
57 }
58
59 #ifdef GPAC_HAS_SPIDERMONKEY
DetachJS()60 void GPAC_ServiceItem::DetachJS()
61 {
62 if (obj) {
63 gf_js_remove_root(js_ctx, &obj, GF_JSGC_OBJECT);
64 SMJS_SET_PRIVATE(js_ctx, obj, NULL);
65 obj = NULL;
66 }
67 if (!JSVAL_IS_NULL(on_event)) {
68 gf_js_remove_root(js_ctx, &on_event, GF_JSGC_VAL);
69 on_event = JSVAL_NULL;
70 }
71 while (gf_list_count(m_StateListeners)) {
72 GPAC_StateVariableListener *svl = (GPAC_StateVariableListener *)gf_list_get(m_StateListeners, 0);
73 gf_list_rem(m_StateListeners, 0);
74 gf_js_remove_root(js_ctx, &svl->on_event, GF_JSGC_VAL);
75 delete svl;
76 }
77 while (gf_list_count(m_ArgListeners)) {
78 GPAC_ActionArgListener *argl = (GPAC_ActionArgListener *)gf_list_get(m_ArgListeners, 0);
79 gf_list_rem(m_ArgListeners, 0);
80 gf_js_remove_root(js_ctx, &argl->on_event, GF_JSGC_VAL);
81 delete argl;
82 }
83 }
84 #endif
85
86
GPAC_DeviceItem(PLT_DeviceDataReference device,NPT_String uuid)87 GPAC_DeviceItem::GPAC_DeviceItem(PLT_DeviceDataReference device, NPT_String uuid)
88 : m_device(device), m_UUID(uuid)
89 {
90 #ifdef GPAC_HAS_SPIDERMONKEY
91 obj = NULL;
92 js_ctx = NULL;
93 m_Services = gf_list_new();
94 #endif
95 }
96
~GPAC_DeviceItem()97 GPAC_DeviceItem::~GPAC_DeviceItem()
98 {
99 #ifdef GPAC_HAS_SPIDERMONKEY
100 DetachJS();
101 #endif
102 gf_list_del(m_Services);
103 }
104
105 #ifdef GPAC_HAS_SPIDERMONKEY
DetachJS()106 void GPAC_DeviceItem::DetachJS() {
107 if (obj) {
108 gf_js_remove_root(js_ctx, &obj, GF_JSGC_OBJECT);
109 SMJS_SET_PRIVATE(js_ctx, obj, NULL);
110 obj = NULL;
111 }
112 while (gf_list_count(m_Services)) {
113 GPAC_ServiceItem *item = (GPAC_ServiceItem*)gf_list_get(m_Services, 0);
114 gf_list_rem(m_Services, 0);
115 delete item;
116 }
117 }
118 #endif
119
120 #ifdef GPAC_HAS_SPIDERMONKEY
SMJS_FUNCTION(upnp_service_set_listener)121 static JSBool SMJS_FUNCTION(upnp_service_set_listener)
122 {
123 GPAC_StateVariableListener *svl = NULL;
124 char *name=NULL;
125 u32 i;
126 SMJS_OBJ
127 SMJS_ARGS
128 GPAC_ServiceItem *service = (GPAC_ServiceItem *)SMJS_GET_PRIVATE(c, obj);
129 if (!service || !argc || !JSVAL_IS_OBJECT(argv[0])) return JS_FALSE;
130
131 if (argc<1) {
132 if (!JSVAL_IS_NULL(service->on_event))
133 gf_js_remove_root(c, &service->on_event, GF_JSGC_VAL);
134 service->on_event = JSVAL_NULL;
135 if (!JSVAL_IS_NULL(argv[0])) {
136 service->on_event = argv[0];
137 gf_js_add_root(c, &service->on_event, GF_JSGC_VAL);
138 if (!service->subscribed) {
139 service->m_device->m_pUPnP->m_pGenericController->m_CtrlPoint->Subscribe(service->m_service);
140 service->subscribed = GF_TRUE;
141 }
142 }
143 return JS_TRUE;
144 }
145 if (!JSVAL_IS_STRING(argv[1])) return JS_FALSE;
146 name = SMJS_CHARS(c, argv[1]);
147 if (!name) return JS_FALSE;
148
149 /*variable state listener*/
150 i=0;
151 while ((svl = (GPAC_StateVariableListener *)gf_list_enum(service->m_StateListeners, &i))) {
152 if (svl->name == (const char *) name) break;
153 }
154 if (!svl) {
155 svl = new GPAC_StateVariableListener();
156 svl->name = name;
157 svl->var = service->m_service->FindStateVariable(name);
158 gf_list_add(service->m_StateListeners, svl);
159 }
160 if (!JSVAL_IS_NULL(svl->on_event))
161 gf_js_remove_root(c, &svl->on_event, GF_JSGC_VAL);
162 if (JSVAL_IS_NULL(argv[0])) {
163 gf_list_del_item(service->m_StateListeners, svl);
164 delete svl;
165 }
166 svl->on_event = argv[0];
167 gf_js_add_root(c, &svl->on_event, GF_JSGC_VAL);
168 if (!service->subscribed) {
169 service->m_device->m_pUPnP->m_pGenericController->m_CtrlPoint->Subscribe(service->m_service);
170 service->subscribed = GF_TRUE;
171 }
172 SMJS_FREE(c, name);
173 return JS_TRUE;
174 }
175
SMJS_FUNCTION(upnp_service_set_action_listener)176 static JSBool SMJS_FUNCTION(upnp_service_set_action_listener)
177 {
178 PLT_ActionDesc *action;
179 PLT_ArgumentDesc *desc;
180 GPAC_ActionArgListener *argl = NULL;
181 char *name;
182 Bool script_callback = GF_FALSE;
183 u32 i;
184 SMJS_OBJ
185 SMJS_ARGS
186 GPAC_ServiceItem *service = (GPAC_ServiceItem *)SMJS_GET_PRIVATE(c, obj);
187 if (!service || (argc<2) || !JSVAL_IS_STRING(argv[0]) || !JSVAL_IS_OBJECT(argv[1])) return JS_FALSE;
188
189 name = SMJS_CHARS(c, argv[0]);
190 if (!name) return JS_FALSE;
191
192 action = service->m_service->FindActionDesc(name);
193 SMJS_FREE(c, name);
194
195 if (!action) return JS_FALSE;
196
197 desc = NULL;
198 if (argc==3) {
199 if (JSVAL_IS_BOOLEAN(argv[2])) {
200 script_callback = GF_TRUE;
201 } else {
202 if (!JSVAL_IS_STRING(argv[2]) ) return JS_FALSE;
203 name = SMJS_CHARS(c, argv[2]);
204 if (!name) return JS_FALSE;
205 desc = action->GetArgumentDesc(name);
206 SMJS_FREE(c, name);
207 if (!desc) return JS_FALSE;
208 }
209 }
210
211
212 /*action listener*/
213 i=0;
214 while ((argl = (GPAC_ActionArgListener *)gf_list_enum(service->m_ArgListeners, &i))) {
215 if (argl->arg == desc) break;
216 argl = NULL;
217 }
218 if (!argl) {
219 argl = new GPAC_ActionArgListener();
220 argl->arg = desc;
221 gf_list_add(service->m_ArgListeners, argl);
222 }
223 argl->action = action;
224 if (!JSVAL_IS_NULL(argl->on_event))
225 gf_js_remove_root(c, &argl->on_event, GF_JSGC_VAL);
226 if (JSVAL_IS_NULL(argv[1])) {
227 gf_list_del_item(service->m_ArgListeners, argl);
228 delete argl;
229 }
230 argl->on_event = argv[1];
231 argl->is_script = script_callback;
232 gf_js_add_root(c, &argl->on_event, GF_JSGC_VAL);
233 return JS_TRUE;
234 }
235
SMJS_FUNCTION(upnp_service_get_scpd)236 static JSBool SMJS_FUNCTION(upnp_service_get_scpd)
237 {
238 NPT_String name;
239 SMJS_OBJ
240 GPAC_ServiceItem *service = (GPAC_ServiceItem *)SMJS_GET_PRIVATE(c, obj);
241 if (!service )
242 return JS_FALSE;
243
244 service->m_service->GetSCPDXML(name);
245 SMJS_SET_RVAL( STRING_TO_JSVAL( JS_NewStringCopyZ(c, name) ) );
246 return JS_TRUE;
247 }
248
SMJS_DECL_FUNC_PROP_GET(upnpservice_getProperty)249 SMJS_DECL_FUNC_PROP_GET( upnpservice_getProperty)
250 {
251 #ifdef USE_FFDEV_15
252 JSObject *obj = (JSObject *) __hobj;
253 jsid id = (jsid) __hid;
254 #endif
255 char *prop_name;
256 GPAC_ServiceItem *service = (GPAC_ServiceItem *)SMJS_GET_PRIVATE(c, obj);
257 if (!service) return JS_FALSE;
258
259 if (!SMJS_ID_IS_STRING(id)) return JS_TRUE;
260 prop_name = SMJS_CHARS_FROM_STRING(c, SMJS_ID_TO_STRING(id));
261 if (!prop_name) return JS_FALSE;
262
263 if (!strcmp(prop_name, "Device")) {
264 VPASSIGN( OBJECT_TO_JSVAL(service->m_device->obj) );
265 }
266 else if (!strcmp(prop_name, "ModifiedStateVariablesCount")) {
267 VPASSIGN( INT_TO_JSVAL(service->vars ? service->vars->GetItemCount() : 0) );
268 }
269
270 SMJS_FREE(c, prop_name);
271 return JS_TRUE;
272 }
273
274
SMJS_FUNCTION(upnp_service_has_var)275 static JSBool SMJS_FUNCTION(upnp_service_has_var)
276 {
277 char *name = NULL;
278 SMJS_OBJ
279 SMJS_ARGS
280 GPAC_ServiceItem *service = (GPAC_ServiceItem *)SMJS_GET_PRIVATE(c, obj);
281 if (!service || !argc || !JSVAL_IS_STRING(argv[0]) )
282 return JS_FALSE;
283
284 name = SMJS_CHARS(c, argv[0]);
285 SMJS_SET_RVAL( BOOLEAN_TO_JSVAL( (service->m_service->FindStateVariable(name)==NULL) ? JS_FALSE : JS_TRUE ) );
286 SMJS_FREE(c, name);
287 return JS_TRUE;
288 }
289
SMJS_FUNCTION(upnp_service_has_action)290 static JSBool SMJS_FUNCTION(upnp_service_has_action)
291 {
292 PLT_ActionDesc *action;
293 char *name = NULL;
294 SMJS_OBJ
295 SMJS_ARGS
296 GPAC_ServiceItem *service = (GPAC_ServiceItem *)SMJS_GET_PRIVATE(c, obj);
297 if (!service || !argc || !JSVAL_IS_STRING(argv[0]) )
298 return JS_FALSE;
299
300 name = SMJS_CHARS(c, argv[0]);
301 action = service->m_service->FindActionDesc(name);
302 SMJS_FREE(c, name);
303
304 SMJS_SET_RVAL( BOOLEAN_TO_JSVAL(action ? JS_TRUE : JS_FALSE) );
305 if (!action) return JS_TRUE;
306
307 if ((argc==2) && JSVAL_IS_STRING(argv[1])) {
308 name = SMJS_CHARS(c, argv[1]);
309 if (action->GetArgumentDesc(name) == NULL) SMJS_SET_RVAL( BOOLEAN_TO_JSVAL(JS_FALSE) );
310 SMJS_FREE(c, name);
311 }
312 return JS_TRUE;
313 }
314
SMJS_FUNCTION(upnp_service_call_action)315 static JSBool SMJS_FUNCTION(upnp_service_call_action)
316 {
317 GPAC_ActionUDTA *act_udta = NULL;
318 char *action_name = NULL;
319 SMJS_OBJ
320 SMJS_ARGS
321 GPAC_ServiceItem *service = (GPAC_ServiceItem *)SMJS_GET_PRIVATE(c, obj);
322 if (!service || !argc || !JSVAL_IS_STRING(argv[0]) ) return JS_FALSE;
323
324 action_name = SMJS_CHARS(c, argv[0]);
325 PLT_ActionDesc* action_desc = service->m_service->FindActionDesc(action_name);
326 SMJS_FREE(c, action_name);
327
328 if (action_desc == NULL) return JS_FALSE;
329 PLT_ActionReference action;
330
331 NPT_CHECK_SEVERE(
332 service->m_device->m_pUPnP->m_pGenericController->m_CtrlPoint->CreateAction(
333 service->m_device->m_device,
334 service->m_service->GetServiceType(),
335 action_name,
336 action)
337 );
338
339
340 if ((argc>=2) && JSVAL_IS_OBJECT(argv[1])) {
341 JSObject *list = JSVAL_TO_OBJECT(argv[1]);
342 u32 i, count;
343 JS_GetArrayLength(c, list, (jsuint*) &count);
344
345 GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[UPnP] Calling %s(", action_name));
346 i=0;
347 while (i+2<=count) {
348 NPT_Result res;
349 jsval an_arg;
350 char *param_val, *_param_val = NULL;
351 char szParamVal[1024];
352 JS_GetElement(c, list, (jsint) i, &an_arg);
353 char *param_name = SMJS_CHARS(c, an_arg);
354
355 JS_GetElement(c, list, (jsint) i+1, &an_arg);
356
357 param_val = (char*)"";
358 if (JSVAL_IS_STRING(an_arg)) {
359 param_val = _param_val = SMJS_CHARS(c, an_arg);
360 } else if (JSVAL_IS_BOOLEAN(an_arg)) {
361 param_val = (char *) ((JSVAL_TO_BOOLEAN(an_arg) == JS_TRUE) ? "true" : "false");
362 }
363 else if (JSVAL_IS_INT(argv[1])) {
364 sprintf(szParamVal, "%d", JSVAL_TO_INT(an_arg));
365 param_val = szParamVal;
366 }
367 else if (JSVAL_IS_NUMBER(an_arg)) {
368 jsdouble v;
369 JS_ValueToNumber(c, an_arg, &v);
370 sprintf(szParamVal, "%g", v);
371 param_val = szParamVal;
372 }
373
374 if (!param_name || !param_val) res = NPT_FAILURE;
375 else {
376 GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, (" %s(%s)", param_name, param_val));
377 res = action->SetArgumentValue(param_name, param_val);
378 }
379 SMJS_FREE(c, param_name);
380 SMJS_FREE(c, _param_val);
381
382 if (res != NPT_SUCCESS) return JS_FALSE;
383
384 i+=2;
385 }
386 GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, (" )\n"));
387 }
388
389 if ((argc==3) && JSVAL_IS_OBJECT(argv[2])) {
390 act_udta = new GPAC_ActionUDTA();
391 act_udta->udta = argv[2];
392 gf_js_add_root(c, &act_udta->udta, GF_JSGC_VAL);
393 }
394 service->m_device->m_pUPnP->m_pGenericController->m_CtrlPoint->InvokeAction(action, act_udta);
395 return JS_TRUE;
396 }
397
398 #endif
399
RefreshServiceList()400 void GPAC_DeviceItem::RefreshServiceList()
401 {
402 u32 i;
403 NPT_Array<PLT_Service*> services = m_device->GetServices();
404
405 for (i=0; i<services.GetItemCount(); i++) {
406 PLT_Service *serv = services[i];
407 FindService(serv->GetServiceType());
408 }
409 }
410
FindService(const char * type)411 GPAC_ServiceItem *GPAC_DeviceItem::FindService(const char *type)
412 {
413 u32 i, count;
414 GPAC_ServiceItem *serv;
415
416 count = gf_list_count(m_Services);
417 for (i=0; i<count; i++) {
418 serv = (GPAC_ServiceItem*)gf_list_get(m_Services, i);
419 if (serv->m_service->GetServiceType() == type)
420 return serv;
421 }
422
423 PLT_Service *service;
424 if (m_device->FindServiceByType(type, service) != NPT_SUCCESS) return NULL;
425
426 serv = new GPAC_ServiceItem(this, service);
427
428 #ifdef GPAC_HAS_SPIDERMONKEY
429 serv->js_ctx = js_ctx;
430 serv->obj = JS_NewObject(serv->js_ctx, &m_pUPnP->upnpServiceClass._class, 0, obj);
431 gf_js_add_root(serv->js_ctx, &serv->obj, GF_JSGC_OBJECT);
432 SMJS_SET_PRIVATE(serv->js_ctx, serv->obj, serv);
433 JS_DefineProperty(serv->js_ctx, serv->obj, "Name", STRING_TO_JSVAL( JS_NewStringCopyZ(serv->js_ctx, service->GetServiceID()) ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
434 JS_DefineProperty(serv->js_ctx, serv->obj, "Type", STRING_TO_JSVAL( JS_NewStringCopyZ(serv->js_ctx, service->GetServiceType()) ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
435 JS_DefineProperty(serv->js_ctx, serv->obj, "Hostname", STRING_TO_JSVAL( JS_NewStringCopyZ(serv->js_ctx, m_device->GetURLBase().GetHost() ) ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
436 JS_DefineFunction(serv->js_ctx, serv->obj, "SetStateVariableListener", upnp_service_set_listener, 1, 0);
437 JS_DefineFunction(serv->js_ctx, serv->obj, "HasStateVariable", upnp_service_has_var, 1, 0);
438 JS_DefineFunction(serv->js_ctx, serv->obj, "HasAction", upnp_service_has_action, 2, 0);
439 JS_DefineFunction(serv->js_ctx, serv->obj, "CallAction", upnp_service_call_action, 2, 0);
440 JS_DefineFunction(serv->js_ctx, serv->obj, "SetActionListener", upnp_service_set_action_listener, 2, 0);
441 JS_DefineFunction(serv->js_ctx, serv->obj, "GetSCPD", upnp_service_get_scpd, 1, 0);
442
443 #endif
444
445 gf_list_add(m_Services, serv);
446 return serv;
447 }
448
449
GPAC_GenericController(PLT_CtrlPointReference & ctrlPoint,GF_UPnP * upnp)450 GPAC_GenericController::GPAC_GenericController(PLT_CtrlPointReference& ctrlPoint, GF_UPnP *upnp)
451 {
452 m_pUPnP = upnp;
453 m_CtrlPoint = ctrlPoint;
454 m_CtrlPoint->AddListener(this);
455 m_ControlPointLock = gf_mx_new("GenericController");
456 m_Devices = gf_list_new();
457 }
458
~GPAC_GenericController()459 GPAC_GenericController::~GPAC_GenericController()
460 {
461 m_CtrlPoint->RemoveListener(this);
462 gf_mx_del(m_ControlPointLock);
463 while (gf_list_count(m_Devices)) {
464 GPAC_DeviceItem*ms = (GPAC_DeviceItem*)gf_list_get(m_Devices, 0);
465 gf_list_rem(m_Devices, 0);
466 delete ms;
467 }
468 gf_list_del(m_Devices);
469 }
470
471
472
OnDeviceAdded(PLT_DeviceDataReference & device)473 NPT_Result GPAC_GenericController::OnDeviceAdded(PLT_DeviceDataReference& device)
474 {
475 GPAC_DeviceItem *item;
476 NPT_String uuid = device->GetUUID();
477 gf_mx_p(m_ControlPointLock);
478
479 u32 i, count = gf_list_count(m_Devices);
480 for (i=0; i<count; i++) {
481 item = (GPAC_DeviceItem *) gf_list_get(m_Devices, i);
482 if (item->m_UUID == uuid ) {
483 gf_mx_v(m_ControlPointLock);
484 return NPT_SUCCESS;
485 }
486 }
487 item = new GPAC_DeviceItem(device, device->GetUUID() );
488 gf_list_add(m_Devices, item );
489 m_pUPnP->OnDeviceAdd(item, 1);
490 gf_mx_v(m_ControlPointLock);
491 return NPT_SUCCESS;
492 }
OnDeviceRemoved(PLT_DeviceDataReference & device)493 NPT_Result GPAC_GenericController::OnDeviceRemoved(PLT_DeviceDataReference& device)
494 {
495 u32 i, count;
496 GPAC_DeviceItem *item = NULL;
497 NPT_String uuid = device->GetUUID();
498 gf_mx_p(m_ControlPointLock);
499 count = gf_list_count(m_Devices);
500 for (i=0; i<count; i++) {
501 item = (GPAC_DeviceItem *) gf_list_get(m_Devices, i);
502 if (item->m_UUID == uuid ) {
503 gf_list_rem(m_Devices, i);
504 break;
505 }
506 item = NULL;
507 }
508 if (item) {
509 m_pUPnP->OnDeviceAdd(item, 0);
510 delete item;
511 }
512 gf_mx_v(m_ControlPointLock);
513 return NPT_SUCCESS;
514 }
515
516
517 #ifdef GPAC_HAS_SPIDERMONKEY
518
SMJS_FUNCTION(upnp_action_get_argument_value)519 static JSBool SMJS_FUNCTION(upnp_action_get_argument_value)
520 {
521 NPT_String res;
522 char *arg_name = NULL;
523 SMJS_OBJ
524 SMJS_ARGS
525 PLT_Action *action = (PLT_Action *) SMJS_GET_PRIVATE(c, obj);
526 if (!action || !argc || !JSVAL_IS_STRING(argv[0])) return JS_FALSE;
527
528 arg_name = SMJS_CHARS(c, argv[0]);
529 if (!arg_name) return JS_FALSE;
530
531 action->GetArgumentValue(arg_name, res);
532 SMJS_SET_RVAL( STRING_TO_JSVAL( JS_NewStringCopyZ(c, res) ));
533 SMJS_FREE(c, arg_name);
534 return JS_TRUE;
535 }
SMJS_FUNCTION(upnp_action_get_error_code)536 static JSBool SMJS_FUNCTION(upnp_action_get_error_code)
537 {
538 NPT_String res;
539 SMJS_OBJ
540 PLT_Action *action = (PLT_Action *) SMJS_GET_PRIVATE(c, obj);
541 if (!action ) return JS_FALSE;
542 SMJS_SET_RVAL( INT_TO_JSVAL( action->GetErrorCode() ));
543 return JS_TRUE;
544 }
545
SMJS_FUNCTION(upnp_action_get_error)546 static JSBool SMJS_FUNCTION(upnp_action_get_error)
547 {
548 NPT_String res;
549 unsigned int code;
550 SMJS_OBJ
551 PLT_Action *action = (PLT_Action *) SMJS_GET_PRIVATE(c, obj);
552 if (!action ) return JS_FALSE;
553 SMJS_SET_RVAL( STRING_TO_JSVAL( JS_NewStringCopyZ(c, action->GetError( &code ) ) ) );
554 return JS_TRUE;
555 }
556
557 #endif
558
559
OnActionResponse(NPT_Result res,PLT_ActionReference & action,void * userdata)560 NPT_Result GPAC_GenericController::OnActionResponse(NPT_Result res, PLT_ActionReference& action, void* userdata)
561 {
562 #ifdef GPAC_HAS_SPIDERMONKEY
563 u32 i, count;
564 GPAC_DeviceItem *item = NULL;
565 GPAC_ServiceItem *serv = NULL;
566 GPAC_ActionArgListener *argl, *act_l;
567 PLT_Service* service = action->GetActionDesc().GetService();
568 NPT_String uuid;
569 GPAC_ActionUDTA *act_udta = (GPAC_ActionUDTA *)userdata;
570
571 /*this is NOT an actionResponse to an action triggered on a generic device*/
572 if (act_udta && act_udta->m_Reserved) act_udta = NULL;
573
574 GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[UPnP] Receive %s Response - error code %d\n", (char *) action->GetActionDesc().GetName(), res));
575
576 gf_mx_p(m_ControlPointLock);
577
578 /*get our device*/
579 uuid = service->GetDevice()->GetUUID();
580 count = gf_list_count(m_Devices);
581 for (i=0; i<count; i++) {
582 item = (GPAC_DeviceItem *) gf_list_get(m_Devices, i);
583 if (item->m_UUID == uuid ) {
584 break;
585 }
586 item = NULL;
587 }
588 gf_mx_v(m_ControlPointLock);
589
590 if (!item) {
591 GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[UPnP] Receive %s Response on unknown device (uuid %s)\n", (char *) action->GetActionDesc().GetName(), (char *) uuid));
592 goto exit;
593 }
594 /*get our service*/
595 count = gf_list_count(item->m_Services);
596 for (i=0; i<count; i++) {
597 serv = (GPAC_ServiceItem *)gf_list_get(item->m_Services, i);
598 if (serv->m_service == service) break;
599 }
600 if (!serv) {
601 GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[UPnP] Receive %s Response on unknown service %s\n", (char *) action->GetActionDesc().GetName(), (char *) service->GetServiceType()));
602 goto exit;
603 }
604
605 /*locate our listeners*/
606 act_l = NULL;
607 i=0;
608 while ((argl = (GPAC_ActionArgListener *)gf_list_enum(serv->m_ArgListeners, &i))) {
609 NPT_String value;
610 GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[UPnP] checking argument %s\n", (char *) argl->action->GetName() ));
611 if (argl->action->GetName() != action->GetActionDesc().GetName() ) continue;
612
613 /*global action listener*/
614 if (argl->arg==NULL) {
615 act_l = argl;
616 continue;
617 }
618 /*if error don't trigger listeners*/
619 if (res != NPT_SUCCESS) {
620 GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[UPnP] Receive %s Response: error on remote device %d\n", (char *) action->GetActionDesc().GetName(), res));
621 continue;
622 }
623 /*action arg listener*/
624 if (action->GetArgumentValue(argl->arg->GetName(), value) == NPT_SUCCESS) {
625 jsval argv[1], rval;
626
627 GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[UPnP] Calling handler for response %s argument %s\n", (char *) action->GetActionDesc().GetName(), (char *) argl->arg->GetName() ));
628 m_pUPnP->LockJavascript(GF_TRUE);
629 argv[0] = STRING_TO_JSVAL( JS_NewStringCopyZ(serv->js_ctx, value) );
630 JS_CallFunctionValue(serv->js_ctx, serv->obj, argl->on_event, 1, argv, &rval);
631 m_pUPnP->LockJavascript(GF_FALSE);
632 } else {
633 GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[UPnP] %s Response: couldn't get argument %s value\n", (char *) action->GetActionDesc().GetName(), (char *) argl->arg->GetName() ));
634 }
635 }
636
637 if (act_l) {
638 jsval rval;
639 m_pUPnP->LockJavascript(GF_TRUE);
640 if (act_l->is_script) {
641 JSObject *act_obj;
642 jsval argv[2];
643
644 GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[UPnP] Calling handler for response %s\n", (char *) action->GetActionDesc().GetName()));
645
646 act_obj = JS_NewObject(serv->js_ctx, &item->m_pUPnP->upnpDeviceClass._class, 0, item->obj);
647 SMJS_SET_PRIVATE(serv->js_ctx, act_obj, (void *)action.AsPointer() );
648 JS_DefineFunction(serv->js_ctx, act_obj, "GetArgumentValue", upnp_action_get_argument_value, 1, 0);
649 JS_DefineFunction(serv->js_ctx, act_obj, "GetErrorCode", upnp_action_get_error_code, 1, 0);
650 JS_DefineFunction(serv->js_ctx, act_obj, "GetError", upnp_action_get_error, 1, 0);
651
652 gf_js_add_root(serv->js_ctx, &act_obj, GF_JSGC_OBJECT);
653 argv[0] = OBJECT_TO_JSVAL(act_obj);
654 if (act_udta) {
655 argv[1] = act_udta->udta;
656 JS_CallFunctionValue(serv->js_ctx, serv->obj, act_l->on_event, 2, argv, &rval);
657 } else {
658 JS_CallFunctionValue(serv->js_ctx, serv->obj, act_l->on_event, 1, argv, &rval);
659 }
660 gf_js_remove_root(serv->js_ctx, &act_obj, GF_JSGC_OBJECT);
661 }
662 /*if error don't trigger listeners*/
663 else if (res == NPT_SUCCESS) {
664 GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[UPnP] Calling handler for response %s\n", (char *) action->GetActionDesc().GetName()));
665 JS_CallFunctionValue(serv->js_ctx, serv->obj, act_l->on_event, 0, 0, &rval);
666 }
667 else {
668 GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[UPnP] response %s has error %d\n", (char *) action->GetActionDesc().GetName(), res ));
669 }
670 m_pUPnP->LockJavascript(GF_FALSE);
671 }
672 GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[UPnP] Done processing response %s\n", (char *) action->GetActionDesc().GetName()));
673
674 exit:
675 if (act_udta) {
676 gf_js_remove_root(serv->js_ctx, &act_udta->udta, GF_JSGC_VAL);
677 delete act_udta;
678 }
679
680 return NPT_SUCCESS;
681 #else
682 return NPT_SUCCESS;
683 #endif
684 }
685
OnEventNotify(PLT_Service * service,NPT_List<PLT_StateVariable * > * vars)686 NPT_Result GPAC_GenericController::OnEventNotify(PLT_Service* service, NPT_List<PLT_StateVariable*>* vars)
687 {
688 #ifdef GPAC_HAS_SPIDERMONKEY
689 u32 i, count;
690 GPAC_DeviceItem *item = NULL;
691 GPAC_ServiceItem *serv = NULL;
692 GPAC_StateVariableListener *svl;
693
694 gf_mx_p(m_ControlPointLock);
695
696 NPT_String uuid = service->GetDevice()->GetUUID();
697 count = gf_list_count(m_Devices);
698 for (i=0; i<count; i++) {
699 item = (GPAC_DeviceItem *) gf_list_get(m_Devices, i);
700 if (item->m_UUID == uuid ) {
701 break;
702 }
703 item = NULL;
704 }
705 gf_mx_v(m_ControlPointLock);
706
707 if (!item) return NPT_SUCCESS;
708
709 count = gf_list_count(item->m_Services);
710 for (i=0; i<count; i++) {
711 serv = (GPAC_ServiceItem *)gf_list_get(item->m_Services, i);
712 if (serv->m_service == service) break;
713 }
714 if (!serv) return NPT_SUCCESS;
715
716 if (!JSVAL_IS_NULL(serv->on_event)) {
717 jsval rval;
718 m_pUPnP->LockJavascript(GF_TRUE);
719 serv->vars = vars;
720 JS_CallFunctionValue(serv->js_ctx, serv->obj, serv->on_event, 0, 0, &rval);
721 m_pUPnP->LockJavascript(GF_FALSE);
722 serv->vars = NULL;
723 }
724
725 i=0;
726 while ((svl = (GPAC_StateVariableListener *)gf_list_enum(serv->m_StateListeners, &i))) {
727 /*check if we can find our var in this list*/
728 if (vars->Contains(svl->var)) {
729 jsval argv[1], rval;
730 m_pUPnP->LockJavascript(GF_TRUE);
731 argv[0] = STRING_TO_JSVAL( JS_NewStringCopyZ(serv->js_ctx, svl->var->GetValue() ) );
732 JS_CallFunctionValue(serv->js_ctx, serv->obj, svl->on_event, 1, argv, &rval);
733 m_pUPnP->LockJavascript(GF_FALSE);
734 }
735
736 }
737
738 #endif
739 return NPT_SUCCESS;
740 }
741
742
GPAC_Service(PLT_DeviceData * device,const char * type,const char * id,const char * name,const char * last_change_namespace)743 GPAC_Service::GPAC_Service(PLT_DeviceData* device, const char *type, const char *id, const char *name, const char *last_change_namespace)
744 : PLT_Service(device, type, id, name, last_change_namespace)
745 {
746 #ifdef GPAC_HAS_SPIDERMONKEY
747 m_pObj = NULL;
748 m_pCtx = NULL;
749 #endif
750 }
751
~GPAC_Service()752 GPAC_Service::~GPAC_Service()
753 {
754 #ifdef GPAC_HAS_SPIDERMONKEY
755 if (m_pObj) gf_js_remove_root(m_pCtx, &m_pObj, GF_JSGC_OBJECT);
756 #endif
757 }
758
759 #ifdef GPAC_HAS_SPIDERMONKEY
760
761
SMJS_FUNCTION(upnp_service_set_state_variable)762 static JSBool SMJS_FUNCTION(upnp_service_set_state_variable)
763 {
764 char *name, *val;
765 SMJS_OBJ
766 SMJS_ARGS
767 GPAC_Service* service = (GPAC_Service*) SMJS_GET_PRIVATE(c, obj);
768 if (!service) return JS_FALSE;
769
770 name = SMJS_CHARS(c, argv[0]);
771 if (!name) return JS_FALSE;
772
773 val = SMJS_CHARS(c, argv[1]);
774 if (!val) {
775 SMJS_FREE(c, name);
776 return JS_FALSE;
777 }
778
779 service->SetStateVariable(name, val);
780 SMJS_FREE(c, name);
781 SMJS_FREE(c, val);
782 return JS_TRUE;
783 }
784
SetupJS(JSContext * c,GF_UPnP * upnp,JSObject * parent)785 void GPAC_Service::SetupJS(JSContext *c, GF_UPnP *upnp, JSObject *parent)
786 {
787 m_pCtx = c;
788 m_pObj = JS_NewObject(c, &upnp->upnpDeviceClass._class, 0, parent);
789 gf_js_add_root(m_pCtx, &m_pObj, GF_JSGC_OBJECT);
790 SMJS_SET_PRIVATE(c, m_pObj, this);
791 JS_DefineFunction(c, m_pObj, "SetStateVariable", upnp_service_set_state_variable, 2, 0);
792
793 }
794 #endif
795
796
GPAC_GenericDevice(const char * FriendlyName,const char * device_id)797 GPAC_GenericDevice::GPAC_GenericDevice(const char* FriendlyName, const char *device_id)
798 : PLT_DeviceHost("/", "", device_id ? device_id : "urn:schemas-upnp-org:device:GenericDevice:1", FriendlyName)
799 {
800 m_pServices = gf_list_new();
801
802 #ifdef GPAC_HAS_SPIDERMONKEY
803 run_proc = JSVAL_NULL;
804 act_proc = JSVAL_NULL;
805 obj = NULL;
806 js_source = "";
807 act_ref = NULL;
808 m_pSema = NULL;
809 m_pMutex = gf_mx_new("UPnP Generic Device");
810 #endif
811 }
812
~GPAC_GenericDevice()813 GPAC_GenericDevice::~GPAC_GenericDevice()
814 {
815 gf_list_del(m_pServices);
816 #ifdef GPAC_HAS_SPIDERMONKEY
817 if (m_pSema) gf_sema_del(m_pSema);
818 m_pSema = NULL;
819 gf_mx_del(m_pMutex);
820 #endif
821 }
822
823 #ifdef GPAC_HAS_SPIDERMONKEY
DetachJS(JSContext * c)824 void GPAC_GenericDevice::DetachJS(JSContext *c)
825 {
826 u32 i, count;
827 if (obj)
828 gf_js_remove_root(c, &obj, GF_JSGC_OBJECT);
829 obj = NULL;
830 if (!JSVAL_IS_NULL(run_proc))
831 gf_js_remove_root(c, &run_proc, GF_JSGC_VAL);
832 run_proc = JSVAL_NULL;
833 if (!JSVAL_IS_NULL(act_proc))
834 gf_js_remove_root(c, &act_proc, GF_JSGC_VAL);
835 act_proc = JSVAL_NULL;
836
837 count = gf_list_count(m_pServices);
838 for (i=0; i<count; i++) {
839 GPAC_Service *service = (GPAC_Service*)gf_list_get(m_pServices, i);
840 if (service->m_pObj) {
841 gf_js_remove_root(c, &service->m_pObj, GF_JSGC_OBJECT);
842 service->m_pObj = NULL;
843 }
844 }
845 }
846 #endif
847
848 NPT_Result
SetupServices()849 GPAC_GenericDevice::SetupServices()
850 {
851 u32 i, count;
852 count = gf_list_count(m_pServices);
853 for (i=0; i<count; i++) {
854 GPAC_Service *service = (GPAC_Service *)gf_list_get(m_pServices, i);
855 AddService(service);
856 }
857 return NPT_SUCCESS;
858 }
859
860
861 #ifdef GPAC_HAS_SPIDERMONKEY
SMJS_FUNCTION(upnp_action_get_argument)862 static JSBool SMJS_FUNCTION(upnp_action_get_argument)
863 {
864 char *act_name;
865 SMJS_OBJ
866 SMJS_ARGS
867 GPAC_GenericDevice *device = (GPAC_GenericDevice *)SMJS_GET_PRIVATE(c, obj);
868 if (!device || !argc || !JSVAL_IS_STRING(argv[0]) ) return JS_FALSE;
869
870 act_name = SMJS_CHARS(c, argv[0]);
871 NPT_String value;
872
873 if (device->act_ref->GetArgumentValue(act_name, value) != NPT_SUCCESS) return JS_FALSE;
874 SMJS_SET_RVAL( STRING_TO_JSVAL( JS_NewStringCopyZ(c, value) ));
875 SMJS_FREE(c, act_name);
876 return JS_TRUE;
877 }
878
SMJS_FUNCTION(upnp_action_send_reply)879 static JSBool SMJS_FUNCTION(upnp_action_send_reply)
880 {
881 SMJS_OBJ
882 SMJS_ARGS
883 GPAC_GenericDevice *device = (GPAC_GenericDevice *)SMJS_GET_PRIVATE(c, obj);
884 if (!device) return JS_FALSE;
885
886 if (argc && JSVAL_IS_OBJECT(argv[0]) ) {
887 JSObject *list = JSVAL_TO_OBJECT(argv[0]);
888 u32 i, count;
889 JS_GetArrayLength(c, list, (jsuint*) &count);
890
891 GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[UPnP] Calling response %s(", (char *) device->act_ref->GetActionDesc().GetName()));
892 i=0;
893 while (i+2<=count) {
894 jsval an_arg;
895 NPT_Result res;
896 char *param_val, *_param_val = NULL;
897 char szParamVal[1024];
898 JS_GetElement(c, list, (jsint) i, &an_arg);
899 char *param_name = SMJS_CHARS(c, an_arg);
900
901 JS_GetElement(c, list, (jsint) i+1, &an_arg);
902
903 param_val = (char*)"";
904 if (JSVAL_IS_STRING(an_arg)) {
905 param_val = _param_val = SMJS_CHARS(c, an_arg);
906 } else if (JSVAL_IS_BOOLEAN(an_arg)) {
907 param_val = (char *) ((JSVAL_TO_BOOLEAN(an_arg) == JS_TRUE) ? "true" : "false");
908 }
909 else if (JSVAL_IS_INT(argv[1])) {
910 sprintf(szParamVal, "%d", JSVAL_TO_INT(an_arg));
911 param_val = szParamVal;
912 }
913 else if (JSVAL_IS_NUMBER(an_arg)) {
914 jsdouble v;
915 JS_ValueToNumber(c, an_arg, &v);
916 sprintf(szParamVal, "%g", v);
917 param_val = szParamVal;
918 }
919
920 if (!param_name || !param_val) res = NPT_FAILURE;
921 else {
922 GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, (" %s(%s)", param_name, param_val));
923
924 res = device->act_ref->SetArgumentValue(param_name, param_val);
925 }
926 SMJS_FREE(c, param_name);
927 SMJS_FREE(c, _param_val);
928 if (res != NPT_SUCCESS) return JS_FALSE;
929 i+=2;
930 }
931 GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, (" )\n"));
932 }
933
934 //notify we are ready
935 if (device->m_pSema) {
936 gf_sema_notify(device->m_pSema, 1);
937 }
938 return JS_TRUE;
939 }
940 #endif
941
942
943 NPT_Result
OnAction(PLT_ActionReference & action,const PLT_HttpRequestContext & context)944 GPAC_GenericDevice::OnAction(PLT_ActionReference& action,
945 const PLT_HttpRequestContext& context)
946 {
947 NPT_COMPILER_UNUSED(context);
948
949 #ifdef GPAC_HAS_SPIDERMONKEY
950 gf_mx_p(m_pMutex);
951 #endif
952 PLT_ActionDesc &act_desc = action->GetActionDesc();
953 NPT_String name = act_desc.GetName();
954 #ifdef GPAC_HAS_SPIDERMONKEY
955 assert(!m_pSema);
956 #endif
957 GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[UPnP] Action %s called (thread %d)\n", (char *) name, gf_th_id() ));
958
959 #ifdef GPAC_HAS_SPIDERMONKEY
960 if (JSVAL_IS_NULL(act_proc)) {
961 gf_mx_v(m_pMutex);
962 return NPT_SUCCESS;
963 }
964
965 jsval argv[2];
966
967 m_pUPnP->LockJavascript(GF_TRUE);
968
969 JSObject *js_action = JS_NewObject(m_pUPnP->m_pJSCtx, &m_pUPnP->upnpDeviceClass._class, 0, 0);
970 argv[0] = OBJECT_TO_JSVAL(js_action);
971 SMJS_SET_PRIVATE(m_pUPnP->m_pJSCtx, js_action, this);
972
973 act_ref = action;
974
975 JS_DefineProperty(m_pUPnP->m_pJSCtx, js_action, "Name", STRING_TO_JSVAL( JS_NewStringCopyZ(m_pUPnP->m_pJSCtx, name) ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
976 GPAC_Service *service = (GPAC_Service *) act_desc.GetService();
977 JS_DefineProperty(m_pUPnP->m_pJSCtx, js_action, "Service", service->m_pObj ? OBJECT_TO_JSVAL( service->m_pObj) : JSVAL_NULL, 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
978 JS_DefineFunction(m_pUPnP->m_pJSCtx, js_action, "GetArgument", upnp_action_get_argument, 1, 0);
979 JS_DefineFunction(m_pUPnP->m_pJSCtx, js_action, "SendReply", upnp_action_send_reply, 1, 0);
980
981 /*create a semaphore*/
982 m_pSema = gf_sema_new(1, 0);
983
984 jsval rval;
985 JS_CallFunctionValue(m_pUPnP->m_pJSCtx, obj, act_proc, 1, argv, &rval);
986 SMJS_SET_PRIVATE(m_pUPnP->m_pJSCtx, js_action, NULL);
987 m_pUPnP->LockJavascript(GF_FALSE);
988
989 if (JSVAL_IS_INT(rval) && (JSVAL_TO_INT(rval) != 0)) {
990 action->SetError(JSVAL_TO_INT(rval), "Action Failed");
991 }
992 /*wait on the semaphore*/
993 if (!gf_sema_wait_for(m_pSema, 10000)) {
994 GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[UPnP] Reply processing to action %s timeout - sending incomplete reply)\n", (char *) name));
995 }
996 gf_sema_del(m_pSema);
997 m_pSema = NULL;
998
999 gf_mx_v(m_pMutex);
1000 #endif
1001 return NPT_SUCCESS;
1002 }
1003