/* * GPAC - Multimedia Framework C SDK * * Authors: Jean Le Feuvre * Copyright (c) Telecom ParisTech 2009-2012 * All rights reserved * * This file is part of GPAC / Platinum UPnP module * * GPAC is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * GPAC is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * * ---------------------------------------------------------------------------------- * PLATINUM IS LICENSED UNDER GPL or commercial agreement - cf platinum license * ---------------------------------------------------------------------------------- * */ #include "GenericDevice.h" #include "GPACPlatinum.h" //#include "PltXmlHelper.h" NPT_SET_LOCAL_LOGGER("gpac.genericdevice") GPAC_ServiceItem::GPAC_ServiceItem(GPAC_DeviceItem *device, PLT_Service *service) : m_device(device), m_service(service) { #ifdef GPAC_HAS_SPIDERMONKEY obj = NULL; on_event = JSVAL_NULL; m_StateListeners = gf_list_new(); m_ArgListeners = gf_list_new(); subscribed = GF_FALSE; vars=NULL; #endif } GPAC_ServiceItem::~GPAC_ServiceItem() { #ifdef GPAC_HAS_SPIDERMONKEY DetachJS(); gf_list_del(m_StateListeners); gf_list_del(m_ArgListeners); #endif } #ifdef GPAC_HAS_SPIDERMONKEY void GPAC_ServiceItem::DetachJS() { if (obj) { gf_js_remove_root(js_ctx, &obj, GF_JSGC_OBJECT); SMJS_SET_PRIVATE(js_ctx, obj, NULL); obj = NULL; } if (!JSVAL_IS_NULL(on_event)) { gf_js_remove_root(js_ctx, &on_event, GF_JSGC_VAL); on_event = JSVAL_NULL; } while (gf_list_count(m_StateListeners)) { GPAC_StateVariableListener *svl = (GPAC_StateVariableListener *)gf_list_get(m_StateListeners, 0); gf_list_rem(m_StateListeners, 0); gf_js_remove_root(js_ctx, &svl->on_event, GF_JSGC_VAL); delete svl; } while (gf_list_count(m_ArgListeners)) { GPAC_ActionArgListener *argl = (GPAC_ActionArgListener *)gf_list_get(m_ArgListeners, 0); gf_list_rem(m_ArgListeners, 0); gf_js_remove_root(js_ctx, &argl->on_event, GF_JSGC_VAL); delete argl; } } #endif GPAC_DeviceItem::GPAC_DeviceItem(PLT_DeviceDataReference device, NPT_String uuid) : m_device(device), m_UUID(uuid) { #ifdef GPAC_HAS_SPIDERMONKEY obj = NULL; js_ctx = NULL; m_Services = gf_list_new(); #endif } GPAC_DeviceItem::~GPAC_DeviceItem() { #ifdef GPAC_HAS_SPIDERMONKEY DetachJS(); #endif gf_list_del(m_Services); } #ifdef GPAC_HAS_SPIDERMONKEY void GPAC_DeviceItem::DetachJS() { if (obj) { gf_js_remove_root(js_ctx, &obj, GF_JSGC_OBJECT); SMJS_SET_PRIVATE(js_ctx, obj, NULL); obj = NULL; } while (gf_list_count(m_Services)) { GPAC_ServiceItem *item = (GPAC_ServiceItem*)gf_list_get(m_Services, 0); gf_list_rem(m_Services, 0); delete item; } } #endif #ifdef GPAC_HAS_SPIDERMONKEY static JSBool SMJS_FUNCTION(upnp_service_set_listener) { GPAC_StateVariableListener *svl = NULL; char *name=NULL; u32 i; SMJS_OBJ SMJS_ARGS GPAC_ServiceItem *service = (GPAC_ServiceItem *)SMJS_GET_PRIVATE(c, obj); if (!service || !argc || !JSVAL_IS_OBJECT(argv[0])) return JS_FALSE; if (argc<1) { if (!JSVAL_IS_NULL(service->on_event)) gf_js_remove_root(c, &service->on_event, GF_JSGC_VAL); service->on_event = JSVAL_NULL; if (!JSVAL_IS_NULL(argv[0])) { service->on_event = argv[0]; gf_js_add_root(c, &service->on_event, GF_JSGC_VAL); if (!service->subscribed) { service->m_device->m_pUPnP->m_pGenericController->m_CtrlPoint->Subscribe(service->m_service); service->subscribed = GF_TRUE; } } return JS_TRUE; } if (!JSVAL_IS_STRING(argv[1])) return JS_FALSE; name = SMJS_CHARS(c, argv[1]); if (!name) return JS_FALSE; /*variable state listener*/ i=0; while ((svl = (GPAC_StateVariableListener *)gf_list_enum(service->m_StateListeners, &i))) { if (svl->name == (const char *) name) break; } if (!svl) { svl = new GPAC_StateVariableListener(); svl->name = name; svl->var = service->m_service->FindStateVariable(name); gf_list_add(service->m_StateListeners, svl); } if (!JSVAL_IS_NULL(svl->on_event)) gf_js_remove_root(c, &svl->on_event, GF_JSGC_VAL); if (JSVAL_IS_NULL(argv[0])) { gf_list_del_item(service->m_StateListeners, svl); delete svl; } svl->on_event = argv[0]; gf_js_add_root(c, &svl->on_event, GF_JSGC_VAL); if (!service->subscribed) { service->m_device->m_pUPnP->m_pGenericController->m_CtrlPoint->Subscribe(service->m_service); service->subscribed = GF_TRUE; } SMJS_FREE(c, name); return JS_TRUE; } static JSBool SMJS_FUNCTION(upnp_service_set_action_listener) { PLT_ActionDesc *action; PLT_ArgumentDesc *desc; GPAC_ActionArgListener *argl = NULL; char *name; Bool script_callback = GF_FALSE; u32 i; SMJS_OBJ SMJS_ARGS GPAC_ServiceItem *service = (GPAC_ServiceItem *)SMJS_GET_PRIVATE(c, obj); if (!service || (argc<2) || !JSVAL_IS_STRING(argv[0]) || !JSVAL_IS_OBJECT(argv[1])) return JS_FALSE; name = SMJS_CHARS(c, argv[0]); if (!name) return JS_FALSE; action = service->m_service->FindActionDesc(name); SMJS_FREE(c, name); if (!action) return JS_FALSE; desc = NULL; if (argc==3) { if (JSVAL_IS_BOOLEAN(argv[2])) { script_callback = GF_TRUE; } else { if (!JSVAL_IS_STRING(argv[2]) ) return JS_FALSE; name = SMJS_CHARS(c, argv[2]); if (!name) return JS_FALSE; desc = action->GetArgumentDesc(name); SMJS_FREE(c, name); if (!desc) return JS_FALSE; } } /*action listener*/ i=0; while ((argl = (GPAC_ActionArgListener *)gf_list_enum(service->m_ArgListeners, &i))) { if (argl->arg == desc) break; argl = NULL; } if (!argl) { argl = new GPAC_ActionArgListener(); argl->arg = desc; gf_list_add(service->m_ArgListeners, argl); } argl->action = action; if (!JSVAL_IS_NULL(argl->on_event)) gf_js_remove_root(c, &argl->on_event, GF_JSGC_VAL); if (JSVAL_IS_NULL(argv[1])) { gf_list_del_item(service->m_ArgListeners, argl); delete argl; } argl->on_event = argv[1]; argl->is_script = script_callback; gf_js_add_root(c, &argl->on_event, GF_JSGC_VAL); return JS_TRUE; } static JSBool SMJS_FUNCTION(upnp_service_get_scpd) { NPT_String name; SMJS_OBJ GPAC_ServiceItem *service = (GPAC_ServiceItem *)SMJS_GET_PRIVATE(c, obj); if (!service ) return JS_FALSE; service->m_service->GetSCPDXML(name); SMJS_SET_RVAL( STRING_TO_JSVAL( JS_NewStringCopyZ(c, name) ) ); return JS_TRUE; } SMJS_DECL_FUNC_PROP_GET( upnpservice_getProperty) { #ifdef USE_FFDEV_15 JSObject *obj = (JSObject *) __hobj; jsid id = (jsid) __hid; #endif char *prop_name; GPAC_ServiceItem *service = (GPAC_ServiceItem *)SMJS_GET_PRIVATE(c, obj); if (!service) return JS_FALSE; if (!SMJS_ID_IS_STRING(id)) return JS_TRUE; prop_name = SMJS_CHARS_FROM_STRING(c, SMJS_ID_TO_STRING(id)); if (!prop_name) return JS_FALSE; if (!strcmp(prop_name, "Device")) { VPASSIGN( OBJECT_TO_JSVAL(service->m_device->obj) ); } else if (!strcmp(prop_name, "ModifiedStateVariablesCount")) { VPASSIGN( INT_TO_JSVAL(service->vars ? service->vars->GetItemCount() : 0) ); } SMJS_FREE(c, prop_name); return JS_TRUE; } static JSBool SMJS_FUNCTION(upnp_service_has_var) { char *name = NULL; SMJS_OBJ SMJS_ARGS GPAC_ServiceItem *service = (GPAC_ServiceItem *)SMJS_GET_PRIVATE(c, obj); if (!service || !argc || !JSVAL_IS_STRING(argv[0]) ) return JS_FALSE; name = SMJS_CHARS(c, argv[0]); SMJS_SET_RVAL( BOOLEAN_TO_JSVAL( (service->m_service->FindStateVariable(name)==NULL) ? JS_FALSE : JS_TRUE ) ); SMJS_FREE(c, name); return JS_TRUE; } static JSBool SMJS_FUNCTION(upnp_service_has_action) { PLT_ActionDesc *action; char *name = NULL; SMJS_OBJ SMJS_ARGS GPAC_ServiceItem *service = (GPAC_ServiceItem *)SMJS_GET_PRIVATE(c, obj); if (!service || !argc || !JSVAL_IS_STRING(argv[0]) ) return JS_FALSE; name = SMJS_CHARS(c, argv[0]); action = service->m_service->FindActionDesc(name); SMJS_FREE(c, name); SMJS_SET_RVAL( BOOLEAN_TO_JSVAL(action ? JS_TRUE : JS_FALSE) ); if (!action) return JS_TRUE; if ((argc==2) && JSVAL_IS_STRING(argv[1])) { name = SMJS_CHARS(c, argv[1]); if (action->GetArgumentDesc(name) == NULL) SMJS_SET_RVAL( BOOLEAN_TO_JSVAL(JS_FALSE) ); SMJS_FREE(c, name); } return JS_TRUE; } static JSBool SMJS_FUNCTION(upnp_service_call_action) { GPAC_ActionUDTA *act_udta = NULL; char *action_name = NULL; SMJS_OBJ SMJS_ARGS GPAC_ServiceItem *service = (GPAC_ServiceItem *)SMJS_GET_PRIVATE(c, obj); if (!service || !argc || !JSVAL_IS_STRING(argv[0]) ) return JS_FALSE; action_name = SMJS_CHARS(c, argv[0]); PLT_ActionDesc* action_desc = service->m_service->FindActionDesc(action_name); SMJS_FREE(c, action_name); if (action_desc == NULL) return JS_FALSE; PLT_ActionReference action; NPT_CHECK_SEVERE( service->m_device->m_pUPnP->m_pGenericController->m_CtrlPoint->CreateAction( service->m_device->m_device, service->m_service->GetServiceType(), action_name, action) ); if ((argc>=2) && JSVAL_IS_OBJECT(argv[1])) { JSObject *list = JSVAL_TO_OBJECT(argv[1]); u32 i, count; JS_GetArrayLength(c, list, (jsuint*) &count); GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[UPnP] Calling %s(", action_name)); i=0; while (i+2<=count) { NPT_Result res; jsval an_arg; char *param_val, *_param_val = NULL; char szParamVal[1024]; JS_GetElement(c, list, (jsint) i, &an_arg); char *param_name = SMJS_CHARS(c, an_arg); JS_GetElement(c, list, (jsint) i+1, &an_arg); param_val = (char*)""; if (JSVAL_IS_STRING(an_arg)) { param_val = _param_val = SMJS_CHARS(c, an_arg); } else if (JSVAL_IS_BOOLEAN(an_arg)) { param_val = (char *) ((JSVAL_TO_BOOLEAN(an_arg) == JS_TRUE) ? "true" : "false"); } else if (JSVAL_IS_INT(argv[1])) { sprintf(szParamVal, "%d", JSVAL_TO_INT(an_arg)); param_val = szParamVal; } else if (JSVAL_IS_NUMBER(an_arg)) { jsdouble v; JS_ValueToNumber(c, an_arg, &v); sprintf(szParamVal, "%g", v); param_val = szParamVal; } if (!param_name || !param_val) res = NPT_FAILURE; else { GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, (" %s(%s)", param_name, param_val)); res = action->SetArgumentValue(param_name, param_val); } SMJS_FREE(c, param_name); SMJS_FREE(c, _param_val); if (res != NPT_SUCCESS) return JS_FALSE; i+=2; } GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, (" )\n")); } if ((argc==3) && JSVAL_IS_OBJECT(argv[2])) { act_udta = new GPAC_ActionUDTA(); act_udta->udta = argv[2]; gf_js_add_root(c, &act_udta->udta, GF_JSGC_VAL); } service->m_device->m_pUPnP->m_pGenericController->m_CtrlPoint->InvokeAction(action, act_udta); return JS_TRUE; } #endif void GPAC_DeviceItem::RefreshServiceList() { u32 i; NPT_Array services = m_device->GetServices(); for (i=0; iGetServiceType()); } } GPAC_ServiceItem *GPAC_DeviceItem::FindService(const char *type) { u32 i, count; GPAC_ServiceItem *serv; count = gf_list_count(m_Services); for (i=0; im_service->GetServiceType() == type) return serv; } PLT_Service *service; if (m_device->FindServiceByType(type, service) != NPT_SUCCESS) return NULL; serv = new GPAC_ServiceItem(this, service); #ifdef GPAC_HAS_SPIDERMONKEY serv->js_ctx = js_ctx; serv->obj = JS_NewObject(serv->js_ctx, &m_pUPnP->upnpServiceClass._class, 0, obj); gf_js_add_root(serv->js_ctx, &serv->obj, GF_JSGC_OBJECT); SMJS_SET_PRIVATE(serv->js_ctx, serv->obj, serv); JS_DefineProperty(serv->js_ctx, serv->obj, "Name", STRING_TO_JSVAL( JS_NewStringCopyZ(serv->js_ctx, service->GetServiceID()) ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); JS_DefineProperty(serv->js_ctx, serv->obj, "Type", STRING_TO_JSVAL( JS_NewStringCopyZ(serv->js_ctx, service->GetServiceType()) ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); 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); JS_DefineFunction(serv->js_ctx, serv->obj, "SetStateVariableListener", upnp_service_set_listener, 1, 0); JS_DefineFunction(serv->js_ctx, serv->obj, "HasStateVariable", upnp_service_has_var, 1, 0); JS_DefineFunction(serv->js_ctx, serv->obj, "HasAction", upnp_service_has_action, 2, 0); JS_DefineFunction(serv->js_ctx, serv->obj, "CallAction", upnp_service_call_action, 2, 0); JS_DefineFunction(serv->js_ctx, serv->obj, "SetActionListener", upnp_service_set_action_listener, 2, 0); JS_DefineFunction(serv->js_ctx, serv->obj, "GetSCPD", upnp_service_get_scpd, 1, 0); #endif gf_list_add(m_Services, serv); return serv; } GPAC_GenericController::GPAC_GenericController(PLT_CtrlPointReference& ctrlPoint, GF_UPnP *upnp) { m_pUPnP = upnp; m_CtrlPoint = ctrlPoint; m_CtrlPoint->AddListener(this); m_ControlPointLock = gf_mx_new("GenericController"); m_Devices = gf_list_new(); } GPAC_GenericController::~GPAC_GenericController() { m_CtrlPoint->RemoveListener(this); gf_mx_del(m_ControlPointLock); while (gf_list_count(m_Devices)) { GPAC_DeviceItem*ms = (GPAC_DeviceItem*)gf_list_get(m_Devices, 0); gf_list_rem(m_Devices, 0); delete ms; } gf_list_del(m_Devices); } NPT_Result GPAC_GenericController::OnDeviceAdded(PLT_DeviceDataReference& device) { GPAC_DeviceItem *item; NPT_String uuid = device->GetUUID(); gf_mx_p(m_ControlPointLock); u32 i, count = gf_list_count(m_Devices); for (i=0; im_UUID == uuid ) { gf_mx_v(m_ControlPointLock); return NPT_SUCCESS; } } item = new GPAC_DeviceItem(device, device->GetUUID() ); gf_list_add(m_Devices, item ); m_pUPnP->OnDeviceAdd(item, 1); gf_mx_v(m_ControlPointLock); return NPT_SUCCESS; } NPT_Result GPAC_GenericController::OnDeviceRemoved(PLT_DeviceDataReference& device) { u32 i, count; GPAC_DeviceItem *item = NULL; NPT_String uuid = device->GetUUID(); gf_mx_p(m_ControlPointLock); count = gf_list_count(m_Devices); for (i=0; im_UUID == uuid ) { gf_list_rem(m_Devices, i); break; } item = NULL; } if (item) { m_pUPnP->OnDeviceAdd(item, 0); delete item; } gf_mx_v(m_ControlPointLock); return NPT_SUCCESS; } #ifdef GPAC_HAS_SPIDERMONKEY static JSBool SMJS_FUNCTION(upnp_action_get_argument_value) { NPT_String res; char *arg_name = NULL; SMJS_OBJ SMJS_ARGS PLT_Action *action = (PLT_Action *) SMJS_GET_PRIVATE(c, obj); if (!action || !argc || !JSVAL_IS_STRING(argv[0])) return JS_FALSE; arg_name = SMJS_CHARS(c, argv[0]); if (!arg_name) return JS_FALSE; action->GetArgumentValue(arg_name, res); SMJS_SET_RVAL( STRING_TO_JSVAL( JS_NewStringCopyZ(c, res) )); SMJS_FREE(c, arg_name); return JS_TRUE; } static JSBool SMJS_FUNCTION(upnp_action_get_error_code) { NPT_String res; SMJS_OBJ PLT_Action *action = (PLT_Action *) SMJS_GET_PRIVATE(c, obj); if (!action ) return JS_FALSE; SMJS_SET_RVAL( INT_TO_JSVAL( action->GetErrorCode() )); return JS_TRUE; } static JSBool SMJS_FUNCTION(upnp_action_get_error) { NPT_String res; unsigned int code; SMJS_OBJ PLT_Action *action = (PLT_Action *) SMJS_GET_PRIVATE(c, obj); if (!action ) return JS_FALSE; SMJS_SET_RVAL( STRING_TO_JSVAL( JS_NewStringCopyZ(c, action->GetError( &code ) ) ) ); return JS_TRUE; } #endif NPT_Result GPAC_GenericController::OnActionResponse(NPT_Result res, PLT_ActionReference& action, void* userdata) { #ifdef GPAC_HAS_SPIDERMONKEY u32 i, count; GPAC_DeviceItem *item = NULL; GPAC_ServiceItem *serv = NULL; GPAC_ActionArgListener *argl, *act_l; PLT_Service* service = action->GetActionDesc().GetService(); NPT_String uuid; GPAC_ActionUDTA *act_udta = (GPAC_ActionUDTA *)userdata; /*this is NOT an actionResponse to an action triggered on a generic device*/ if (act_udta && act_udta->m_Reserved) act_udta = NULL; GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[UPnP] Receive %s Response - error code %d\n", (char *) action->GetActionDesc().GetName(), res)); gf_mx_p(m_ControlPointLock); /*get our device*/ uuid = service->GetDevice()->GetUUID(); count = gf_list_count(m_Devices); for (i=0; im_UUID == uuid ) { break; } item = NULL; } gf_mx_v(m_ControlPointLock); if (!item) { GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[UPnP] Receive %s Response on unknown device (uuid %s)\n", (char *) action->GetActionDesc().GetName(), (char *) uuid)); goto exit; } /*get our service*/ count = gf_list_count(item->m_Services); for (i=0; im_Services, i); if (serv->m_service == service) break; } if (!serv) { GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[UPnP] Receive %s Response on unknown service %s\n", (char *) action->GetActionDesc().GetName(), (char *) service->GetServiceType())); goto exit; } /*locate our listeners*/ act_l = NULL; i=0; while ((argl = (GPAC_ActionArgListener *)gf_list_enum(serv->m_ArgListeners, &i))) { NPT_String value; GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[UPnP] checking argument %s\n", (char *) argl->action->GetName() )); if (argl->action->GetName() != action->GetActionDesc().GetName() ) continue; /*global action listener*/ if (argl->arg==NULL) { act_l = argl; continue; } /*if error don't trigger listeners*/ if (res != NPT_SUCCESS) { GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[UPnP] Receive %s Response: error on remote device %d\n", (char *) action->GetActionDesc().GetName(), res)); continue; } /*action arg listener*/ if (action->GetArgumentValue(argl->arg->GetName(), value) == NPT_SUCCESS) { jsval argv[1], rval; 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() )); m_pUPnP->LockJavascript(GF_TRUE); argv[0] = STRING_TO_JSVAL( JS_NewStringCopyZ(serv->js_ctx, value) ); JS_CallFunctionValue(serv->js_ctx, serv->obj, argl->on_event, 1, argv, &rval); m_pUPnP->LockJavascript(GF_FALSE); } else { 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() )); } } if (act_l) { jsval rval; m_pUPnP->LockJavascript(GF_TRUE); if (act_l->is_script) { JSObject *act_obj; jsval argv[2]; GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[UPnP] Calling handler for response %s\n", (char *) action->GetActionDesc().GetName())); act_obj = JS_NewObject(serv->js_ctx, &item->m_pUPnP->upnpDeviceClass._class, 0, item->obj); SMJS_SET_PRIVATE(serv->js_ctx, act_obj, (void *)action.AsPointer() ); JS_DefineFunction(serv->js_ctx, act_obj, "GetArgumentValue", upnp_action_get_argument_value, 1, 0); JS_DefineFunction(serv->js_ctx, act_obj, "GetErrorCode", upnp_action_get_error_code, 1, 0); JS_DefineFunction(serv->js_ctx, act_obj, "GetError", upnp_action_get_error, 1, 0); gf_js_add_root(serv->js_ctx, &act_obj, GF_JSGC_OBJECT); argv[0] = OBJECT_TO_JSVAL(act_obj); if (act_udta) { argv[1] = act_udta->udta; JS_CallFunctionValue(serv->js_ctx, serv->obj, act_l->on_event, 2, argv, &rval); } else { JS_CallFunctionValue(serv->js_ctx, serv->obj, act_l->on_event, 1, argv, &rval); } gf_js_remove_root(serv->js_ctx, &act_obj, GF_JSGC_OBJECT); } /*if error don't trigger listeners*/ else if (res == NPT_SUCCESS) { GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[UPnP] Calling handler for response %s\n", (char *) action->GetActionDesc().GetName())); JS_CallFunctionValue(serv->js_ctx, serv->obj, act_l->on_event, 0, 0, &rval); } else { GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[UPnP] response %s has error %d\n", (char *) action->GetActionDesc().GetName(), res )); } m_pUPnP->LockJavascript(GF_FALSE); } GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[UPnP] Done processing response %s\n", (char *) action->GetActionDesc().GetName())); exit: if (act_udta) { gf_js_remove_root(serv->js_ctx, &act_udta->udta, GF_JSGC_VAL); delete act_udta; } return NPT_SUCCESS; #else return NPT_SUCCESS; #endif } NPT_Result GPAC_GenericController::OnEventNotify(PLT_Service* service, NPT_List* vars) { #ifdef GPAC_HAS_SPIDERMONKEY u32 i, count; GPAC_DeviceItem *item = NULL; GPAC_ServiceItem *serv = NULL; GPAC_StateVariableListener *svl; gf_mx_p(m_ControlPointLock); NPT_String uuid = service->GetDevice()->GetUUID(); count = gf_list_count(m_Devices); for (i=0; im_UUID == uuid ) { break; } item = NULL; } gf_mx_v(m_ControlPointLock); if (!item) return NPT_SUCCESS; count = gf_list_count(item->m_Services); for (i=0; im_Services, i); if (serv->m_service == service) break; } if (!serv) return NPT_SUCCESS; if (!JSVAL_IS_NULL(serv->on_event)) { jsval rval; m_pUPnP->LockJavascript(GF_TRUE); serv->vars = vars; JS_CallFunctionValue(serv->js_ctx, serv->obj, serv->on_event, 0, 0, &rval); m_pUPnP->LockJavascript(GF_FALSE); serv->vars = NULL; } i=0; while ((svl = (GPAC_StateVariableListener *)gf_list_enum(serv->m_StateListeners, &i))) { /*check if we can find our var in this list*/ if (vars->Contains(svl->var)) { jsval argv[1], rval; m_pUPnP->LockJavascript(GF_TRUE); argv[0] = STRING_TO_JSVAL( JS_NewStringCopyZ(serv->js_ctx, svl->var->GetValue() ) ); JS_CallFunctionValue(serv->js_ctx, serv->obj, svl->on_event, 1, argv, &rval); m_pUPnP->LockJavascript(GF_FALSE); } } #endif return NPT_SUCCESS; } GPAC_Service::GPAC_Service(PLT_DeviceData* device, const char *type, const char *id, const char *name, const char *last_change_namespace) : PLT_Service(device, type, id, name, last_change_namespace) { #ifdef GPAC_HAS_SPIDERMONKEY m_pObj = NULL; m_pCtx = NULL; #endif } GPAC_Service::~GPAC_Service() { #ifdef GPAC_HAS_SPIDERMONKEY if (m_pObj) gf_js_remove_root(m_pCtx, &m_pObj, GF_JSGC_OBJECT); #endif } #ifdef GPAC_HAS_SPIDERMONKEY static JSBool SMJS_FUNCTION(upnp_service_set_state_variable) { char *name, *val; SMJS_OBJ SMJS_ARGS GPAC_Service* service = (GPAC_Service*) SMJS_GET_PRIVATE(c, obj); if (!service) return JS_FALSE; name = SMJS_CHARS(c, argv[0]); if (!name) return JS_FALSE; val = SMJS_CHARS(c, argv[1]); if (!val) { SMJS_FREE(c, name); return JS_FALSE; } service->SetStateVariable(name, val); SMJS_FREE(c, name); SMJS_FREE(c, val); return JS_TRUE; } void GPAC_Service::SetupJS(JSContext *c, GF_UPnP *upnp, JSObject *parent) { m_pCtx = c; m_pObj = JS_NewObject(c, &upnp->upnpDeviceClass._class, 0, parent); gf_js_add_root(m_pCtx, &m_pObj, GF_JSGC_OBJECT); SMJS_SET_PRIVATE(c, m_pObj, this); JS_DefineFunction(c, m_pObj, "SetStateVariable", upnp_service_set_state_variable, 2, 0); } #endif GPAC_GenericDevice::GPAC_GenericDevice(const char* FriendlyName, const char *device_id) : PLT_DeviceHost("/", "", device_id ? device_id : "urn:schemas-upnp-org:device:GenericDevice:1", FriendlyName) { m_pServices = gf_list_new(); #ifdef GPAC_HAS_SPIDERMONKEY run_proc = JSVAL_NULL; act_proc = JSVAL_NULL; obj = NULL; js_source = ""; act_ref = NULL; m_pSema = NULL; m_pMutex = gf_mx_new("UPnP Generic Device"); #endif } GPAC_GenericDevice::~GPAC_GenericDevice() { gf_list_del(m_pServices); #ifdef GPAC_HAS_SPIDERMONKEY if (m_pSema) gf_sema_del(m_pSema); m_pSema = NULL; gf_mx_del(m_pMutex); #endif } #ifdef GPAC_HAS_SPIDERMONKEY void GPAC_GenericDevice::DetachJS(JSContext *c) { u32 i, count; if (obj) gf_js_remove_root(c, &obj, GF_JSGC_OBJECT); obj = NULL; if (!JSVAL_IS_NULL(run_proc)) gf_js_remove_root(c, &run_proc, GF_JSGC_VAL); run_proc = JSVAL_NULL; if (!JSVAL_IS_NULL(act_proc)) gf_js_remove_root(c, &act_proc, GF_JSGC_VAL); act_proc = JSVAL_NULL; count = gf_list_count(m_pServices); for (i=0; im_pObj) { gf_js_remove_root(c, &service->m_pObj, GF_JSGC_OBJECT); service->m_pObj = NULL; } } } #endif NPT_Result GPAC_GenericDevice::SetupServices() { u32 i, count; count = gf_list_count(m_pServices); for (i=0; iact_ref->GetArgumentValue(act_name, value) != NPT_SUCCESS) return JS_FALSE; SMJS_SET_RVAL( STRING_TO_JSVAL( JS_NewStringCopyZ(c, value) )); SMJS_FREE(c, act_name); return JS_TRUE; } static JSBool SMJS_FUNCTION(upnp_action_send_reply) { SMJS_OBJ SMJS_ARGS GPAC_GenericDevice *device = (GPAC_GenericDevice *)SMJS_GET_PRIVATE(c, obj); if (!device) return JS_FALSE; if (argc && JSVAL_IS_OBJECT(argv[0]) ) { JSObject *list = JSVAL_TO_OBJECT(argv[0]); u32 i, count; JS_GetArrayLength(c, list, (jsuint*) &count); GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[UPnP] Calling response %s(", (char *) device->act_ref->GetActionDesc().GetName())); i=0; while (i+2<=count) { jsval an_arg; NPT_Result res; char *param_val, *_param_val = NULL; char szParamVal[1024]; JS_GetElement(c, list, (jsint) i, &an_arg); char *param_name = SMJS_CHARS(c, an_arg); JS_GetElement(c, list, (jsint) i+1, &an_arg); param_val = (char*)""; if (JSVAL_IS_STRING(an_arg)) { param_val = _param_val = SMJS_CHARS(c, an_arg); } else if (JSVAL_IS_BOOLEAN(an_arg)) { param_val = (char *) ((JSVAL_TO_BOOLEAN(an_arg) == JS_TRUE) ? "true" : "false"); } else if (JSVAL_IS_INT(argv[1])) { sprintf(szParamVal, "%d", JSVAL_TO_INT(an_arg)); param_val = szParamVal; } else if (JSVAL_IS_NUMBER(an_arg)) { jsdouble v; JS_ValueToNumber(c, an_arg, &v); sprintf(szParamVal, "%g", v); param_val = szParamVal; } if (!param_name || !param_val) res = NPT_FAILURE; else { GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, (" %s(%s)", param_name, param_val)); res = device->act_ref->SetArgumentValue(param_name, param_val); } SMJS_FREE(c, param_name); SMJS_FREE(c, _param_val); if (res != NPT_SUCCESS) return JS_FALSE; i+=2; } GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, (" )\n")); } //notify we are ready if (device->m_pSema) { gf_sema_notify(device->m_pSema, 1); } return JS_TRUE; } #endif NPT_Result GPAC_GenericDevice::OnAction(PLT_ActionReference& action, const PLT_HttpRequestContext& context) { NPT_COMPILER_UNUSED(context); #ifdef GPAC_HAS_SPIDERMONKEY gf_mx_p(m_pMutex); #endif PLT_ActionDesc &act_desc = action->GetActionDesc(); NPT_String name = act_desc.GetName(); #ifdef GPAC_HAS_SPIDERMONKEY assert(!m_pSema); #endif GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[UPnP] Action %s called (thread %d)\n", (char *) name, gf_th_id() )); #ifdef GPAC_HAS_SPIDERMONKEY if (JSVAL_IS_NULL(act_proc)) { gf_mx_v(m_pMutex); return NPT_SUCCESS; } jsval argv[2]; m_pUPnP->LockJavascript(GF_TRUE); JSObject *js_action = JS_NewObject(m_pUPnP->m_pJSCtx, &m_pUPnP->upnpDeviceClass._class, 0, 0); argv[0] = OBJECT_TO_JSVAL(js_action); SMJS_SET_PRIVATE(m_pUPnP->m_pJSCtx, js_action, this); act_ref = action; 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); GPAC_Service *service = (GPAC_Service *) act_desc.GetService(); 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); JS_DefineFunction(m_pUPnP->m_pJSCtx, js_action, "GetArgument", upnp_action_get_argument, 1, 0); JS_DefineFunction(m_pUPnP->m_pJSCtx, js_action, "SendReply", upnp_action_send_reply, 1, 0); /*create a semaphore*/ m_pSema = gf_sema_new(1, 0); jsval rval; JS_CallFunctionValue(m_pUPnP->m_pJSCtx, obj, act_proc, 1, argv, &rval); SMJS_SET_PRIVATE(m_pUPnP->m_pJSCtx, js_action, NULL); m_pUPnP->LockJavascript(GF_FALSE); if (JSVAL_IS_INT(rval) && (JSVAL_TO_INT(rval) != 0)) { action->SetError(JSVAL_TO_INT(rval), "Action Failed"); } /*wait on the semaphore*/ if (!gf_sema_wait_for(m_pSema, 10000)) { GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[UPnP] Reply processing to action %s timeout - sending incomplete reply)\n", (char *) name)); } gf_sema_del(m_pSema); m_pSema = NULL; gf_mx_v(m_pMutex); #endif return NPT_SUCCESS; }