/******************************************************************************* * * Copyright (c) 2000-2003 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither name of Intel Corporation nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ******************************************************************************/ #include "config.h" /*! * \file */ #if EXCLUDE_GENA == 0 #ifdef INCLUDE_CLIENT_APIS #include "EventSubscribe.h" #include "gena.h" #include "httpparser.h" #include "httpreadwrite.h" #include "parsetools.h" #include "statcodes.h" #include "sysdep.h" #include "uuid.h" #include "upnpapi.h" extern ithread_mutex_t GlobalClientSubscribeMutex; /*! * \brief This is a thread function to send the renewal just before the * subscription times out. */ static void GenaAutoRenewSubscription( /*! [in] Thread data(upnp_timeout *) needed to send the renewal. */ IN void *input) { upnp_timeout *event = (upnp_timeout *) input; UpnpEventSubscribe *sub_struct = (UpnpEventSubscribe *)event->Event; void *cookie; Upnp_FunPtr callback_fun; struct Handle_Info *handle_info; int send_callback = 0; int eventType = 0; int timeout = 0; int errCode = 0; if (AUTO_RENEW_TIME == 0) { UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, "GENA SUB EXPIRED"); UpnpEventSubscribe_set_ErrCode(sub_struct, UPNP_E_SUCCESS); send_callback = 1; eventType = UPNP_EVENT_SUBSCRIPTION_EXPIRED; } else { UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__, "GENA AUTO RENEW"); timeout = UpnpEventSubscribe_get_TimeOut(sub_struct); errCode = genaRenewSubscription( event->handle, UpnpEventSubscribe_get_SID(sub_struct), &timeout); UpnpEventSubscribe_set_ErrCode(sub_struct, errCode); UpnpEventSubscribe_set_TimeOut(sub_struct, timeout); if (errCode != UPNP_E_SUCCESS && errCode != GENA_E_BAD_SID && errCode != GENA_E_BAD_HANDLE) { send_callback = 1; eventType = UPNP_EVENT_AUTORENEWAL_FAILED; } } if (send_callback) { HandleReadLock(); if( GetHandleInfo( event->handle, &handle_info ) != HND_CLIENT ) { HandleUnlock(); free_upnp_timeout(event); goto end_function; } UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__, "HANDLE IS VALID"); /* make callback */ callback_fun = handle_info->Callback; cookie = handle_info->Cookie; HandleUnlock(); callback_fun(eventType, event->Event, cookie); } free_upnp_timeout(event); end_function: return; } /*! * \brief Schedules a job to renew the subscription just before time out. * * \return GENA_E_SUCCESS if successful, otherwise returns the appropriate * error code. */ static int ScheduleGenaAutoRenew( /*! [in] Handle that also contains the subscription list. */ IN int client_handle, /*! [in] The time out value of the subscription. */ IN int TimeOut, /*! [in] Subscription being renewed. */ IN GenlibClientSubscription *sub) { UpnpEventSubscribe *RenewEventStruct = NULL; upnp_timeout *RenewEvent = NULL; int return_code = GENA_SUCCESS; ThreadPoolJob job; if (TimeOut == UPNP_INFINITE) { return_code = GENA_SUCCESS; goto end_function; } RenewEventStruct = UpnpEventSubscribe_new(); if (RenewEventStruct == NULL) { return_code = UPNP_E_OUTOF_MEMORY; goto end_function; } RenewEvent = (upnp_timeout *) malloc(sizeof(upnp_timeout)); if (RenewEvent == NULL) { free(RenewEventStruct); return_code = UPNP_E_OUTOF_MEMORY; goto end_function; } /* schedule expire event */ UpnpEventSubscribe_set_ErrCode(RenewEventStruct, UPNP_E_SUCCESS); UpnpEventSubscribe_set_TimeOut(RenewEventStruct, TimeOut); UpnpEventSubscribe_set_SID(RenewEventStruct, GenlibClientSubscription_get_SID(sub)); UpnpEventSubscribe_set_PublisherUrl(RenewEventStruct, GenlibClientSubscription_get_EventURL(sub)); /* RenewEvent->EventType=UPNP_EVENT_SUBSCRIPTION_EXPIRE; */ RenewEvent->handle = client_handle; RenewEvent->Event = RenewEventStruct; TPJobInit(&job, (start_routine) GenaAutoRenewSubscription, RenewEvent); TPJobSetFreeFunction(&job, (free_routine)free_upnp_timeout); TPJobSetPriority(&job, MED_PRIORITY); /* Schedule the job */ return_code = TimerThreadSchedule( &gTimerThread, TimeOut - AUTO_RENEW_TIME, REL_SEC, &job, SHORT_TERM, &(RenewEvent->eventId)); if (return_code != UPNP_E_SUCCESS) { free(RenewEvent); free(RenewEventStruct); goto end_function; } GenlibClientSubscription_set_RenewEventId(sub, RenewEvent->eventId); return_code = GENA_SUCCESS; end_function: return return_code; } /*! * \brief Sends the UNSUBCRIBE gena request and recieves the response from the * device and returns it as a parameter. * * \returns 0 if successful, otherwise returns the appropriate error code. */ static int gena_unsubscribe( /*! [in] Event URL of the service. */ IN const UpnpString *url, /*! [in] The subcription ID. */ IN const UpnpString *sid, /*! [out] The UNSUBCRIBE response from the device. */ OUT http_parser_t *response ) { int return_code; uri_type dest_url; membuffer request; /* parse url */ return_code = http_FixStrUrl( UpnpString_get_String(url), UpnpString_get_Length(url), &dest_url); if (return_code != 0) { return return_code; } /* make request msg */ membuffer_init(&request); request.size_inc = 30; return_code = http_MakeMessage( &request, 1, 1, "q" "ssc" "Uc", HTTPMETHOD_UNSUBSCRIBE, &dest_url, "SID: ", UpnpString_get_String(sid)); /* Not able to make the message so destroy the existing buffer */ if (return_code != 0) { membuffer_destroy(&request); return return_code; } /* send request and get reply */ return_code = http_RequestAndResponse( &dest_url, request.buf, request.length, HTTPMETHOD_UNSUBSCRIBE, HTTP_DEFAULT_TIMEOUT, response); membuffer_destroy(&request); if (return_code != 0) { httpmsg_destroy(&response->msg); } if (return_code == 0 && response->msg.status_code != HTTP_OK) { return_code = UPNP_E_UNSUBSCRIBE_UNACCEPTED; httpmsg_destroy(&response->msg); } return return_code; } /*! * \brief Subscribes or renew subscription. * * \return 0 if successful, otherwise returns the appropriate error code. */ static int gena_subscribe( /*! [in] URL of service to subscribe. */ IN const UpnpString *url, /*! [in,out] Subscription time desired (in secs). */ INOUT int *timeout, /*! [in] for renewal, this contains a currently held subscription SID. * For first time subscription, this must be NULL. */ IN const UpnpString *renewal_sid, /*! [out] SID returned by the subscription or renew msg. */ OUT UpnpString *sid) { int return_code; int parse_ret = 0; int local_timeout = CP_MINIMUM_SUBSCRIPTION_TIME; memptr sid_hdr; memptr timeout_hdr; char timeout_str[25]; membuffer request; uri_type dest_url; http_parser_t response; UpnpString_clear(sid); /* request timeout to string */ if (timeout == NULL) { timeout = &local_timeout; } if (*timeout < 0) { strcpy(timeout_str, "infinite"); } else if(*timeout < CP_MINIMUM_SUBSCRIPTION_TIME) { sprintf(timeout_str, "%d", CP_MINIMUM_SUBSCRIPTION_TIME); } else { sprintf(timeout_str, "%d", *timeout); } /* parse url */ return_code = http_FixStrUrl( UpnpString_get_String(url), UpnpString_get_Length(url), &dest_url); if (return_code != 0) { return return_code; } /* make request msg */ membuffer_init(&request); request.size_inc = 30; if (renewal_sid) { /* renew subscription */ return_code = http_MakeMessage( &request, 1, 1, "q" "ssc" "sscc", HTTPMETHOD_SUBSCRIBE, &dest_url, "SID: ", UpnpString_get_String(renewal_sid), "TIMEOUT: Second-", timeout_str ); } else { /* subscribe */ if (dest_url.hostport.IPaddress.ss_family == AF_INET6) { struct sockaddr_in6* DestAddr6 = (struct sockaddr_in6*)&dest_url.hostport.IPaddress; return_code = http_MakeMessage( &request, 1, 1, "q" "sssdsc" "sc" "sscc", HTTPMETHOD_SUBSCRIBE, &dest_url, "CALLBACK: sin6_addr) || strlen(gIF_IPV6_ULA_GUA) == 0) ? gIF_IPV6 : gIF_IPV6_ULA_GUA, "]:", LOCAL_PORT_V6, "/>", "NT: upnp:event", "TIMEOUT: Second-", timeout_str); } else { return_code = http_MakeMessage( &request, 1, 1, "q" "sssdsc" "sc" "sscc", HTTPMETHOD_SUBSCRIBE, &dest_url, "CALLBACK: ", "NT: upnp:event", "TIMEOUT: Second-", timeout_str); } } if (return_code != 0) { return return_code; } /* send request and get reply */ return_code = http_RequestAndResponse(&dest_url, request.buf, request.length, HTTPMETHOD_SUBSCRIBE, HTTP_DEFAULT_TIMEOUT, &response); membuffer_destroy(&request); if (return_code != 0) { httpmsg_destroy(&response.msg); return return_code; } if (response.msg.status_code != HTTP_OK) { httpmsg_destroy(&response.msg); return UPNP_E_SUBSCRIBE_UNACCEPTED; } /* get SID and TIMEOUT */ if (httpmsg_find_hdr(&response.msg, HDR_SID, &sid_hdr) == NULL || sid_hdr.length == 0 || httpmsg_find_hdr( &response.msg, HDR_TIMEOUT, &timeout_hdr ) == NULL || timeout_hdr.length == 0 ) { httpmsg_destroy( &response.msg ); return UPNP_E_BAD_RESPONSE; } /* save timeout */ parse_ret = matchstr(timeout_hdr.buf, timeout_hdr.length, "%iSecond-%d%0", timeout); if (parse_ret == PARSE_OK) { /* nothing to do */ } else if (memptr_cmp_nocase(&timeout_hdr, "Second-infinite") == 0) { *timeout = -1; } else { httpmsg_destroy( &response.msg ); return UPNP_E_BAD_RESPONSE; } /* save SID */ UpnpString_set_StringN(sid, sid_hdr.buf, sid_hdr.length); if (UpnpString_get_String(sid) == NULL) { httpmsg_destroy(&response.msg); return UPNP_E_OUTOF_MEMORY; } httpmsg_destroy(&response.msg); return UPNP_E_SUCCESS; } int genaUnregisterClient(UpnpClient_Handle client_handle) { GenlibClientSubscription *sub_copy = GenlibClientSubscription_new(); int return_code = UPNP_E_SUCCESS; struct Handle_Info *handle_info = NULL; http_parser_t response; while (TRUE) { HandleLock(); if (GetHandleInfo(client_handle, &handle_info) != HND_CLIENT) { HandleUnlock(); return_code = GENA_E_BAD_HANDLE; goto exit_function; } if (handle_info->ClientSubList == NULL) { return_code = UPNP_E_SUCCESS; break; } GenlibClientSubscription_assign(sub_copy, handle_info->ClientSubList); RemoveClientSubClientSID( &handle_info->ClientSubList, GenlibClientSubscription_get_SID(sub_copy)); HandleUnlock(); return_code = gena_unsubscribe( GenlibClientSubscription_get_EventURL(sub_copy), GenlibClientSubscription_get_ActualSID(sub_copy), &response); if (return_code == 0) { httpmsg_destroy(&response.msg); } free_client_subscription(sub_copy); } freeClientSubList(handle_info->ClientSubList); HandleUnlock(); exit_function: GenlibClientSubscription_delete(sub_copy); return return_code; } #ifdef INCLUDE_CLIENT_APIS int genaUnSubscribe( UpnpClient_Handle client_handle, const UpnpString *in_sid) { GenlibClientSubscription *sub = NULL; int return_code = GENA_SUCCESS; struct Handle_Info *handle_info; GenlibClientSubscription *sub_copy = GenlibClientSubscription_new(); http_parser_t response; /* validate handle and sid */ HandleLock(); if (GetHandleInfo(client_handle, &handle_info) != HND_CLIENT) { HandleUnlock(); return_code = GENA_E_BAD_HANDLE; goto exit_function; } sub = GetClientSubClientSID(handle_info->ClientSubList, in_sid); if (sub == NULL) { HandleUnlock(); return_code = GENA_E_BAD_SID; goto exit_function; } GenlibClientSubscription_assign(sub_copy, sub); HandleUnlock(); return_code = gena_unsubscribe( GenlibClientSubscription_get_EventURL(sub_copy), GenlibClientSubscription_get_ActualSID(sub_copy), &response); if (return_code == 0) { httpmsg_destroy(&response.msg); } free_client_subscription(sub_copy); HandleLock(); if (GetHandleInfo(client_handle, &handle_info) != HND_CLIENT) { HandleUnlock(); return_code = GENA_E_BAD_HANDLE; goto exit_function; } RemoveClientSubClientSID(&handle_info->ClientSubList, in_sid); HandleUnlock(); exit_function: GenlibClientSubscription_delete(sub_copy); return return_code; } #endif /* INCLUDE_CLIENT_APIS */ #ifdef INCLUDE_CLIENT_APIS int genaSubscribe( UpnpClient_Handle client_handle, const UpnpString *PublisherURL, int *TimeOut, UpnpString *out_sid) { int return_code = GENA_SUCCESS; GenlibClientSubscription *newSubscription = GenlibClientSubscription_new(); uuid_upnp uid; Upnp_SID temp_sid; Upnp_SID temp_sid2; UpnpString *ActualSID = UpnpString_new(); UpnpString *EventURL = UpnpString_new(); struct Handle_Info *handle_info; UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__, "GENA SUBSCRIBE BEGIN"); UpnpString_clear(out_sid); HandleReadLock(); /* validate handle */ if (GetHandleInfo(client_handle, &handle_info) != HND_CLIENT) { HandleUnlock(); return GENA_E_BAD_HANDLE; } HandleUnlock(); /* subscribe */ SubscribeLock(); return_code = gena_subscribe(PublisherURL, TimeOut, NULL, ActualSID); HandleLock(); if (return_code != UPNP_E_SUCCESS) { UpnpPrintf( UPNP_CRITICAL, GENA, __FILE__, __LINE__, "SUBSCRIBE FAILED in transfer error code: %d returned\n", return_code ); goto error_handler; } if(GetHandleInfo(client_handle, &handle_info) != HND_CLIENT) { return_code = GENA_E_BAD_HANDLE; goto error_handler; } /* generate client SID */ uuid_create(&uid ); uuid_unpack(&uid, temp_sid); sprintf(temp_sid2, "uuid:%s", temp_sid); UpnpString_set_String(out_sid, temp_sid2); /* create event url */ UpnpString_assign(EventURL, PublisherURL); /* fill subscription */ if (newSubscription == NULL) { return_code = UPNP_E_OUTOF_MEMORY; goto error_handler; } GenlibClientSubscription_set_RenewEventId(newSubscription, -1); GenlibClientSubscription_set_SID(newSubscription, out_sid); GenlibClientSubscription_set_ActualSID(newSubscription, ActualSID); GenlibClientSubscription_set_EventURL(newSubscription, EventURL); GenlibClientSubscription_set_Next(newSubscription, handle_info->ClientSubList); handle_info->ClientSubList = newSubscription; /* schedule expiration event */ return_code = ScheduleGenaAutoRenew(client_handle, *TimeOut, newSubscription); error_handler: if (return_code != UPNP_E_SUCCESS) { UpnpString_delete(ActualSID); UpnpString_delete(EventURL); GenlibClientSubscription_delete(newSubscription); } HandleUnlock(); SubscribeUnlock(); return return_code; } #endif /* INCLUDE_CLIENT_APIS */ int genaRenewSubscription( UpnpClient_Handle client_handle, const UpnpString *in_sid, int *TimeOut) { int return_code = GENA_SUCCESS; GenlibClientSubscription *sub = NULL; GenlibClientSubscription *sub_copy = GenlibClientSubscription_new(); struct Handle_Info *handle_info; UpnpString *ActualSID = UpnpString_new(); ThreadPoolJob tempJob; HandleLock(); /* validate handle and sid */ if (GetHandleInfo(client_handle, &handle_info) != HND_CLIENT) { HandleUnlock(); return_code = GENA_E_BAD_HANDLE; goto exit_function; } sub = GetClientSubClientSID(handle_info->ClientSubList, in_sid); if (sub == NULL) { HandleUnlock(); return_code = GENA_E_BAD_SID; goto exit_function; } /* remove old events */ if (TimerThreadRemove( &gTimerThread, GenlibClientSubscription_get_RenewEventId(sub), &tempJob) == 0 ) { free_upnp_timeout((upnp_timeout *)tempJob.arg); } UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__, "REMOVED AUTO RENEW EVENT"); GenlibClientSubscription_set_RenewEventId(sub, -1); GenlibClientSubscription_assign(sub_copy, sub); HandleUnlock(); return_code = gena_subscribe( GenlibClientSubscription_get_EventURL(sub_copy), TimeOut, GenlibClientSubscription_get_ActualSID(sub_copy), ActualSID); HandleLock(); if (GetHandleInfo(client_handle, &handle_info) != HND_CLIENT) { HandleUnlock(); return_code = GENA_E_BAD_HANDLE; goto exit_function; } /* we just called GetHandleInfo, so we don't check for return value */ /*GetHandleInfo(client_handle, &handle_info); */ if (return_code != UPNP_E_SUCCESS) { /* network failure (remove client sub) */ RemoveClientSubClientSID(&handle_info->ClientSubList, in_sid); free_client_subscription(sub_copy); HandleUnlock(); goto exit_function; } /* get subscription */ sub = GetClientSubClientSID(handle_info->ClientSubList, in_sid); if (sub == NULL) { free_client_subscription(sub_copy); HandleUnlock(); return_code = GENA_E_BAD_SID; goto exit_function; } /* store actual sid */ GenlibClientSubscription_set_ActualSID(sub, ActualSID); /* start renew subscription timer */ return_code = ScheduleGenaAutoRenew(client_handle, *TimeOut, sub); if (return_code != GENA_SUCCESS) { RemoveClientSubClientSID( &handle_info->ClientSubList, GenlibClientSubscription_get_SID(sub)); } free_client_subscription(sub_copy); HandleUnlock(); exit_function: UpnpString_delete(ActualSID); GenlibClientSubscription_delete(sub_copy); return return_code; } void gena_process_notification_event( SOCKINFO *info, http_message_t *event) { UpnpEvent *event_struct = UpnpEvent_new(); IXML_Document *ChangedVars = NULL; int eventKey; token sid; GenlibClientSubscription *subscription = NULL; struct Handle_Info *handle_info; void *cookie; Upnp_FunPtr callback; UpnpClient_Handle client_handle; memptr sid_hdr; memptr nt_hdr, nts_hdr; memptr seq_hdr; /* get SID */ if (httpmsg_find_hdr(event, HDR_SID, &sid_hdr) == NULL) { error_respond(info, HTTP_PRECONDITION_FAILED, event); goto exit_function; } sid.buff = sid_hdr.buf; sid.size = sid_hdr.length; /* get event key */ if (httpmsg_find_hdr(event, HDR_SEQ, &seq_hdr) == NULL || matchstr(seq_hdr.buf, seq_hdr.length, "%d%0", &eventKey) != PARSE_OK) { error_respond( info, HTTP_BAD_REQUEST, event ); goto exit_function; } /* get NT and NTS headers */ if (httpmsg_find_hdr(event, HDR_NT, &nt_hdr) == NULL || httpmsg_find_hdr(event, HDR_NTS, &nts_hdr) == NULL) { error_respond( info, HTTP_BAD_REQUEST, event ); goto exit_function; } /* verify NT and NTS headers */ if (memptr_cmp(&nt_hdr, "upnp:event") != 0 || memptr_cmp(&nts_hdr, "upnp:propchange") != 0) { error_respond(info, HTTP_PRECONDITION_FAILED, event); goto exit_function; } /* parse the content (should be XML) */ if (!has_xml_content_type(event) || event->msg.length == 0 || ixmlParseBufferEx(event->entity.buf, &ChangedVars) != IXML_SUCCESS) { error_respond(info, HTTP_BAD_REQUEST, event); goto exit_function; } HandleLock(); /* get client info */ if (GetClientHandleInfo(&client_handle, &handle_info) != HND_CLIENT) { error_respond(info, HTTP_PRECONDITION_FAILED, event); HandleUnlock(); goto exit_function; } /* get subscription based on SID */ subscription = GetClientSubActualSID(handle_info->ClientSubList, &sid); if (subscription == NULL) { if (eventKey == 0) { /* wait until we've finished processing a subscription */ /* (if we are in the middle) */ /* this is to avoid mistakenly rejecting the first event if we */ /* receive it before the subscription response */ HandleUnlock(); /* try and get Subscription Lock */ /* (in case we are in the process of subscribing) */ SubscribeLock(); /* get HandleLock again */ HandleLock(); if (GetClientHandleInfo(&client_handle, &handle_info) != HND_CLIENT) { error_respond(info, HTTP_PRECONDITION_FAILED, event); SubscribeUnlock(); HandleUnlock(); goto exit_function; } subscription = GetClientSubActualSID(handle_info->ClientSubList, &sid); if (subscription == NULL) { error_respond( info, HTTP_PRECONDITION_FAILED, event ); SubscribeUnlock(); HandleUnlock(); goto exit_function; } SubscribeUnlock(); } else { error_respond( info, HTTP_PRECONDITION_FAILED, event ); HandleUnlock(); goto exit_function; } } /* success */ error_respond(info, HTTP_OK, event); /* fill event struct */ UpnpEvent_set_EventKey(event_struct, eventKey); UpnpEvent_set_ChangedVariables(event_struct, ChangedVars); UpnpEvent_set_SID(event_struct, GenlibClientSubscription_get_SID(subscription)); /* copy callback */ callback = handle_info->Callback; cookie = handle_info->Cookie; HandleUnlock(); /* make callback with event struct */ /* In future, should find a way of mainting */ /* that the handle is not unregistered in the middle of a */ /* callback */ callback(UPNP_EVENT_RECEIVED, event_struct, cookie); exit_function: ixmlDocument_free(ChangedVars); UpnpEvent_delete(event_struct); } #endif /* INCLUDE_CLIENT_APIS */ #endif /* EXCLUDE_GENA */