void gena_process_unsubscribe_request( SOCKINFO *info, http_message_t *request) { Upnp_SID sid; service_info *service; struct Handle_Info *handle_info; UpnpDevice_Handle device_handle; memptr temp_hdr; membuffer event_url_path; /* if a CALLBACK or NT header is present, then it is an error */ if( httpmsg_find_hdr( request, HDR_CALLBACK, NULL ) != NULL || httpmsg_find_hdr( request, HDR_NT, NULL ) != NULL ) { error_respond( info, HTTP_BAD_REQUEST, request ); return; } /* get SID */ if( httpmsg_find_hdr( request, HDR_SID, &temp_hdr ) == NULL || temp_hdr.length > SID_SIZE ) { error_respond( info, HTTP_PRECONDITION_FAILED, request ); return; } memcpy( sid, temp_hdr.buf, temp_hdr.length ); sid[temp_hdr.length] = '\0'; /* lookup service by eventURL */ membuffer_init( &event_url_path ); if( membuffer_append( &event_url_path, request->uri.pathquery.buff, request->uri.pathquery.size ) != 0 ) { error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request ); return; } HandleLock(); if( GetDeviceHandleInfoForPath(event_url_path.buf, info->foreign_sockaddr.ss_family, &device_handle, &handle_info, &service) != HND_DEVICE ) { error_respond( info, HTTP_PRECONDITION_FAILED, request ); membuffer_destroy( &event_url_path ); HandleUnlock(); return; } membuffer_destroy( &event_url_path ); /* validate service */ if( service == NULL || !service->active || GetSubscriptionSID( sid, service ) == NULL ) { error_respond( info, HTTP_PRECONDITION_FAILED, request ); HandleUnlock(); return; } RemoveSubscriptionSID(sid, service); error_respond(info, HTTP_OK, request); /* success */ HandleUnlock(); }
/*! * \brief Returns OK message in the case of a subscription request. * * \return UPNP_E_SUCCESS if successful, otherwise the appropriate error code. */ static int respond_ok( /*! [in] Socket connection of request. */ SOCKINFO *info, /*! [in] Accepted duration. */ int time_out, /*! [in] Accepted subscription. */ subscription *sub, /*! [in] Http request. */ http_message_t *request) { int major; int minor; membuffer response; int return_code; char timeout_str[100]; int upnp_timeout = UPNP_TIMEOUT; int rc = 0; http_CalcResponseVersion( request->major_version, request->minor_version, &major, &minor ); if( time_out >= 0 ) { rc = snprintf( timeout_str, sizeof ( timeout_str ), "TIMEOUT: Second-%d", time_out ); } else { memset( timeout_str, 0, sizeof( timeout_str ) ); strncpy( timeout_str, "TIMEOUT: Second-infinite", sizeof ( timeout_str ) - 1); } if (rc < 0 || (unsigned int) rc >= sizeof ( timeout_str ) ) { error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request ); return UPNP_E_OUTOF_MEMORY; } membuffer_init( &response ); response.size_inc = 30; if( http_MakeMessage( &response, major, minor, "R" "D" "S" "N" "Xc" "ssc" "scc", HTTP_OK, (off_t)0, X_USER_AGENT, "SID: ", sub->sid, timeout_str ) != 0 ) { membuffer_destroy( &response ); error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request ); return UPNP_E_OUTOF_MEMORY; } return_code = http_SendMessage( info, &upnp_timeout, "b", response.buf, response.length ); membuffer_destroy( &response ); 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; }
/**************************************************************************** * Function : send_var_query_response * * Parameters : * IN SOCKINFO *info : socket info * IN const char* var_value : value of the state variable * IN http_message_t* hmsg : HTTP request * * Description : This function sends response of get var status * * Return : void * * Note : ****************************************************************************/ static UPNP_INLINE void send_var_query_response( IN SOCKINFO * info, IN const char *var_value, IN http_message_t * hmsg ) { off_t content_length; int timeout_secs = SOAP_TIMEOUT; int major; int minor; const char *start_body = "<s:Envelope " "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" " "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n" "<s:Body>\n" "<u:QueryStateVariableResponse " "xmlns:u=\"urn:schemas-upnp-org:control-1-0\">\n" "<return>"; const char *end_body = "</return>\n" "</u:QueryStateVariableResponse>\n" "</s:Body>\n" "</s:Envelope>\n"; membuffer response; http_CalcResponseVersion( hmsg->major_version, hmsg->minor_version, &major, &minor ); content_length = strlen( start_body ) + strlen( var_value ) + strlen( end_body ); // make headers membuffer_init( &response ); if (http_MakeMessage( &response, major, minor, "RNsDsSXcc" "sss", HTTP_OK, content_length, ContentTypeHeader, "EXT:\r\n", X_USER_AGENT, start_body, var_value, end_body ) != 0 ) { membuffer_destroy( &response ); return; // out of mem } // send msg http_SendMessage( info, &timeout_secs, "b", response.buf, response.length ); membuffer_destroy( &response ); }
/* Cleans up the Curl handle when the Session object is garbage collected. */ void session_free(struct curl_state *curl) { if (curl->handle) { curl_easy_cleanup(curl->handle); curl->handle = NULL; } session_close_debug_file(curl); membuffer_destroy( &curl->header_buffer ); membuffer_destroy( &curl->body_buffer ); cs_list_remove(curl); free(curl); }
/************************************************************************ * Function : membuffer_assign * * Parameters : * INOUT membuffer* m ; buffer whose memory is to be allocated and * assigned. * IN const void* buf ; source buffer whose contents will be copied * IN size_t buf_len ; length of the source buffer * * Description : Allocate memory to membuffer* m and copy the contents * of the in parameter IN const void* buf. * * Return : int ; * UPNP_E_SUCCESS * UPNP_E_OUTOF_MEMORY * * Note : ************************************************************************/ int membuffer_assign( INOUT membuffer * m, IN const void *buf, IN size_t buf_len ) { int return_code; assert( m != NULL ); // set value to null if( buf == NULL ) { membuffer_destroy( m ); return 0; } // alloc mem return_code = membuffer_set_size( m, buf_len ); if( return_code != 0 ) { return return_code; } // copy memcpy( m->buf, buf, buf_len ); m->buf[buf_len] = 0; // null-terminate m->length = buf_len; return 0; }
/*! * \brief Function to Notify a particular subscription of a particular event. * * In general the service should NOT be blocked around this call (this may * cause deadlock with a client). * * NOTIFY http request is sent and the reply is processed. * * \return GENA_SUCCESS if the event was delivered, otherwise returns the * appropriate error code. */ static int genaNotify( /*! [in] Null terminated, includes all headers (including \\r\\n) except SID and SEQ. */ char *headers, /*! [in] The evented XML. */ char *propertySet, /*! [in] subscription to be Notified, assumes this is valid for life of function. */ subscription *sub) { size_t i; membuffer mid_msg; uri_type *url; http_parser_t response; int return_code = -1; membuffer_init(&mid_msg); if (http_MakeMessage(&mid_msg, 1, 1, "s" "ssc" "sdcc", headers, "SID: ", sub->sid, "SEQ: ", sub->ToSendEventKey) != 0) { membuffer_destroy(&mid_msg); return UPNP_E_OUTOF_MEMORY; } /* send a notify to each url until one goes thru */ for (i = 0; i < sub->DeliveryURLs.size; i++) { url = &sub->DeliveryURLs.parsedURLs[i]; return_code = notify_send_and_recv( url, &mid_msg, propertySet, &response); if (return_code == UPNP_E_SUCCESS) break; } membuffer_destroy(&mid_msg); if (return_code == UPNP_E_SUCCESS) { if (response.msg.status_code == HTTP_OK) return_code = GENA_SUCCESS; else { if (response.msg.status_code == HTTP_PRECONDITION_FAILED) /*Invalid SID gets removed */ return_code = GENA_E_NOTIFY_UNACCEPTED_REMOVE_SUB; else return_code = GENA_E_NOTIFY_UNACCEPTED; } httpmsg_destroy(&response.msg); } return return_code; }
/************************************************************************ * Function : gena_unsubscribe * * Parameters: * IN char *url: Event URL of the service * IN char *sid: The subcription ID. * OUT http_parser_t* response: The UNSUBCRIBE response from the device * * Description: * This function sends the UNSUBCRIBE gena request and recieves the * response from the device and returns it as a parameter * * Returns: int * return 0 if successful else returns appropriate error ***************************************************************************/ static int gena_unsubscribe( IN char *url, IN char *sid, OUT http_parser_t * response ) { int return_code; uri_type dest_url; membuffer request; // parse url return_code = http_FixStrUrl( url, strlen( 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" "U" "c", HTTPMETHOD_UNSUBSCRIBE, &dest_url, "SID: ", 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; }
int web_server_set_alias(const char *alias_name, const char *alias_content, size_t alias_content_length, time_t last_modified) { int ret_code; struct xml_alias_t alias; alias_release(&gAliasDoc); if (alias_name == NULL) { /* don't serve aliased doc anymore */ return 0; } assert(alias_content != NULL); membuffer_init(&alias.doc); membuffer_init(&alias.name); alias.ct = NULL; do { /* insert leading /, if missing */ if (*alias_name != '/') if (membuffer_assign_str(&alias.name, "/") != 0) break; /* error; out of mem */ ret_code = membuffer_append_str(&alias.name, alias_name); if (ret_code != 0) break; /* error */ if ((alias.ct = (int *)malloc(sizeof(int))) == NULL) break; /* error */ *alias.ct = 1; membuffer_attach(&alias.doc, (char *)alias_content, alias_content_length); alias.last_modified = last_modified; /* save in module var */ ithread_mutex_lock(&gWebMutex); gAliasDoc = alias; ithread_mutex_unlock(&gWebMutex); return 0; } while (FALSE); /* error handler */ /* free temp alias */ membuffer_destroy(&alias.name); membuffer_destroy(&alias.doc); free(alias.ct); return UPNP_E_OUTOF_MEMORY; }
void membuffer_attach(membuffer *m, char *new_buf, size_t buf_len) { assert(m != NULL); membuffer_destroy(m); m->buf = new_buf; m->length = buf_len; m->capacity = buf_len; }
/*! * \brief Release the XML document referred to by the input parameter. Free * the allocated buffers associated with this object. */ static void alias_release( /*! [in] XML alias object. */ struct xml_alias_t *alias) { ithread_mutex_lock(&gWebMutex); /* ignore invalid alias */ if (!is_valid_alias(alias)) { ithread_mutex_unlock(&gWebMutex); return; } assert(*alias->ct > 0); *alias->ct -= 1; if (*alias->ct <= 0) { membuffer_destroy(&alias->doc); membuffer_destroy(&alias->name); free(alias->ct); } ithread_mutex_unlock(&gWebMutex); }
void web_server_destroy(void) { if (bWebServerState == WEB_SERVER_ENABLED) { membuffer_destroy(&gDocumentRootDir); alias_release(&gAliasDoc); ithread_mutex_lock(&gWebMutex); memset(&gAliasDoc, 0, sizeof(struct xml_alias_t)); ithread_mutex_unlock(&gWebMutex); ithread_mutex_destroy(&gWebMutex); bWebServerState = WEB_SERVER_DISABLED; } }
int membuffer_assign(membuffer *m, const void *buf, size_t buf_len) { int return_code; assert(m != NULL); /* set value to null */ if (buf == NULL) { membuffer_destroy(m); return 0; } /* alloc mem */ return_code = membuffer_set_size(m, buf_len); if (return_code != 0) return return_code; /* copy */ if (buf_len) { memcpy(m->buf, buf, buf_len); m->buf[buf_len] = 0; /* null-terminate */ } m->length = buf_len; return 0; }
/*! * \brief Creates a HTTP request packet. Depending on the input parameter, * it either creates a service advertisement request or service shutdown * request etc. */ static void CreateServicePacket( /*! [in] type of the message (Search Reply, Advertisement * or Shutdown). */ int msg_type, /*! [in] ssdp type. */ const char *nt, /*! [in] unique service name ( go in the HTTP Header). */ char *usn, /*! [in] Location URL. */ char *location, /*! [in] Service duration in sec. */ int duration, /*! [out] Output buffer filled with HTTP statement. */ char **packet, /*! [in] Address family of the HTTP request. */ int AddressFamily, /*! [in] PowerState as defined by UPnP Low Power. */ int PowerState, /*! [in] SleepPeriod as defined by UPnP Low Power. */ int SleepPeriod, /*! [in] RegistrationState as defined by UPnP Low Power. */ int RegistrationState) { int ret_code; const char *nts; membuffer buf; /* Notf == 0 means service shutdown, * Notf == 1 means service advertisement, * Notf == 2 means reply */ membuffer_init(&buf); buf.size_inc = (size_t)30; *packet = NULL; if (msg_type == MSGTYPE_REPLY) { if (PowerState > 0) { #ifdef UPNP_HAVE_OPTSSDP ret_code = http_MakeMessage(&buf, 1, 1, "R" "sdc" "D" "sc" "ssc" "ssc" "ssc" "S" "Xc" "ssc" "ssc" "sdc" "sdc" "sdcc", HTTP_OK, "CACHE-CONTROL: max-age=", duration, "EXT:", "LOCATION: ", location, "OPT: ", "\"http://schemas.upnp.org/upnp/1/0/\"; ns=01", "01-NLS: ", gUpnpSdkNLSuuid, X_USER_AGENT, "ST: ", nt, "USN: ", usn, "Powerstate: ", PowerState, "SleepPeriod: ", SleepPeriod, "RegistrationState: ", RegistrationState); #else ret_code = http_MakeMessage(&buf, 1, 1, "R" "sdc" "D" "sc" "ssc" "S" "ssc" "ssc" "sdc" "sdc" "sdcc", HTTP_OK, "CACHE-CONTROL: max-age=", duration, "EXT:", "LOCATION: ", location, "ST: ", nt, "USN: ", usn, "Powerstate: ", PowerState, "SleepPeriod: ", SleepPeriod, "RegistrationState: ", RegistrationState); #endif /* UPNP_HAVE_OPTSSDP */ } else { #ifdef UPNP_HAVE_OPTSSDP ret_code = http_MakeMessage(&buf, 1, 1, "R" "sdc" "D" "sc" "ssc" "ssc" "ssc" "S" "Xc" "ssc" "sscc", HTTP_OK, "CACHE-CONTROL: max-age=", duration, "EXT:", "LOCATION: ", location, "OPT: ", "\"http://schemas.upnp.org/upnp/1/0/\"; ns=01", "01-NLS: ", gUpnpSdkNLSuuid, X_USER_AGENT, "ST: ", nt, "USN: ", usn); #else ret_code = http_MakeMessage(&buf, 1, 1, "R" "sdc" "D" "sc" "ssc" "S" "ssc" "sscc", HTTP_OK, "CACHE-CONTROL: max-age=", duration, "EXT:", "LOCATION: ", location, "ST: ", nt, "USN: ", usn); #endif /* UPNP_HAVE_OPTSSDP */ } if (ret_code != 0) { return; } } else if (msg_type == MSGTYPE_ADVERTISEMENT || msg_type == MSGTYPE_SHUTDOWN) { const char *host = NULL; if (msg_type == MSGTYPE_ADVERTISEMENT) nts = "ssdp:alive"; else /* shutdown */ nts = "ssdp:byebye"; /* NOTE: The CACHE-CONTROL and LOCATION headers are not present in * a shutdown msg, but are present here for MS WinMe interop. */ switch (AddressFamily) { case AF_INET: host = SSDP_IP; break; default: if (isUrlV6UlaGua(location)) host = "[" SSDP_IPV6_SITELOCAL "]"; else host = "[" SSDP_IPV6_LINKLOCAL "]"; } if (PowerState > 0) { #ifdef UPNP_HAVE_OPTSSDP ret_code = http_MakeMessage(&buf, 1, 1, "Q" "sssdc" "sdc" "ssc" "ssc" "ssc" "ssc" "ssc" "S" "Xc" "ssc" "sdc" "sdc" "sdcc", HTTPMETHOD_NOTIFY, "*", (size_t) 1, "HOST: ", host, ":", SSDP_PORT, "CACHE-CONTROL: max-age=", duration, "LOCATION: ", location, "OPT: ", "\"http://schemas.upnp.org/upnp/1/0/\"; ns=01", "01-NLS: ", gUpnpSdkNLSuuid, "NT: ", nt, "NTS: ", nts, X_USER_AGENT, "USN: ", usn, "Powerstate: ", PowerState, "SleepPeriod: ", SleepPeriod, "RegistrationState: ", RegistrationState); #else ret_code = http_MakeMessage(&buf, 1, 1, "Q" "sssdc" "sdc" "ssc" "ssc" "ssc" "S" "ssc" "sdc" "sdc" "sdcc", HTTPMETHOD_NOTIFY, "*", (size_t) 1, "HOST: ", host, ":", SSDP_PORT, "CACHE-CONTROL: max-age=", duration, "LOCATION: ", location, "NT: ", nt, "NTS: ", nts, "USN: ", usn, "Powerstate: ", PowerState, "SleepPeriod: ", SleepPeriod, "RegistrationState: ", RegistrationState); #endif /* UPNP_HAVE_OPTSSDP */ } else { #ifdef UPNP_HAVE_OPTSSDP ret_code = http_MakeMessage(&buf, 1, 1, "Q" "sssdc" "sdc" "ssc" "ssc" "ssc" "ssc" "ssc" "S" "Xc" "sscc", HTTPMETHOD_NOTIFY, "*", (size_t) 1, "HOST: ", host, ":", SSDP_PORT, "CACHE-CONTROL: max-age=", duration, "LOCATION: ", location, "OPT: ", "\"http://schemas.upnp.org/upnp/1/0/\"; ns=01", "01-NLS: ", gUpnpSdkNLSuuid, "NT: ", nt, "NTS: ", nts, X_USER_AGENT, "USN: ", usn); #else ret_code = http_MakeMessage(&buf, 1, 1, "Q" "sssdc" "sdc" "ssc" "ssc" "ssc" "S" "sscc", HTTPMETHOD_NOTIFY, "*", (size_t) 1, "HOST: ", host, ":", SSDP_PORT, "CACHE-CONTROL: max-age=", duration, "LOCATION: ", location, "NT: ", nt, "NTS: ", nts, "USN: ", usn); #endif /* UPNP_HAVE_OPTSSDP */ } if (ret_code) return; } else /* unknown msg */ assert(0); /* return msg */ *packet = membuffer_detach(&buf); membuffer_destroy(&buf); return; }
void web_server_callback(http_parser_t *parser, INOUT http_message_t *req, SOCKINFO *info) { int ret; int timeout = 0; enum resp_type rtype = 0; membuffer headers; membuffer filename; struct xml_alias_t xmldoc; struct SendInstruction RespInstr; /*Initialize instruction header. */ RespInstr.IsVirtualFile = 0; RespInstr.IsChunkActive = 0; RespInstr.IsRangeActive = 0; RespInstr.IsTrailers = 0; memset(RespInstr.AcceptLanguageHeader, 0, sizeof(RespInstr.AcceptLanguageHeader)); /* init */ membuffer_init(&headers); membuffer_init(&filename); /*Process request should create the different kind of header depending on the */ /*the type of request. */ ret = process_request(req, &rtype, &headers, &filename, &xmldoc, &RespInstr); if (ret != HTTP_OK) { /* send error code */ http_SendStatusResponse(info, ret, req->major_version, req->minor_version); } else { /* send response */ switch (rtype) { case RESP_FILEDOC: http_SendMessage(info, &timeout, "Ibf", &RespInstr, headers.buf, headers.length, filename.buf); break; case RESP_XMLDOC: http_SendMessage(info, &timeout, "Ibb", &RespInstr, headers.buf, headers.length, xmldoc.doc.buf, xmldoc.doc.length); alias_release(&xmldoc); break; case RESP_WEBDOC: /*http_SendVirtualDirDoc(info, &timeout, "Ibf", &RespInstr, headers.buf, headers.length, filename.buf);*/ http_SendMessage(info, &timeout, "Ibf", &RespInstr, headers.buf, headers.length, filename.buf); break; case RESP_HEADERS: /* headers only */ http_SendMessage(info, &timeout, "b", headers.buf, headers.length); break; case RESP_POST: /* headers only */ ret = http_RecvPostMessage(parser, info, filename.buf, &RespInstr); /* Send response. */ http_MakeMessage(&headers, 1, 1, "RTLSXcCc", ret, "text/html", &RespInstr, X_USER_AGENT); http_SendMessage(info, &timeout, "b", headers.buf, headers.length); break; default: UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__, "webserver: Invalid response type received.\n"); assert(0); } } UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__, "webserver: request processed...\n"); membuffer_destroy(&headers); membuffer_destroy(&filename); }
/**************************************************************************** * Function : SoapGetServiceVarStatus * * Parameters : * IN char * action_url : Address to send this variable * query message. * IN char *var_name : Name of the variable. * OUT char **var_value : Output value. * * Description : This function creates a status variable query message * send it to the specified URL. It also collect the response. * * Return : int * * Note : ****************************************************************************/ int SoapGetServiceVarStatus( IN char *action_url, IN char *var_name, OUT char **var_value ) { const memptr host; /* value for HOST header */ const memptr path; /* ctrl path in first line in msg */ uri_type url; membuffer request; int ret_code; http_parser_t response; int upnp_error_code; off_t content_length; const char *xml_start = "<s:Envelope " "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" " "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" "<s:Body>\r\n" "<u:QueryStateVariable xmlns:u=\"urn:schemas-upnp-org:control-1-0\">\r\n" "<u:varName>"; const char *xml_end = "</u:varName>\r\n" "</u:QueryStateVariable>\r\n" "</s:Body>\r\n" "</s:Envelope>\r\n"; *var_value = NULL; /* return NULL in case of an error */ membuffer_init( &request ); /* get host hdr and url path */ if( get_host_and_path( action_url, &host, &path, &url ) == -1 ) { return UPNP_E_INVALID_URL; } /* make headers */ request.size_inc = 50; content_length = (off_t)(strlen(xml_start) + strlen(var_name) + strlen(xml_end)); if (http_MakeMessage( &request, 1, 1, "Q" "sbc" "N" "s" "sc" "Ucc" "sss", SOAPMETHOD_POST, path.buf, path.length, "HOST: ", host.buf, host.length, content_length, ContentTypeHeader, "SOAPACTION: \"urn:schemas-upnp-org:control-1-0#QueryStateVariable\"", xml_start, var_name, xml_end ) != 0 ) { return UPNP_E_OUTOF_MEMORY; } /* send msg and get reply */ ret_code = soap_request_and_response( &request, &url, &response ); membuffer_destroy( &request ); if( ret_code != UPNP_E_SUCCESS ) { return ret_code; } /* get variable value from the response */ ret_code = get_response_value( &response.msg, SOAP_VAR_RESP, NULL, &upnp_error_code, NULL, var_value ); httpmsg_destroy( &response.msg ); if( ret_code == SOAP_VAR_RESP ) { return UPNP_E_SUCCESS; } else if( ret_code == SOAP_VAR_RESP_ERROR ) { return upnp_error_code; } else { return ret_code; } }
/**************************************************************************** * Function : send_error_response * * Parameters : * IN SOCKINFO *info : socket info * IN int error_code : error code * IN const char* err_msg : error message * IN http_message_t* hmsg : HTTP request * * Description : This function sends SOAP error response * * Return : void * * Note : ****************************************************************************/ static void send_error_response( IN SOCKINFO * info, IN int error_code, IN const char *err_msg, IN http_message_t * hmsg ) { int content_length; int timeout_secs = SOAP_TIMEOUT; int major, minor; const char *start_body = "<s:Envelope\n" "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"\n" "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n" "<s:Body>\n" "<s:Fault>\n" "<faultcode>s:Client</faultcode>\n" "<faultstring>UPnPError</faultstring>\n" "<detail>\n" "<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\">\n" "<errorCode>"; const char *mid_body = "</errorCode>\n" "<errorDescription>"; const char *end_body = "</errorDescription>\n" "</UPnPError>\n" "</detail>\n" "</s:Fault>\n" "</s:Body>\n" "</s:Envelope>\n"; char err_code_str[30]; membuffer headers; sprintf( err_code_str, "%d", error_code ); // calc body len content_length = strlen( start_body ) + strlen( err_code_str ) + strlen( mid_body ) + strlen( err_msg ) + strlen( end_body ); http_CalcResponseVersion( hmsg->major_version, hmsg->minor_version, &major, &minor ); // make headers membuffer_init( &headers ); if( http_MakeMessage( &headers, major, minor, "RNsDsSc" "sssss", 500, content_length, ContentTypeHeader, "EXT:\r\n", start_body, err_code_str, mid_body, err_msg, end_body ) != 0 ) { membuffer_destroy( &headers ); return; // out of mem } // send err msg http_SendMessage( info, &timeout_secs, "b", headers.buf, headers.length ); membuffer_destroy( &headers ); }
/*! * \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; int rc = 0; UpnpString_clear(sid); /* request timeout to string */ if (timeout == NULL) { timeout = &local_timeout; } if (*timeout < 0) { memset(timeout_str, 0, sizeof(timeout_str)); strncpy(timeout_str, "infinite", sizeof(timeout_str) - 1); } else if(*timeout < CP_MINIMUM_SUBSCRIPTION_TIME) { rc = snprintf(timeout_str, sizeof(timeout_str), "%d", CP_MINIMUM_SUBSCRIPTION_TIME); } else { rc = snprintf(timeout_str, sizeof(timeout_str), "%d", *timeout); } if (rc < 0 || (unsigned int) rc >= sizeof(timeout_str)) return UPNP_E_OUTOF_MEMORY; /* 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: <http://[", (IN6_IS_ADDR_LINKLOCAL(&DestAddr6->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: <http://", gIF_IPV4, ":", LOCAL_PORT_V4, "/>", "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; }
/************************************************************************ * Function : gena_subscribe * * Parameters: * IN char *url: url of service to subscribe * INOUT int* timeout:subscription time desired (in secs) * IN char* renewal_sid:for renewal, this contains a currently h * held subscription SID. For first time * subscription, this must be NULL * OUT char** sid: SID returned by the subscription or renew msg * * Description: * This function subscribes or renew subscription * * Returns: int * return 0 if successful else returns appropriate error ***************************************************************************/ static int gena_subscribe( IN char *url, INOUT int *timeout, IN char *renewal_sid, OUT char **sid ) { int return_code; memptr sid_hdr, timeout_hdr; char timeout_str[25]; membuffer request; uri_type dest_url; http_parser_t response; *sid = NULL; // init // request timeout to string if( ( timeout == NULL ) || ( ( *timeout > 0 ) && ( *timeout < CP_MINIMUM_SUBSCRIPTION_TIME ) ) ) { sprintf( timeout_str, "%d", CP_MINIMUM_SUBSCRIPTION_TIME ); } else if( *timeout >= 0 ) { sprintf( timeout_str, "%d", *timeout ); } else { strcpy( timeout_str, "infinite" ); } // parse url return_code = http_FixStrUrl( url, strlen( 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" "ssc" "c", HTTPMETHOD_SUBSCRIBE, &dest_url, "SID: ", renewal_sid, "TIMEOUT: Second-", timeout_str ); } else { // subscribe return_code = http_MakeMessage( &request, 1, 1, "q" "sssdsscc", HTTPMETHOD_SUBSCRIBE, &dest_url, "CALLBACK: <http://", LOCAL_HOST, ":", LOCAL_PORT, "/>\r\n" "NT: upnp:event\r\n" "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 if( matchstr( timeout_hdr.buf, timeout_hdr.length, "%iSecond-%d%0", timeout ) == PARSE_OK ) { // nothing } else if( memptr_cmp_nocase( &timeout_hdr, "Second-infinite" ) == 0 ) { *timeout = -1; } else { httpmsg_destroy( &response.msg ); return UPNP_E_BAD_RESPONSE; } // save SID *sid = str_alloc( sid_hdr.buf, sid_hdr.length ); if( *sid == NULL ) { httpmsg_destroy( &response.msg ); return UPNP_E_OUTOF_MEMORY; } httpmsg_destroy( &response.msg ); return UPNP_E_SUCCESS; }
/************************************************************************ * Function : config_description_doc * * Parameters : * INOUT IXML_Document *doc ;IMXL description document to be * configured * IN const char* ip_str ; string containing the IP port number * OUT char** root_path_str ; buffer to hold the root path * of the configured description document * INOUT IXML_Document *doc : Description document * IN const char* ip_str : ipaddress string * OUT char** root_path_str : root path string * * Description : Configure the description document. Add the standard * format and then add information from the root device and any * child nodes. * * Return : int ; * UPNP_E_SUCCESS - On Success * UPNP_E_OUTOF_MEMORY - Default Error * UPNP_E_INVALID_DESC - Invalid child node * UPNP_E_INVALID_URL - Invalid node information * * Note : ************************************************************************/ static int config_description_doc( INOUT IXML_Document * doc, IN const char *ip_str, OUT char **root_path_str ) { xboolean addNew = FALSE; IXML_NodeList *baseList; IXML_Element *element = NULL; IXML_Element *newElement = NULL; IXML_Node *textNode = NULL; IXML_Node *rootNode = NULL; IXML_Node *urlbase_node = NULL; char *urlBaseStr = "URLBase"; const DOMString domStr = NULL; uri_type uri; int err_code; int len; membuffer url_str; membuffer root_path; membuffer_init( &url_str ); membuffer_init( &root_path ); err_code = UPNP_E_OUTOF_MEMORY; // default error baseList = ixmlDocument_getElementsByTagName( doc, urlBaseStr ); if( baseList == NULL ) { // urlbase not found -- create new one addNew = TRUE; element = ixmlDocument_createElement( doc, urlBaseStr ); if( element == NULL ) { goto error_handler; } if( membuffer_append_str( &url_str, "http://" ) != 0 || membuffer_append_str( &url_str, ip_str ) != 0 || membuffer_append_str( &url_str, "/" ) != 0 || membuffer_append_str( &root_path, "/" ) != 0 ) { goto error_handler; } rootNode = ixmlNode_getFirstChild( ( IXML_Node * ) doc ); if( rootNode == NULL ) { err_code = UPNP_E_INVALID_DESC; goto error_handler; } err_code = ixmlNode_appendChild( rootNode, ( IXML_Node * ) element ); if( err_code != IXML_SUCCESS ) { goto error_handler; } textNode = ixmlDocument_createTextNode( doc, ( char * )url_str.buf ); if( textNode == NULL ) { goto error_handler; } err_code = ixmlNode_appendChild( ( IXML_Node * ) element, textNode ); if( err_code != IXML_SUCCESS ) { goto error_handler; } } else { // urlbase found urlbase_node = ixmlNodeList_item( baseList, 0 ); assert( urlbase_node != NULL ); textNode = ixmlNode_getFirstChild( urlbase_node ); if( textNode == NULL ) { err_code = UPNP_E_INVALID_DESC; goto error_handler; } domStr = ixmlNode_getNodeValue( textNode ); if( domStr == NULL ) { err_code = UPNP_E_INVALID_URL; goto error_handler; } len = parse_uri( domStr, strlen( domStr ), &uri ); if( len < 0 || uri.type != ABSOLUTE ) { err_code = UPNP_E_INVALID_URL; goto error_handler; } if( membuffer_assign( &url_str, uri.scheme.buff, uri.scheme.size ) != 0 || membuffer_append_str( &url_str, "://" ) != 0 || membuffer_append_str( &url_str, ip_str ) != 0 ) { goto error_handler; } // add leading '/' if missing from relative path if( ( uri.pathquery.size > 0 && uri.pathquery.buff[0] != '/' ) || ( uri.pathquery.size == 0 ) ) { if( membuffer_append_str( &url_str, "/" ) != 0 || membuffer_append_str( &root_path, "/" ) != 0 ) { goto error_handler; } } if( membuffer_append( &url_str, uri.pathquery.buff, uri.pathquery.size ) != 0 || membuffer_append( &root_path, uri.pathquery.buff, uri.pathquery.size ) != 0 ) { goto error_handler; } // add trailing '/' if missing if( url_str.buf[url_str.length - 1] != '/' ) { if( membuffer_append( &url_str, "/", 1 ) != 0 ) { goto error_handler; } } err_code = ixmlNode_setNodeValue( textNode, url_str.buf ); if( err_code != IXML_SUCCESS ) { goto error_handler; } } *root_path_str = membuffer_detach( &root_path ); // return path err_code = UPNP_E_SUCCESS; error_handler: if( err_code != UPNP_E_SUCCESS ) { ixmlElement_free( newElement ); } ixmlNodeList_free( baseList ); membuffer_destroy( &root_path ); membuffer_destroy( &url_str ); return err_code; }
void web_server_callback(http_parser_t *parser, INOUT http_message_t *req, SOCKINFO *info) { int ret; int timeout = 5; enum resp_type rtype = 0; membuffer headers; membuffer filename; struct xml_alias_t xmldoc; #if 0 struct SendInstruction *pRespInstr; struct SendInstruction RespInstr; /*Initialize instruction header. */ RespInstr.IsVirtualFile = 0; RespInstr.IsChunkActive = 0; RespInstr.IsRangeActive = 0; RespInstr.IsTrailers = 0; memset(RespInstr.AcceptLanguageHeader, 0, sizeof(RespInstr.AcceptLanguageHeader)); pRespInstr = &RespInstr; #else struct SendInstruction *pRespInstr = osal_zmalloc( sizeof(struct SendInstruction) ); #endif /* init */ membuffer_init(&headers); membuffer_init(&filename); /*Process request should create the different kind of header depending on the */ /*the type of request. */ ret = process_request(req, &rtype, &headers, &filename, &xmldoc, pRespInstr); if (ret != HTTP_OK) { /* send error code */ http_SendStatusResponse(info, ret, req->major_version, req->minor_version); } else { /* send response */ switch (rtype) { case RESP_FILEDOC: // The timeout need consider as transfer rate is high but consumption is slow and recevie buffer // is small in recevier. It will cause receiver pauses to receive data a while. If timeout is short, // the socket will be closed by timeout!! // HTTP file playback may get a timeout issue when media (bitrate 15Mbits) sent fast (26Mbits or above) // to Android device. timeout = 20; // The value 20 is by experiment,as video is 15Mbits, // tcp has 26Mbits in transfer to NEXUS 7 video player! http_SendMessage(info, &timeout, "Ibf", pRespInstr, headers.buf, headers.length, filename.buf); UpnpPrintf(UPNP_WARN, HTTP, __FILE__, __LINE__,"%d FILEDOC %s\r\n", __LINE__, filename.buf); break; case RESP_XMLDOC: http_SendMessage(info, &timeout, "Ibb", pRespInstr, headers.buf, headers.length, xmldoc.doc.buf, xmldoc.doc.length); alias_release(&xmldoc); UpnpPrintf(UPNP_WARN, HTTP, __FILE__, __LINE__,"%d XMLDOC %s\r\n", __LINE__, xmldoc.doc.buf); break; case RESP_WEBDOC: /*http_SendVirtualDirDoc(info, &timeout, "Ibf", pRespInstr, headers.buf, headers.length, filename.buf);*/ http_SendMessage(info, &timeout, "Ibf", pRespInstr, headers.buf, headers.length, filename.buf); UpnpPrintf(UPNP_WARN, HTTP, __FILE__, __LINE__,"%d WEBDOC %s\r\n", __LINE__, filename.buf); break; case RESP_HEADERS: /* headers only */ http_SendMessage(info, &timeout, "b", headers.buf, headers.length); UpnpPrintf(UPNP_WARN, HTTP, __FILE__, __LINE__,"%d HEADERS %s\r\n", __LINE__, headers.buf); break; case RESP_POST: /* headers only */ ret = http_RecvPostMessage(parser, info, filename.buf, pRespInstr); if (ret != HTTP_OK){ req->status_code = HTTP_BAD_REQUEST; } /* Send response. */ http_MakeMessage(&headers, 1, 1, "RTLSXcCc", ret, "text/html", pRespInstr, X_USER_AGENT, "WebServer by Alpha Imaging Technology Corp."); http_SendMessage(info, &timeout, "b", headers.buf, headers.length); UpnpPrintf(UPNP_WARN, HTTP, __FILE__, __LINE__,"%d POST %s\r\n", __LINE__, headers.buf); break; default: UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__, "webserver: Invalid response type received.\n"); assert(0); break; } } UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__, "webserver: request processed...\n"); membuffer_destroy(&headers); membuffer_destroy(&filename); osal_free(pRespInstr) }
void gena_process_subscription_renewal_request( SOCKINFO *info, http_message_t *request) { Upnp_SID sid; subscription *sub; int time_out = 1801; service_info *service; struct Handle_Info *handle_info; UpnpDevice_Handle device_handle; memptr temp_hdr; membuffer event_url_path; memptr timeout_hdr; /* if a CALLBACK or NT header is present, then it is an error */ if( httpmsg_find_hdr( request, HDR_CALLBACK, NULL ) != NULL || httpmsg_find_hdr( request, HDR_NT, NULL ) != NULL ) { error_respond( info, HTTP_BAD_REQUEST, request ); return; } /* get SID */ if( httpmsg_find_hdr( request, HDR_SID, &temp_hdr ) == NULL || temp_hdr.length > SID_SIZE ) { error_respond( info, HTTP_PRECONDITION_FAILED, request ); return; } memcpy( sid, temp_hdr.buf, temp_hdr.length ); sid[temp_hdr.length] = '\0'; /* lookup service by eventURL */ membuffer_init( &event_url_path ); if( membuffer_append( &event_url_path, request->uri.pathquery.buff, request->uri.pathquery.size ) != 0 ) { error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request ); return; } HandleLock(); if (GetDeviceHandleInfoForPath(event_url_path.buf, info->foreign_sockaddr.ss_family, &device_handle, &handle_info, &service) != HND_DEVICE ) { error_respond( info, HTTP_PRECONDITION_FAILED, request ); membuffer_destroy( &event_url_path ); HandleUnlock(); return; } membuffer_destroy( &event_url_path ); /* get subscription */ if( service == NULL || !service->active || ( ( sub = GetSubscriptionSID( sid, service ) ) == NULL ) ) { error_respond( info, HTTP_PRECONDITION_FAILED, request ); HandleUnlock(); return; } UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, "Renew request: Number of subscriptions already: %d\n " "Max Subscriptions allowed:%d\n", service->TotalSubscriptions, handle_info->MaxSubscriptions ); /* too many subscriptions */ if( handle_info->MaxSubscriptions != -1 && service->TotalSubscriptions > handle_info->MaxSubscriptions ) { error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request ); RemoveSubscriptionSID( sub->sid, service ); HandleUnlock(); return; } /* set the timeout */ if( httpmsg_find_hdr( request, HDR_TIMEOUT, &timeout_hdr ) != NULL ) { if( matchstr( timeout_hdr.buf, timeout_hdr.length, "%iSecond-%d%0", &time_out ) == PARSE_OK ) { /*nothing */ } else if( memptr_cmp_nocase( &timeout_hdr, "Second-infinite" ) == 0 ) { time_out = -1; /* inifinite timeout */ } else { time_out = DEFAULT_TIMEOUT; /* default is > 1800 seconds */ } } /* replace infinite timeout with max timeout, if possible */ if( handle_info->MaxSubscriptionTimeOut != -1 ) { if( time_out == -1 || time_out > handle_info->MaxSubscriptionTimeOut ) { time_out = handle_info->MaxSubscriptionTimeOut; } } if( time_out == -1 ) { sub->expireTime = 0; } else { sub->expireTime = time( NULL ) + time_out; } if( respond_ok( info, time_out, sub, request ) != UPNP_E_SUCCESS ) { RemoveSubscriptionSID( sub->sid, service ); } HandleUnlock(); }
/*! * \brief Sends SOAP error response. */ static void send_error_response( /*! [in] Socket info. */ IN SOCKINFO *info, /*! [in] Error code. */ IN int error_code, /*! [in] Error message. */ IN const char *err_msg, /*! [in] HTTP request. */ IN http_message_t *hmsg) { off_t content_length; int timeout_secs = SOAP_TIMEOUT; int major; int minor; const char *start_body = "<?xml version=\"1.0\"?>\n" "<s:Envelope " "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" " "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n" "<s:Body>\n" "<s:Fault>\n" "<faultcode>s:Client</faultcode>\n" "<faultstring>UPnPError</faultstring>\n" "<detail>\n" "<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\">\n" "<errorCode>"; const char *mid_body = "</errorCode>\n" "<errorDescription>"; const char *end_body = "</errorDescription>\n" "</UPnPError>\n" "</detail>\n" "</s:Fault>\n" "</s:Body>\n" "</s:Envelope>\n"; char err_code_str[30]; membuffer headers; memset(err_code_str, 0, sizeof(err_code_str)); snprintf(err_code_str, sizeof(err_code_str), "%d", error_code); /* calc body len */ content_length = (off_t) (strlen(start_body) + strlen(err_code_str) + strlen(mid_body) + strlen(err_msg) + strlen(end_body)); http_CalcResponseVersion(hmsg->major_version, hmsg->minor_version, &major, &minor); /* make headers */ membuffer_init(&headers); if (http_MakeMessage(&headers, major, minor, "RNsDsSXcc" "sssss", 500, content_length, ContentTypeHeader, "EXT:\r\n", X_USER_AGENT, start_body, err_code_str, mid_body, err_msg, end_body) != 0) { membuffer_destroy(&headers); /* out of mem */ return; } /* send err msg */ http_SendMessage(info, &timeout_secs, "b", headers.buf, headers.length); membuffer_destroy(&headers); }
/*! * \brief Sends the notify message and returns a reply. * * \return on success returns UPNP_E_SUCCESS, otherwise returns a UPNP error. * * \note called by genaNotify */ static UPNP_INLINE int notify_send_and_recv( /*! [in] subscription callback URL (URL of the control point). */ uri_type *destination_url, /*! [in] Common HTTP headers. */ membuffer *mid_msg, /*! [in] The evented XML. */ char *propertySet, /*! [out] The response from the control point. */ http_parser_t *response) { uri_type url; SOCKET conn_fd; membuffer start_msg; int ret_code; int err_code; int timeout; SOCKINFO info; const char *CRLF = "\r\n"; /* connect */ UpnpPrintf(UPNP_ALL, GENA, __FILE__, __LINE__, "gena notify to: %.*s\n", (int)destination_url->hostport.text.size, destination_url->hostport.text.buff); conn_fd = http_Connect(destination_url, &url); if (conn_fd < 0) /* return UPNP error */ return UPNP_E_SOCKET_CONNECT; ret_code = sock_init(&info, conn_fd); if (ret_code) { sock_destroy(&info, SD_BOTH); return ret_code; } /* make start line and HOST header */ membuffer_init(&start_msg); if (http_MakeMessage( &start_msg, 1, 1, "q" "s", HTTPMETHOD_NOTIFY, &url, mid_msg->buf) != 0) { membuffer_destroy(&start_msg); sock_destroy(&info, SD_BOTH); return UPNP_E_OUTOF_MEMORY; } timeout = GENA_NOTIFICATION_SENDING_TIMEOUT; /* send msg (note: end of notification will contain "\r\n" twice) */ ret_code = http_SendMessage(&info, &timeout, "bbb", start_msg.buf, start_msg.length, propertySet, strlen(propertySet), CRLF, strlen(CRLF)); if (ret_code) { membuffer_destroy(&start_msg); sock_destroy(&info, SD_BOTH); return ret_code; } timeout = GENA_NOTIFICATION_ANSWERING_TIMEOUT; ret_code = http_RecvMessage(&info, response, HTTPMETHOD_NOTIFY, &timeout, &err_code); if (ret_code) { membuffer_destroy(&start_msg); sock_destroy(&info, SD_BOTH); httpmsg_destroy(&response->msg); return ret_code; } /* should shutdown completely when closing socket */ sock_destroy(&info, SD_BOTH); membuffer_destroy(&start_msg); return UPNP_E_SUCCESS; }
/*! * \brief Sends the SOAP action response. */ static UPNP_INLINE void send_action_response( /*! [in] Socket info. */ SOCKINFO *info, /*! [in] The response document. */ IXML_Document *action_resp, /*! [in] Action request document. */ http_message_t *request) { char *xml_response = NULL; membuffer headers; int major, minor; int err_code; off_t content_length; int ret_code; int timeout_secs = SOAP_TIMEOUT; static const char *start_body = "<?xml version=\"1.0\"?>" "<s:Envelope xmlns:s=\"http://schemas.xmlsoap." "org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap." "org/soap/encoding/\"><s:Body>\n"; static const char *end_body = "</s:Body> </s:Envelope>"; /* init */ http_CalcResponseVersion(request->major_version, request->minor_version, &major, &minor); membuffer_init(&headers); err_code = UPNP_E_OUTOF_MEMORY; /* one error only */ /* get xml */ xml_response = ixmlPrintNode((IXML_Node *) action_resp); if (!xml_response) goto error_handler; content_length = (off_t)(strlen(start_body) + strlen(xml_response) + strlen(end_body)); /* make headers */ if (http_MakeMessage(&headers, major, minor, "RNsDsSXcc", HTTP_OK, /* status code */ content_length, ContentTypeHeader, "EXT:\r\n", X_USER_AGENT) != 0) { goto error_handler; } /* send whole msg */ ret_code = http_SendMessage( info, &timeout_secs, "bbbb", headers.buf, headers.length, start_body, strlen(start_body), xml_response, strlen(xml_response), end_body, strlen(end_body)); if (ret_code != 0) { UpnpPrintf(UPNP_INFO, SOAP, __FILE__, __LINE__, "Failed to send response: err code = %d\n", ret_code); } err_code = 0; error_handler: ixmlFreeDOMString(xml_response); membuffer_destroy(&headers); if (err_code != 0) { /* only one type of error to worry about - out of mem */ send_error_response(info, SOAP_ACTION_FAILED, "Out of memory", request); } }
/*! * \brief This function retrives the name of the SOAP action. * * \return 0 if successful else returns appropriate error. */ static UPNP_INLINE int get_request_type( /*! [in] HTTP request. */ http_message_t *request, /*! [out] SOAP action name. */ memptr *action_name) { memptr value; memptr ns_value, dummy_quote; http_header_t *hdr; char save_char; char *s; membuffer soap_action_name; size_t n; /* find soapaction header */ if (request->method == SOAPMETHOD_POST) { if (!httpmsg_find_hdr(request, HDR_SOAPACTION, &value)) return SREQ_HDR_NOT_FOUND; } else { /* M-POST */ /* get NS value from MAN header */ hdr = httpmsg_find_hdr(request, HDR_MAN, &value); if (hdr == NULL) return SREQ_HDR_NOT_FOUND; if (matchstr(value.buf, value.length, "%q%i ; ns = %s", &dummy_quote, &ns_value) != 0) return SREQ_BAD_HDR_FORMAT; /* create soapaction name header */ membuffer_init(&soap_action_name); if (membuffer_assign(&soap_action_name, ns_value.buf, ns_value.length) == UPNP_E_OUTOF_MEMORY || membuffer_append_str(&soap_action_name, "-SOAPACTION") == UPNP_E_OUTOF_MEMORY) { membuffer_destroy(&soap_action_name); return UPNP_E_OUTOF_MEMORY; } hdr = httpmsg_find_hdr_str(request, soap_action_name.buf); membuffer_destroy(&soap_action_name); if (!hdr) return SREQ_HDR_NOT_FOUND; value.buf = hdr->value.buf; value.length = hdr->value.length; } /* determine type */ save_char = value.buf[value.length]; value.buf[value.length] = '\0'; s = strchr(value.buf, '#'); if (s == NULL) { value.buf[value.length] = save_char; return SREQ_BAD_HDR_FORMAT; } /* move to value */ s++; n = value.length - (size_t)(s - value.buf); if (matchstr(s, n, "%s", action_name) != PARSE_OK) { value.buf[value.length] = save_char; return SREQ_BAD_HDR_FORMAT; } /* action name or variable ? */ if (memptr_cmp(action_name, "QueryStateVariable") == 0) { /* query variable */ action_name->buf = NULL; action_name->length = 0; } /* restore */ value.buf[value.length] = save_char; return 0; }
/*! * \brief Processes the request and returns the result in the output parameters. * * \return * \li \c HTTP_BAD_REQUEST * \li \c HTTP_INTERNAL_SERVER_ERROR * \li \c HTTP_REQUEST_RANGE_NOT_SATISFIABLE * \li \c HTTP_FORBIDDEN * \li \c HTTP_NOT_FOUND * \li \c HTTP_NOT_ACCEPTABLE * \li \c HTTP_OK */ static int process_request( /*! [in] HTTP Request message. */ http_message_t *req, /*! [out] Tpye of response. */ enum resp_type *rtype, /*! [out] Headers. */ membuffer *headers, /*! [out] Get filename from request document. */ membuffer *filename, /*! [out] Xml alias document from the request document. */ struct xml_alias_t *alias, /*! [out] Send Instruction object where the response is set up. */ struct SendInstruction *RespInstr) { int code; int err_code; char *request_doc; struct File_Info finfo; int using_alias; int using_virtual_dir; uri_type *url; const char *temp_str; int resp_major; int resp_minor; int alias_grabbed; size_t dummy; const char *extra_headers = NULL; print_http_headers(req); url = &req->uri; assert(req->method == HTTPMETHOD_GET || req->method == HTTPMETHOD_HEAD || req->method == HTTPMETHOD_POST || req->method == HTTPMETHOD_SIMPLEGET); /* init */ memset(&finfo, 0, sizeof(finfo)); request_doc = NULL; finfo.content_type = NULL; alias_grabbed = FALSE; err_code = HTTP_INTERNAL_SERVER_ERROR; /* default error */ using_virtual_dir = FALSE; using_alias = FALSE; http_CalcResponseVersion(req->major_version, req->minor_version, &resp_major, &resp_minor); /* */ /* remove dots */ /* */ request_doc = malloc(url->pathquery.size + 1); if (request_doc == NULL) { goto error_handler; /* out of mem */ } memcpy(request_doc, url->pathquery.buff, url->pathquery.size); request_doc[url->pathquery.size] = '\0'; dummy = url->pathquery.size; remove_escaped_chars(request_doc, &dummy); code = remove_dots(request_doc, url->pathquery.size); if (code != 0) { err_code = HTTP_FORBIDDEN; goto error_handler; } if (*request_doc != '/') { /* no slash */ err_code = HTTP_BAD_REQUEST; goto error_handler; } if (isFileInVirtualDir(request_doc)) { using_virtual_dir = TRUE; RespInstr->IsVirtualFile = 1; if (membuffer_assign_str(filename, request_doc) != 0) { goto error_handler; } } else { /* try using alias */ if (is_valid_alias(&gAliasDoc)) { alias_grab(alias); alias_grabbed = TRUE; using_alias = get_alias(request_doc, alias, &finfo); if (using_alias == TRUE) { finfo.content_type = ixmlCloneDOMString("text/xml"); if (finfo.content_type == NULL) { goto error_handler; } } } } if (using_virtual_dir) { if (req->method != HTTPMETHOD_POST) { /* get file info */ if (virtualDirCallback. get_info(filename->buf, &finfo) != 0) { err_code = HTTP_NOT_FOUND; goto error_handler; } /* try index.html if req is a dir */ if (finfo.is_directory) { if (filename->buf[filename->length - 1] == '/') { temp_str = "index.html"; } else { temp_str = "/index.html"; } if (membuffer_append_str(filename, temp_str) != 0) { goto error_handler; } /* get info */ if ((virtualDirCallback. get_info(filename->buf, &finfo) != UPNP_E_SUCCESS) || finfo.is_directory) { err_code = HTTP_NOT_FOUND; goto error_handler; } } /* not readable */ if (!finfo.is_readable) { err_code = HTTP_FORBIDDEN; goto error_handler; } /* finally, get content type */ /* if ( get_content_type(filename->buf, &content_type) != 0 ) */ /*{ */ /* goto error_handler; */ /* } */ } } else if (!using_alias) { if (gDocumentRootDir.length == 0) { goto error_handler; } /* */ /* get file name */ /* */ /* filename str */ if (membuffer_assign_str(filename, gDocumentRootDir.buf) != 0 || membuffer_append_str(filename, request_doc) != 0) { goto error_handler; /* out of mem */ } /* remove trailing slashes */ while (filename->length > 0 && filename->buf[filename->length - 1] == '/') { membuffer_delete(filename, filename->length - 1, 1); } if (req->method != HTTPMETHOD_POST) { /* get info on file */ if (get_file_info(filename->buf, &finfo) != 0) { err_code = HTTP_NOT_FOUND; goto error_handler; } /* try index.html if req is a dir */ if (finfo.is_directory) { if (filename->buf[filename->length - 1] == '/') { temp_str = "index.html"; } else { temp_str = "/index.html"; } if (membuffer_append_str(filename, temp_str) != 0) { goto error_handler; } /* get info */ if (get_file_info(filename->buf, &finfo) != 0 || finfo.is_directory) { err_code = HTTP_NOT_FOUND; goto error_handler; } } /* not readable */ if (!finfo.is_readable) { err_code = HTTP_FORBIDDEN; goto error_handler; } } /* finally, get content type */ /* if ( get_content_type(filename->buf, &content_type) != 0 ) */ /* { */ /* goto error_handler; */ /* } */ } RespInstr->ReadSendSize = finfo.file_length; /* Check other header field. */ if ((code = CheckOtherHTTPHeaders(req, RespInstr, finfo.file_length)) != HTTP_OK) { err_code = code; goto error_handler; } if (req->method == HTTPMETHOD_POST) { *rtype = RESP_POST; err_code = HTTP_OK; goto error_handler; } /*extra_headers = UpnpFileInfo_get_ExtraHeaders(finfo); */ if (!extra_headers) { extra_headers = ""; } /* Check if chunked encoding should be used. */ if (using_virtual_dir && finfo.file_length == UPNP_USING_CHUNKED) { /* Chunked encoding is only supported by HTTP 1.1 clients */ if (resp_major == 1 && resp_minor == 1) { RespInstr->IsChunkActive = 1; } else { /* The virtual callback indicates that we should use * chunked encoding however the client doesn't support * it. Return with an internal server error. */ err_code = HTTP_NOT_ACCEPTABLE; goto error_handler; } } if (RespInstr->IsRangeActive && RespInstr->IsChunkActive) { /* Content-Range: bytes 222-3333/4000 HTTP_PARTIAL_CONTENT */ /* Transfer-Encoding: chunked */ if (http_MakeMessage(headers, resp_major, resp_minor, "R" "T" "GKLD" "s" "tcS" "Xc" "sCc", HTTP_PARTIAL_CONTENT, /* status code */ finfo.content_type, /* content type */ RespInstr, /* range info */ RespInstr, /* language info */ "LAST-MODIFIED: ", &finfo.last_modified, X_USER_AGENT, extra_headers) != 0) { goto error_handler; } } else if (RespInstr->IsRangeActive && !RespInstr->IsChunkActive) { /* Content-Range: bytes 222-3333/4000 HTTP_PARTIAL_CONTENT */ if (http_MakeMessage(headers, resp_major, resp_minor, "R" "N" "T" "GLD" "s" "tcS" "Xc" "sCc", HTTP_PARTIAL_CONTENT, /* status code */ RespInstr->ReadSendSize, /* content length */ finfo.content_type, /* content type */ RespInstr, /* range info */ RespInstr, /* language info */ "LAST-MODIFIED: ", &finfo.last_modified, X_USER_AGENT, extra_headers) != 0) { goto error_handler; } } else if (!RespInstr->IsRangeActive && RespInstr->IsChunkActive) { /* Transfer-Encoding: chunked */ if (http_MakeMessage(headers, resp_major, resp_minor, "RK" "TLD" "s" "tcS" "Xc" "sCc", HTTP_OK, /* status code */ finfo.content_type, /* content type */ RespInstr, /* language info */ "LAST-MODIFIED: ", &finfo.last_modified, X_USER_AGENT, extra_headers) != 0) { goto error_handler; } } else { /* !RespInstr->IsRangeActive && !RespInstr->IsChunkActive */ if (RespInstr->ReadSendSize >= 0) { if (http_MakeMessage(headers, resp_major, resp_minor, "R" "N" "TLD" "s" "tcS" "Xc" "sCc", HTTP_OK, /* status code */ RespInstr->ReadSendSize, /* content length */ finfo.content_type, /* content type */ RespInstr, /* language info */ "LAST-MODIFIED: ", &finfo.last_modified, X_USER_AGENT, extra_headers) != 0) { goto error_handler; } } else { if (http_MakeMessage(headers, resp_major, resp_minor, "R" "TLD" "s" "tcS" "Xc" "sCc", HTTP_OK, /* status code */ finfo.content_type, /* content type */ RespInstr, /* language info */ "LAST-MODIFIED: ", &finfo.last_modified, X_USER_AGENT, extra_headers) != 0) { goto error_handler; } } } if (req->method == HTTPMETHOD_HEAD) { *rtype = RESP_HEADERS; } else if (using_alias) { /* GET xml */ *rtype = RESP_XMLDOC; } else if (using_virtual_dir) { *rtype = RESP_WEBDOC; } else { /* GET filename */ *rtype = RESP_FILEDOC; } /* simple get http 0.9 as specified in http 1.0 */ /* don't send headers */ if (req->method == HTTPMETHOD_SIMPLEGET) { membuffer_destroy(headers); } err_code = HTTP_OK; error_handler: free(request_doc); ixmlFreeDOMString(finfo.content_type); if (err_code != HTTP_OK && alias_grabbed) { alias_release(alias); } return err_code; }
/**************************************************************************** * Function : get_request_type * * Parameters : * IN http_message_t* request : HTTP request * OUT memptr* action_name : SOAP action name * * Description : This function retrives the name of the SOAP action * * Return : int * 0 if successful else returns appropriate error. * Note : ****************************************************************************/ static XINLINE int get_request_type( IN http_message_t * request, OUT memptr * action_name ) { memptr value; memptr ns_value, dummy_quote; http_header_t *hdr; char save_char; char *s; membuffer soap_action_name; // find soapaction header // if( request->method == SOAPMETHOD_POST ) { if( httpmsg_find_hdr( request, HDR_SOAPACTION, &value ) == NULL ) { return SREQ_HDR_NOT_FOUND; } } else // M-POST { // get NS value from MAN header hdr = httpmsg_find_hdr( request, HDR_MAN, &value ); if( hdr == NULL ) { return SREQ_HDR_NOT_FOUND; } if( matchstr( value.buf, value.length, "%q%i ; ns = %s", &dummy_quote, &ns_value ) != 0 ) { return SREQ_BAD_HDR_FORMAT; } // create soapaction name header membuffer_init( &soap_action_name ); if( ( membuffer_assign( &soap_action_name, ns_value.buf, ns_value.length ) == UPNP_E_OUTOF_MEMORY ) || ( membuffer_append_str( &soap_action_name, "-SOAPACTION" ) == UPNP_E_OUTOF_MEMORY ) ) { membuffer_destroy( &soap_action_name ); return UPNP_E_OUTOF_MEMORY; } hdr = httpmsg_find_hdr_str( request, soap_action_name.buf ); membuffer_destroy( &soap_action_name ); if( hdr == NULL ) { return SREQ_HDR_NOT_FOUND; } value.buf = hdr->value.buf; value.length = hdr->value.length; } // determine type // save_char = value.buf[value.length]; value.buf[value.length] = '\0'; s = strchr( value.buf, '#' ); if( s == NULL ) { value.buf[value.length] = save_char; return SREQ_BAD_HDR_FORMAT; } s++; // move to value if( matchstr( s, value.length - ( s - value.buf ), "%s", action_name ) != PARSE_OK ) { value.buf[value.length] = save_char; return SREQ_BAD_HDR_FORMAT; } // action name or variable ? if( memptr_cmp( action_name, "QueryStateVariable" ) == 0 ) { // query variable action_name->buf = NULL; action_name->length = 0; } value.buf[value.length] = save_char; // restore return 0; }
/**************************************************************************** * Function : SoapSendActionEx * * Parameters : * IN char* action_url : device contrl URL * IN char *service_type : device service type IN IXML_Document *Header: Soap header * IN IXML_Document *action_node : SOAP action node ( SOAP body) * OUT IXML_Document **response_node : SOAP response node * * Description : This function is called by UPnP API to send the SOAP * action request and waits till it gets the response from the device * pass the response to the API layer. This action is similar to the * the SoapSendAction with only difference that it allows users to * pass the SOAP header along the SOAP body ( soap action request) * * Return : int * returns UPNP_E_SUCCESS if successful else returns appropriate error * Note : ****************************************************************************/ int SoapSendActionEx( IN char *action_url, IN char *service_type, IN IXML_Document * header, IN IXML_Document * action_node, OUT IXML_Document ** response_node ) { char *xml_header_str = NULL; char *action_str = NULL; memptr name; membuffer request; membuffer responsename; int err_code; int ret_code; http_parser_t response; uri_type url; int upnp_error_code; char *upnp_error_str; int got_response = FALSE; const char *xml_start = "<s:Envelope " "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" " "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n"; const char *xml_header_start = "<s:Header>\r\n"; const char *xml_header_end = "</s:Header>\r\n"; const char *xml_body_start = "<s:Body>"; const char *xml_end = "</s:Body>\r\n" "</s:Envelope>\r\n"; size_t xml_start_len; size_t xml_header_start_len; size_t xml_header_str_len; size_t xml_header_end_len; size_t xml_body_start_len; size_t action_str_len; size_t xml_end_len; off_t content_length; *response_node = NULL; /* init */ err_code = UPNP_E_OUTOF_MEMORY; /* default error */ UpnpPrintf( UPNP_INFO, SOAP, __FILE__, __LINE__, "Inside SoapSendActionEx():" ); /* init */ membuffer_init( &request ); membuffer_init( &responsename ); /* header string */ xml_header_str = ixmlPrintNode( ( IXML_Node * ) header ); if( xml_header_str == NULL ) { goto error_handler; } /* print action */ action_str = ixmlPrintNode( ( IXML_Node * ) action_node ); if( action_str == NULL ) { goto error_handler; } /* get action name */ if( get_action_name( action_str, &name ) != 0 ) { err_code = UPNP_E_INVALID_ACTION; goto error_handler; } /* parse url */ if( http_FixStrUrl( action_url, strlen( action_url ), &url ) != 0 ) { err_code = UPNP_E_INVALID_URL; goto error_handler; } UpnpPrintf( UPNP_INFO, SOAP, __FILE__, __LINE__, "path=%.*s, hostport=%.*s\n", (int)url.pathquery.size, url.pathquery.buff, (int)url.hostport.text.size, url.hostport.text.buff ); xml_start_len = strlen( xml_start ); xml_body_start_len = strlen( xml_body_start ); xml_end_len = strlen( xml_end ); action_str_len = strlen( action_str ); xml_header_start_len = strlen( xml_header_start ); xml_header_end_len = strlen( xml_header_end ); xml_header_str_len = strlen( xml_header_str ); /* make request msg */ request.size_inc = 50; content_length = (off_t)(xml_start_len + xml_header_start_len + xml_header_str_len + xml_header_end_len + xml_body_start_len + action_str_len + xml_end_len); if (http_MakeMessage( &request, 1, 1, "q" "N" "s" "sssbsc" "Uc" "b" "b" "b" "b" "b" "b" "b", SOAPMETHOD_POST, &url, content_length, ContentTypeHeader, "SOAPACTION: \"", service_type, "#", name.buf, name.length, "\"", xml_start, xml_start_len, xml_header_start, xml_header_start_len, xml_header_str, xml_header_str_len, xml_header_end, xml_header_end_len, xml_body_start, xml_body_start_len, action_str, action_str_len, xml_end, xml_end_len ) != 0 ) { goto error_handler; } ret_code = soap_request_and_response( &request, &url, &response ); got_response = TRUE; if( ret_code != UPNP_E_SUCCESS ) { err_code = ret_code; goto error_handler; } if( membuffer_append( &responsename, name.buf, name.length ) != 0 || membuffer_append_str( &responsename, "Response" ) != 0 ) { goto error_handler; } /* get action node from the response */ ret_code = get_response_value( &response.msg, SOAP_ACTION_RESP, responsename.buf, &upnp_error_code, ( IXML_Node ** ) response_node, &upnp_error_str ); if( ret_code == SOAP_ACTION_RESP ) { err_code = UPNP_E_SUCCESS; } else if( ret_code == SOAP_ACTION_RESP_ERROR ) { err_code = upnp_error_code; } else { err_code = ret_code; } error_handler: ixmlFreeDOMString( action_str ); ixmlFreeDOMString( xml_header_str ); membuffer_destroy( &request ); membuffer_destroy( &responsename ); if( got_response ) { httpmsg_destroy( &response.msg ); } return err_code; }