/************************************************************************ * Function : membuffer_append * * Parameters : * INOUT membuffer* m ; buffer whose memory is to be appended. * IN const void* buf ; source buffer whose contents will be * copied * IN size_t buf_len ; length of the source buffer * * Description : Invokes function to appends data from a constant buffer * to the buffer * * Return : int ; * * Note : ************************************************************************/ int membuffer_append( INOUT membuffer * m, IN const void *buf, IN size_t buf_len ) { assert( m != NULL ); return membuffer_insert( m, buf, buf_len, m->length ); }
/*! * \brief Adds "MAN" field in the HTTP header. * * \return 0 on success, UPNP_E_OUTOFMEMORY on error. */ static UPNP_INLINE int add_man_header( /* [in,out] HTTP header. */ membuffer *headers) { size_t n; char *soap_action_hdr; const char *man_hdr = "MAN: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns=01\r\n01-"; /* change POST to M-POST */ if (membuffer_insert(headers, "M-", 2, 0) != 0) return UPNP_E_OUTOF_MEMORY; soap_action_hdr = strstr(headers->buf, "SOAPACTION:"); /* can't fail */ assert(soap_action_hdr != NULL); /* insert MAN header */ n = (size_t)(soap_action_hdr - headers->buf); if (membuffer_insert(headers, man_hdr, strlen(man_hdr), n)) return UPNP_E_OUTOF_MEMORY; return 0; }
/**************************************************************************** * Function : add_man_header * * Parameters : * INOUT membuffer* headers : HTTP header * * Description : This function adds "MAN" field in the HTTP header * * Return : int * returns 0 on success; UPNP_E_OUTOFMEMORY on error * * Note : ****************************************************************************/ static XINLINE int add_man_header( INOUT membuffer * headers ) { char *soap_action_hdr; char *man_hdr = "MAN: \"http://schemas.xmlsoap.org/soap/envelope/\"; " "ns=01\r\n01-"; // change POST to M-POST if( membuffer_insert( headers, "M-", 2, 0 ) != 0 ) { return UPNP_E_OUTOF_MEMORY; } soap_action_hdr = strstr( headers->buf, "SOAPACTION:" ); assert( soap_action_hdr != NULL ); // can't fail // insert MAN header if( membuffer_insert( headers, man_hdr, strlen( man_hdr ), soap_action_hdr - headers->buf ) != 0 ) { return UPNP_E_OUTOF_MEMORY; } return 0; }
/************************************************************************ * Function : membuffer_append_str * * Parameters : * INOUT membuffer* m ; buffer whose memory is to be appended. * IN const char* c_str ; source buffer whose contents will be * copied * * Description : Invokes function to appends data from a constant string * to the buffer * * Return : int ; * * Note : ************************************************************************/ int membuffer_append_str( INOUT membuffer * m, IN const char *c_str ) { return membuffer_insert( m, c_str, strlen( c_str ), m->length ); }
/*! * \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_OK */ static int process_request( /*! [in] HTTP Request message. */ http_message_t *req, /*! [out] Type 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; char *extra_headers = NULL; int extra_func; //chrison 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 = osal_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 */ int ret; if ((ret = virtualDirCallback. get_info(filename->buf, req, &extra_headers, &finfo, &extra_func)) != 0) { switch(ret) { case VHAND_ERR__OUT_OF_SERVICE: err_code = HTTP_SERVICE_UNAVAILABLE; break; default: 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, req, &extra_headers, &finfo,&extra_func) != 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) { int i; if (gDocumentRootDir.length == 0) { goto error_handler; } /* */ /* get file name */ /* */ /* filename str */ UpnpPrintf(UPNP_WARN, HTTP, __FILE__, __LINE__,"%d GET filename %s\r\n", __LINE__, request_doc); // Find symbolic link path for (i = 0; _symlink[i].symbol != NULL; i++) { if (!strncmp(request_doc, _symlink[i].symbol, strlen(_symlink[i].symbol))) { // replace symbol by target membuffer_assign_str(filename, request_doc); membuffer_delete(filename, 0, strlen(_symlink[i].symbol)); if (membuffer_insert(filename, _symlink[i].target, strlen(_symlink[i].target), 0) != 0) goto error_handler; // out of mem break; } } if (!_symlink[i].symbol) { // No match, append default root path if (membuffer_assign_str(filename, gDocumentRootDir.buf) != 0 || membuffer_append_str(filename, request_doc) != 0) { goto error_handler; /* out of mem */ } } UpnpPrintf(UPNP_WARN, HTTP, __FILE__, __LINE__,"%d GET filename %s\r\n", __LINE__, filename->buf); /* 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 */ /* Transfer-Encoding: chunked */ 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) { /* Content-Range: bytes 222-3333/4000 HTTP_PARTIAL_CONTENT */ /* 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) { /* Content-Range: bytes 222-3333/4000 HTTP_PARTIAL_CONTENT */ /* Transfer-Encoding: chunked */ 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 { /* Content-Range: bytes 222-3333/4000 HTTP_PARTIAL_CONTENT */ /* Transfer-Encoding: chunked */ 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: osal_free(request_doc); ixmlFreeDOMString(finfo.content_type); if (err_code != HTTP_OK && alias_grabbed) { alias_release(alias); } return err_code; }