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