From cc96edeb70fa2938a4d963524261142132ece540 Mon Sep 17 00:00:00 2001 From: Tucker Kern Date: Sun, 20 Oct 2019 15:04:47 -0600 Subject: [PATCH] Support for libupnp v1.8.x & v1.6.x (#190) * Compile against libupnp 1.8 (Patch from Debian, https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=884246) * Define a compatibility layer to allow libupnp v1.8.x & v1.6.x without relying on v1.6.26 (including compatibility macros for UPNP root device callback to eliminate warnings) * Add missing upnp_compat include for webserver.c * Fixed const warning in find_action * Set ActionResult after adding response data. Necessary since we've made a copy & modified the pointer * Fix action logging for libupnp-v1.8.x * Additional compatibility macros for VD callbacks to eliminate warnings due to changed callback prototype * Increase upnp_set_error buffer size to match LINE_SIZE. Also eliminate additional local vars * Clean up some of the weird formatting in upnp_device. Eliminate some unnecessary local vars. Closes #173 #170 #189 #176 --- src/main.c | 1 + src/upnp.c | 2 +- src/upnp.h | 7 ++- src/upnp_compat.h | 144 +++++++++++++++++++++++++++++++++++++++++++++ src/upnp_device.c | 147 ++++++++++++++++++++++++---------------------- src/webserver.c | 29 ++++----- 6 files changed, 244 insertions(+), 86 deletions(-) create mode 100644 src/upnp_compat.h diff --git a/src/main.c b/src/main.c index cf73b0f..8de787d 100644 --- a/src/main.c +++ b/src/main.c @@ -40,6 +40,7 @@ # error "To have gmrender any useful, you need to have libupnp installed." #endif +#include #include // For version strings of upnp and gstreamer diff --git a/src/upnp.c b/src/upnp.c index 6da69b8..708db98 100644 --- a/src/upnp.c +++ b/src/upnp.c @@ -194,7 +194,7 @@ static struct xmldoc *generate_scpd(struct service *srv) } struct action *find_action(struct service *event_service, - char *action_name) + const char *action_name) { struct action *event_action; int actionNum = 0; diff --git a/src/upnp.h b/src/upnp.h index 8c62978..1b06007 100644 --- a/src/upnp.h +++ b/src/upnp.h @@ -24,6 +24,9 @@ #ifndef _UPNP_H #define _UPNP_H +#include +#include "upnp_compat.h" + struct action; struct service; struct action_event; @@ -106,14 +109,14 @@ struct service { }; struct action_event { - struct Upnp_Action_Request *request; + UpnpActionRequest *request; int status; struct service *service; struct upnp_device *device; }; struct action *find_action(struct service *event_service, - char *action_name); + const char *action_name); char *upnp_get_scpd(struct service *srv); diff --git a/src/upnp_compat.h b/src/upnp_compat.h new file mode 100644 index 0000000..0bdb114 --- /dev/null +++ b/src/upnp_compat.h @@ -0,0 +1,144 @@ +/* upnp_compat.h - libupnp v1.8.x/v1.6.x compatibilty layer + * + * Copyright (C) 2019 Tucker Kern + * + * This file is part of GMediaRender. + * + * GMediaRender is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GMediaRender 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 Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GMediaRender; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + */ + +#ifndef _UPNP_COMPAT_H +#define _UPNP_COMPAT_H + +#include +#include + +#if UPNP_VERSION >= 10803 +#define UpnpAddVirtualDir(x) UpnpAddVirtualDir(x, NULL, NULL) +#define VD_GET_INFO_CALLBACK(NAME, FILENAME, INFO, COOKIE) int NAME(const char* FILENAME, UpnpFileInfo* INFO, const void* COOKIE) +#define VD_OPEN_CALLBACK(NAME, FILENAME, MODE, COOKIE) UpnpWebFileHandle NAME(const char* FILENAME, enum UpnpOpenFileMode MODE, const void* COOKIE) +#define VD_READ_CALLBACK(NAME, HANDLE, BUFFER, LENGTH, COOKIE) int NAME(UpnpWebFileHandle HANDLE, char* BUFFER, size_t LENGTH, const void* COOKIE) +#define VD_WRITE_CALLBACK(...) VD_READ_CALLBACK(__VA_ARGS__) +#define VD_SEEK_CALLBACK(NAME, HANDLE, OFFSET, ORIGIN, COOKIE) int NAME(UpnpWebFileHandle HANDLE, off_t OFFSET, int ORIGIN, const void* COOKIE) +#define VD_CLOSE_CALLBACK(NAME, HANDLE, COOKIE) int NAME(UpnpWebFileHandle HANDLE, const void* COOKIE) +#else +#define VD_GET_INFO_CALLBACK(NAME, FILENAME, INFO, COOKIE) int NAME(const char* FILENAME, UpnpFileInfo* INFO) +#define VD_OPEN_CALLBACK(NAME, FILENAME, MODE, COOKIE) UpnpWebFileHandle NAME(const char* FILENAME, enum UpnpOpenFileMode MODE) +#define VD_READ_CALLBACK(NAME, HANDLE, BUFFER, LENGTH, COOKIE) int NAME(UpnpWebFileHandle HANDLE, char* BUFFER, size_t LENGTH) +#define VD_WRITE_CALLBACK(...) VD_READ_CALLBACK(__VA_ARGS__) +#define VD_SEEK_CALLBACK(NAME, HANDLE, OFFSET, ORIGIN, COOKIE) int NAME(UpnpWebFileHandle HANDLE, off_t OFFSET, int ORIGIN) +#define VD_CLOSE_CALLBACK(NAME, HANDLE, COOKIE) int NAME(UpnpWebFileHandle HANDLE) +#endif + +#if UPNP_VERSION >= 10800 +#define UPNP_CALLBACK(NAME, TYPE, EVENT, COOKIE) int NAME(Upnp_EventType TYPE, const void* EVENT, void* COOKIE) +#else +#define UPNP_CALLBACK(NAME, TYPE, EVENT, COOKIE) int NAME(Upnp_EventType TYPE, void* EVENT, void* COOKIE) +#endif + +#if UPNP_VERSION < 10626 +// Compatibility defines from libupnp 1.6.26 to allow code targeting v1.8.x +// to compile for v1.6.x + +/* compat code for libupnp-1.8 */ +typedef struct Upnp_Action_Request UpnpActionRequest; +#define UpnpActionRequest_get_ErrCode(x) ((x)->ErrCode) +#define UpnpActionRequest_set_ErrCode(x, v) ((x)->ErrCode = (v)) +#define UpnpActionRequest_get_Socket(x) ((x)->Socket) +#define UpnpActionRequest_get_ErrStr_cstr(x) ((x)->ErrStr) +#define UpnpActionRequest_set_ErrStr(x, v) (strncpy((x)->ErrStr, UpnpString_get_String((v)), LINE_SIZE)) +#define UpnpActionRequest_get_ActionName_cstr(x) ((x)->ActionName) +#define UpnpActionRequest_get_DevUDN_cstr(x) ((x)->DevUDN) +#define UpnpActionRequest_get_ServiceID_cstr(x) ((x)->ServiceID) +#define UpnpActionRequest_get_ActionRequest(x) ((x)->ActionRequest) +#define UpnpActionRequest_set_ActionRequest(x, v) ((x)->ActionRequest = (v)) +#define UpnpActionRequest_get_ActionResult(x) ((x)->ActionResult) +#define UpnpActionRequest_set_ActionResult(x, v) ((x)->ActionResult = (v)) + +/* compat code for libupnp-1.8 */ +typedef struct Upnp_Action_Complete UpnpActionComplete; +#define UpnpActionComplete_get_ErrCode(x) ((x)->ErrCode) +#define UpnpActionComplete_get_CtrlUrl_cstr(x) ((x)->CtrlUrl) +#define UpnpActionComplete_get_ActionRequest(x) ((x)->ActionRequest) +#define UpnpActionComplete_get_ActionResult(x) ((x)->ActionResult) + +/* compat code for libupnp-1.8 */ +typedef struct Upnp_State_Var_Request UpnpStateVarRequest; +#define UpnpStateVarRequest_get_ErrCode(x) ((x)->ErrCode) +#define UpnpStateVarRequest_set_ErrCode(x, v) ((x)->ErrCode = (v)) +#define UpnpStateVarRequest_get_Socket(x) ((x)->Socket) +#define UpnpStateVarRequest_get_ErrStr_cstr(x) ((x)->ErrStr) +#define UpnpStateVarRequest_get_DevUDN_cstr(x) ((x)->DevUDN) +#define UpnpStateVarRequest_get_ServiceID_cstr(x) ((x)->ServiceID) +#define UpnpStateVarRequest_get_StateVarName_cstr(x) ((x)->StateVarName) +#define UpnpStateVarRequest_get_CurrentVal(x) ((x)->CurrentVal) +#define UpnpStateVarRequest_set_CurrentVal(x, v) ((x)->CurrentVal = (v)) + +/* compat code for libupnp-1.8 */ +typedef struct Upnp_State_Var_Complete UpnpStateVarComplete; +#define UpnpStateVarComplete_get_ErrCode(x) ((x)->ErrCode) +#define UpnpStateVarComplete_get_CtrlUrl_cstr(x) ((x)->CtrlUrl) +#define UpnpStateVarComplete_get_StateVarName_cstr(x) ((x)->StateVarName) + +/* compat code for libupnp-1.8 */ +typedef struct Upnp_Event UpnpEvent; +#define UpnpEvent_get_SID_cstr(x) ((x)->Sid) +#define UpnpEvent_get_EventKey(x) ((x)->EventKey) +#define UpnpEvent_get_ChangedVariables(x) ((x)->ChangedVariables) + +/* compat code for libupnp-1.8 */ +typedef struct Upnp_Discovery UpnpDiscovery; +#define UpnpDiscovery_get_ErrCode(x) ((x)->ErrCode) +#define UpnpDiscovery_get_Expires(x) ((x)->Expires) +#define UpnpDiscovery_get_DeviceID_cstr(x) ((x)->DeviceId) +#define UpnpDiscovery_get_DeviceType_cstr(x) ((x)->DeviceType) +#define UpnpDiscovery_get_ServiceType_cstr(x) ((x)->ServiceType) +#define UpnpDiscovery_get_ServiceVer_cstr(x) ((x)->ServiceVer) +#define UpnpDiscovery_get_Location_cstr(x) ((x)->Location) +#define UpnpDiscovery_get_Os_cstr(x) ((x)->Os) +#define UpnpDiscovery_get_Date_cstr(x) ((x)->Date) +#define UpnpDiscovery_get_Ext_cstr(x) ((x)->Ext) + +/* compat code for libupnp-1.8 */ +typedef struct Upnp_Event_Subscribe UpnpEventSubscribe; +#define UpnpEventSubscribe_get_SID_cstr(x) ((x)->Sid) +#define UpnpEventSubscribe_get_ErrCode(x) ((x)->ErrCode) +#define UpnpEventSubscribe_get_PublisherUrl_cstr(x) ((x)->PublisherUrl) +#define UpnpEventSubscribe_get_TimeOut(x) ((x)->TimeOut) + +/* compat code for libupnp-1.8 */ +typedef struct Upnp_Subscription_Request UpnpSubscriptionRequest; +#define UpnpSubscriptionRequest_get_ServiceId_cstr(x) ((x)->ServiceId) +#define UpnpSubscriptionRequest_get_UDN_cstr(x) ((x)->UDN) +#define UpnpSubscriptionRequest_get_SID_cstr(x) ((x)->Sid) + +/* compat code for libupnp-1.8 */ +typedef struct File_Info UpnpFileInfo; +#define UpnpFileInfo_get_FileLength(x) ((x)->file_length) +#define UpnpFileInfo_set_FileLength(x, v) ((x)->file_length = (v)) +#define UpnpFileInfo_get_LastModified(x) ((x)->last_modified) +#define UpnpFileInfo_set_LastModified(x, v) ((x)->last_modified = (v)) +#define UpnpFileInfo_get_IsDirectory(x) ((x)->is_directory) +#define UpnpFileInfo_set_IsDirectory(x, v) ((x)->is_directory = (v)) +#define UpnpFileInfo_get_IsReadable(x) ((x)->is_readable) +#define UpnpFileInfo_set_IsReadable(x, v) ((x)->is_readable = (v)) +#define UpnpFileInfo_get_ContentType(x) ((x)->content_type) +#define UpnpFileInfo_set_ContentType(x, v) ((x)->content_type = (v)) + +#endif + +#endif /* _UPNP_COMPAT_H */ diff --git a/src/upnp_device.c b/src/upnp_device.c index 5ded858..9a8e67a 100644 --- a/src/upnp_device.c +++ b/src/upnp_device.c @@ -71,17 +71,21 @@ int upnp_add_response(struct action_event *event, return -1; } - int rc; - rc = UpnpAddToActionResponse(&event->request->ActionResult, - event->request->ActionName, + IXML_Document* actionResult = UpnpActionRequest_get_ActionResult(event->request); + const char* actionName = UpnpActionRequest_get_ActionName_cstr(event->request); + int rc = UpnpAddToActionResponse(&actionResult, actionName, event->service->service_type, key, value); if (rc != UPNP_E_SUCCESS) { /* report custom error */ - event->request->ActionResult = NULL; - event->request->ErrCode = UPNP_SOAP_E_ACTION_FAILED; - strcpy(event->request->ErrStr, UpnpGetErrorMessage(rc)); + UpnpString *errorMessage = UpnpString_new(); + UpnpString_set_String(errorMessage, UpnpGetErrorMessage(rc)); + UpnpActionRequest_set_ActionResult(event->request, NULL); + UpnpActionRequest_set_ErrCode(event->request, UPNP_SOAP_E_ACTION_FAILED); + UpnpActionRequest_set_ErrStr(event->request, errorMessage); return -1; } + + UpnpActionRequest_set_ActionResult(event->request, actionResult); return 0; } @@ -110,20 +114,24 @@ void upnp_set_error(struct action_event *event, int error_code, va_list ap; va_start(ap, format); - event->request->ActionResult = NULL; - event->request->ErrCode = UPNP_SOAP_E_ACTION_FAILED; - vsnprintf(event->request->ErrStr, sizeof(event->request->ErrStr), - format, ap); - + char buffer[LINE_SIZE]; + vsnprintf(buffer, sizeof(buffer), format, ap); va_end(ap); - Log_error("upnp", "%s: %s\n", __FUNCTION__, event->request->ErrStr); + + UpnpActionRequest_set_ActionResult(event->request, NULL); + UpnpActionRequest_set_ErrCode(event->request, UPNP_SOAP_E_ACTION_FAILED); + UpnpString *errStr = UpnpString_new(); + UpnpString_set_String(errStr, buffer); + UpnpActionRequest_set_ErrStr(event->request, errStr); + Log_error("upnp", "%s: %s\n", __FUNCTION__, + UpnpActionRequest_get_ErrStr_cstr(event->request)); } const char *upnp_get_string(struct action_event *event, const char *key) { IXML_Node *node; - node = (IXML_Node *) event->request->ActionRequest; + node = (IXML_Node *)UpnpActionRequest_get_ActionRequest(event->request); if (node == NULL) { upnp_set_error(event, UPNP_SOAP_E_INVALID_ARGS, "Invalid action request document"); @@ -153,21 +161,20 @@ const char *upnp_get_string(struct action_event *event, const char *key) } static int handle_subscription_request(struct upnp_device *priv, - struct Upnp_Subscription_Request - *sr_event) + UpnpSubscriptionRequest *sr_event) { struct service *srv; int rc; assert(priv != NULL); - Log_info("upnp", "Subscription request for %s (%s)", - sr_event->ServiceId, sr_event->UDN); - - srv = find_service(priv->upnp_device_descriptor, sr_event->ServiceId); + const char *serviceId = UpnpSubscriptionRequest_get_ServiceId_cstr(sr_event); + const char *udn = UpnpSubscriptionRequest_get_UDN_cstr(sr_event); + Log_info("upnp", "Subscription request for %s (%s)", serviceId, udn); + srv = find_service(priv->upnp_device_descriptor, serviceId); if (srv == NULL) { Log_error("upnp", "%s: Unknown service '%s'", __FUNCTION__, - sr_event->ServiceId); + serviceId); return -1; } @@ -208,10 +215,10 @@ static int handle_subscription_request(struct upnp_device *priv, free(xml_value); UPnPLastChangeBuilder_delete(builder); + const char *sid = UpnpSubscriptionRequest_get_SID_cstr(sr_event); rc = UpnpAcceptSubscription(priv->device_handle, - sr_event->UDN, sr_event->ServiceId, - eventvar_names, eventvar_values, 1, - sr_event->Sid); + udn, serviceId, + eventvar_names, eventvar_values, 1, sid); if (rc == UPNP_E_SUCCESS) { result = 0; } else { @@ -240,11 +247,13 @@ int upnp_device_notify(struct upnp_device *device, static int handle_var_request(struct upnp_device *priv, - struct Upnp_State_Var_Request *var_event) { - struct service *srv = find_service(priv->upnp_device_descriptor, - var_event->ServiceID); + UpnpStateVarRequest *event) +{ + const char *serviceID = UpnpStateVarRequest_get_ServiceID_cstr(event); + + struct service *srv = find_service(priv->upnp_device_descriptor, serviceID); if (srv == NULL) { - var_event->ErrCode = UPNP_SOAP_E_INVALID_ARGS; + UpnpStateVarRequest_set_ErrCode(event, UPNP_SOAP_E_INVALID_ARGS); return -1; } @@ -254,10 +263,10 @@ static int handle_var_request(struct upnp_device *priv, const int var_count = VariableContainer_get_num_vars(srv->variable_container); for (int i = 0; i < var_count; ++i) { - const char *name; - const char *value = - VariableContainer_get(srv->variable_container, i, &name); - if (value && strcmp(var_event->StateVarName, name) == 0) { + const char *name = NULL; + const char *value = VariableContainer_get(srv->variable_container, i, &name); + const char *stateVarName = UpnpStateVarRequest_get_StateVarName_cstr(event); + if (value && strcmp(stateVarName, name) == 0) { result = strdup(value); break; } @@ -265,30 +274,27 @@ static int handle_var_request(struct upnp_device *priv, ithread_mutex_unlock(srv->service_mutex); - var_event->CurrentVal = result; - var_event->ErrCode = (result == NULL) - ? UPNP_SOAP_E_INVALID_VAR - : UPNP_E_SUCCESS; + UpnpStateVarRequest_set_CurrentVal(event, result); + int errCode = (result == NULL) ? UPNP_SOAP_E_INVALID_VAR : UPNP_E_SUCCESS; + UpnpStateVarRequest_set_ErrCode(event, errCode); Log_info("upnp", "Variable request %s -> %s (%s)", - var_event->StateVarName, result, var_event->ServiceID); + UpnpStateVarRequest_get_StateVarName_cstr(event), result, serviceID); return 0; } static int handle_action_request(struct upnp_device *priv, - struct Upnp_Action_Request *ar_event) + UpnpActionRequest *ar_event) { - struct service *event_service; - struct action *event_action; - - event_service = find_service(priv->upnp_device_descriptor, - ar_event->ServiceID); - event_action = find_action(event_service, ar_event->ActionName); + const char *serviceID = UpnpActionRequest_get_ServiceID_cstr(ar_event); + const char *actionName = UpnpActionRequest_get_ActionName_cstr(ar_event); + struct service *event_service = find_service(priv->upnp_device_descriptor, serviceID); + struct action *event_action = find_action(event_service, actionName); if (event_action == NULL) { Log_error("upnp", "Unknown action '%s' for service '%s'", - ar_event->ActionName, ar_event->ServiceID); - ar_event->ActionResult = NULL; - ar_event->ErrCode = 401; + actionName, serviceID); + UpnpActionRequest_set_ActionResult(ar_event, NULL); + UpnpActionRequest_set_ErrCode(ar_event, 401); return -1; } @@ -315,12 +321,12 @@ static int handle_action_request(struct upnp_device *priv, #ifdef ENABLE_ACTION_LOGGING { char *action_request_xml = NULL; - if (ar_event->ActionRequest) { + if (UpnpActionRequest_get_ActionRequest(ar_event)) { action_request_xml = ixmlDocumenttoString( - ar_event->ActionRequest); + UpnpActionRequest_get_ActionRequest(ar_event)); } Log_info("upnp", "Action '%s'; Request: %s", - ar_event->ActionName, action_request_xml); + UpnpActionRequest_get_ActionName_cstr(ar_event), action_request_xml); free(action_request_xml); } #endif @@ -335,29 +341,34 @@ static int handle_action_request(struct upnp_device *priv, rc = (event_action->callback) (&event); if (rc == 0) { - ar_event->ErrCode = UPNP_E_SUCCESS; + UpnpActionRequest_set_ErrCode(event.request, UPNP_E_SUCCESS); #ifdef ENABLE_ACTION_LOGGING - if (ar_event->ActionResult) { - char *action_result_xml = NULL; - action_result_xml = ixmlDocumenttoString( - ar_event->ActionResult); + if (UpnpActionRequest_get_ActionResult(ar_event)) { + char *action_result_xml = ixmlDocumenttoString( + UpnpActionRequest_get_ActionResult(ar_event)); Log_info("upnp", "Action '%s' OK; Response %s", - ar_event->ActionName, + UpnpActionRequest_get_ActionName_cstr(ar_event), action_result_xml); free(action_result_xml); } else { Log_info("upnp", "Action '%s' OK", - ar_event->ActionName); + UpnpActionRequest_get_ActionName_cstr(ar_event)); } #endif } - if (ar_event->ActionResult == NULL) { - ar_event->ActionResult = - UpnpMakeActionResponse(ar_event->ActionName, - event_service->service_type, - 0, NULL); + IXML_Document *actionResult = UpnpActionRequest_get_ActionResult(ar_event); + if (actionResult == NULL) { + actionResult = UpnpMakeActionResponse(actionName, + event_service->service_type, 0, NULL); + UpnpActionRequest_set_ActionResult(event.request, actionResult); } } else { + int errCode = UpnpActionRequest_get_ErrCode(ar_event); + int sock = UpnpActionRequest_get_Socket(ar_event); + const char *errStr = UpnpActionRequest_get_ErrStr_cstr(ar_event); + const char *actionName = UpnpActionRequest_get_ActionName_cstr(ar_event); + const char *devUDN = UpnpActionRequest_get_DevUDN_cstr(ar_event); + const char *serviceID = UpnpActionRequest_get_ServiceID_cstr(ar_event); Log_error("upnp", "Got a valid action, but no handler defined (!)\n" " ErrCode: %d\n" @@ -366,10 +377,8 @@ static int handle_action_request(struct upnp_device *priv, " ActionName: '%s'\n" " DevUDN: '%s'\n" " ServiceID: '%s'\n", - ar_event->ErrCode, ar_event->Socket, ar_event->ErrStr, - ar_event->ActionName, ar_event->DevUDN, - ar_event->ServiceID); - ar_event->ErrCode = UPNP_E_SUCCESS; + errCode, sock, errStr, actionName, devUDN, serviceID); + UpnpActionRequest_set_ErrCode(ar_event, UPNP_E_SUCCESS); } if (event_service->last_change) { // See comment above. @@ -380,20 +389,20 @@ static int handle_action_request(struct upnp_device *priv, return 0; } -static int event_handler(Upnp_EventType EventType, void *event, void *userdata) +static UPNP_CALLBACK(event_handler, EventType, event, userdata) { struct upnp_device *priv = (struct upnp_device *) userdata; switch (EventType) { case UPNP_CONTROL_ACTION_REQUEST: - handle_action_request(priv, event); + handle_action_request(priv, (void*) event); break; case UPNP_CONTROL_GET_VAR_REQUEST: - handle_var_request(priv, event); + handle_var_request(priv, (void*) event); break; case UPNP_EVENT_SUBSCRIPTION_REQUEST: - handle_subscription_request(priv, event); + handle_subscription_request(priv, (void*) event); break; default: diff --git a/src/webserver.c b/src/webserver.c index eaf0331..ea19c82 100644 --- a/src/webserver.c +++ b/src/webserver.c @@ -41,6 +41,7 @@ #include "logging.h" #include "webserver.h" +#include "upnp_compat.h" typedef struct { off_t pos; @@ -142,20 +143,21 @@ int webserver_register_file(const char *path, const char *content_type) return 0; } -static int webserver_get_info(const char *filename, struct File_Info *info) +static VD_GET_INFO_CALLBACK(webserver_get_info, filename, info, cookie) { struct virtual_file *virtfile = virtual_files; while (virtfile != NULL) { if (strcmp(filename, virtfile->virtual_fname) == 0) { - info->file_length = virtfile->len; - info->last_modified = 0; - info->is_directory = 0; - info->is_readable = 1; - info->content_type = - ixmlCloneDOMString(virtfile->content_type); + UpnpFileInfo_set_FileLength(info, virtfile->len); + UpnpFileInfo_set_LastModified(info, 0); + UpnpFileInfo_set_IsDirectory(info, 0); + UpnpFileInfo_set_IsReadable(info, 1); + const char *contentType = + ixmlCloneDOMString(virtfile->content_type); + UpnpFileInfo_set_ContentType(info, (char*) contentType); Log_info("webserver", "Access %s (%s) len=%zd", - filename, info->content_type, virtfile->len); + filename, contentType, virtfile->len); return 0; } virtfile = virtfile->next; @@ -167,8 +169,7 @@ static int webserver_get_info(const char *filename, struct File_Info *info) return -1; } -static UpnpWebFileHandle -webserver_open(const char *filename, enum UpnpOpenFileMode mode) +static VD_OPEN_CALLBACK(webserver_open, filename, mode, cookie) { if (mode != UPNP_READ) { Log_error("webserver", @@ -195,7 +196,7 @@ static inline int minimum(int a, int b) return (a