/*! * \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; }
/**************************************************************************** * 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 ); }
/**************************************************************************** * 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 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; }
/*! * \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 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); }