Пример #1
0
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();
}
Пример #2
0
/* Takes data streamed from libcurl and writes it to a Ruby string buffer. */
static size_t session_write_handler(char* stream, size_t size, size_t nmemb, membuffer* buf) {
  int rc = membuffer_append(buf, stream, size * nmemb);

  /* return 0 to signal that we could not append data to our buffer */
  if (MB_OK != rc) { return 0; }

  /* otherwise, return the number of bytes appended */
  return size * nmemb;
}
Пример #3
0
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();
}
Пример #4
0
/*!
 * \brief Receives the HTTP post message.
 *
 * \return
 * \li \c HTTP_INTERNAL_SERVER_ERROR
 * \li \c HTTP_UNAUTHORIZED
 * \li \c HTTP_BAD_REQUEST
 * \li \c HTTP_SERVICE_UNAVAILABLE
 * \li \c HTTP_OK
 */
static int http_RecvPostMessage(
	/*! HTTP Parser object. */
	http_parser_t *parser,
	/*! [in] Socket Information object. */
	SOCKINFO *info,
	/*! File where received data is copied to. */
	char *filename,
	/*! Send Instruction object which gives information whether the file
	 * is a virtual file or not. */
	struct SendInstruction *Instr)
{
	size_t Data_Buf_Size = 1024;
	char Buf[1024];
	int Timeout = 0;
	FILE *Fp;
	parse_status_t status = PARSE_OK;
	int ok_on_close = FALSE;
	size_t entity_offset = 0;
	int num_read = 0;
	int ret_code = HTTP_OK;

	if (Instr && Instr->IsVirtualFile) {
		Fp = (virtualDirCallback.open) (filename, UPNP_WRITE);
		if (Fp == NULL)
			return HTTP_INTERNAL_SERVER_ERROR;
	} else {
		Fp = fopen(filename, "wb");
		if (Fp == NULL)
			return HTTP_UNAUTHORIZED;
	}
	parser->position = POS_ENTITY;
	do {
		/* first parse what has already been gotten */
		if (parser->position != POS_COMPLETE)
			status = parser_parse_entity(parser);
		if (status == PARSE_INCOMPLETE_ENTITY) {
			/* read until close */
			ok_on_close = TRUE;
		} else if ((status != PARSE_SUCCESS)
			   && (status != PARSE_CONTINUE_1)
			   && (status != PARSE_INCOMPLETE)) {
			/* error */
			ret_code = HTTP_BAD_REQUEST;
			goto ExitFunction;
		}
		/* read more if necessary entity */
		while (entity_offset + Data_Buf_Size > parser->msg.entity.length &&
		       parser->position != POS_COMPLETE) {
			num_read = sock_read(info, Buf, sizeof(Buf), &Timeout);
			if (num_read > 0) {
				/* append data to buffer */
				if (membuffer_append(&parser->msg.msg,
					Buf, (size_t)num_read) != 0) {
					/* set failure status */
					parser->http_error_code =
					    HTTP_INTERNAL_SERVER_ERROR;
					ret_code = HTTP_INTERNAL_SERVER_ERROR;
					goto ExitFunction;
				}
				status = parser_parse_entity(parser);
				if (status == PARSE_INCOMPLETE_ENTITY) {
					/* read until close */
					ok_on_close = TRUE;
				} else if ((status != PARSE_SUCCESS)
					   && (status != PARSE_CONTINUE_1)
					   && (status != PARSE_INCOMPLETE)) {
					ret_code = HTTP_BAD_REQUEST;
					goto ExitFunction;
				}
			} else if (num_read == 0) {
				if (ok_on_close) {
					UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
						"<<< (RECVD) <<<\n%s\n-----------------\n",
						parser->msg.msg.buf);
					print_http_headers(&parser->msg);
					parser->position = POS_COMPLETE;
				} else {
					/* partial msg or response */
					parser->http_error_code = HTTP_BAD_REQUEST;
					ret_code = HTTP_BAD_REQUEST;
					goto ExitFunction;
				}
			} else {
				ret_code = HTTP_SERVICE_UNAVAILABLE;
				goto ExitFunction;
			}
		}
		if ((entity_offset + Data_Buf_Size) > parser->msg.entity.length) {
			Data_Buf_Size =
			    parser->msg.entity.length - entity_offset;
		}
		memcpy(Buf,
		       &parser->msg.msg.buf[parser->entity_start_position + entity_offset],
		       Data_Buf_Size);
		entity_offset += Data_Buf_Size;
		if (Instr && Instr->IsVirtualFile) {
			int n = virtualDirCallback.write(Fp, Buf, Data_Buf_Size);
			if (n < 0) {
				ret_code = HTTP_INTERNAL_SERVER_ERROR;
				goto ExitFunction;
			}
		} else {
			size_t n = fwrite(Buf, 1, Data_Buf_Size, Fp);
			if (n != Data_Buf_Size) {
				ret_code = HTTP_INTERNAL_SERVER_ERROR;
				goto ExitFunction;
			}
		}
	} while (parser->position != POS_COMPLETE ||
		 entity_offset != parser->msg.entity.length);
ExitFunction:
	if (Instr && Instr->IsVirtualFile) {
		virtualDirCallback.close(Fp);
	} else {
		fclose(Fp);
	}

	return ret_code;
}
Пример #5
0
/************************************************************************
*	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;
}
Пример #6
0
/*!
 * \brief Receives the HTTP post message.
 *
 * \return
 * \li \c HTTP_INTERNAL_SERVER_ERROR
 * \li \c HTTP_UNAUTHORIZED
 * \li \c HTTP_REQUEST_RANGE_NOT_SATISFIABLE
 * \li \c HTTP_OK
 */
static int http_RecvPostMessage(
	/*! HTTP Parser object. */
	http_parser_t *parser,
	/*! [in] Socket Information object. */
	SOCKINFO *info,
	/*! File where received data is copied to. */
	char *filename,
	/*! Send Instruction object which gives information whether the file
	 * is a virtual file or not. */
	struct SendInstruction *Instr)
{
	char *Buf = NULL;   // char Buf[1024];
	int Timeout = 0;
	OSAL_FILE *Fp;
	//parse_status_t status = PARSE_OK;
	int ok_on_close = FALSE;
	int num_read = 0;
	int ret_code = HTTP_OK;
	long	wrcb;

	if (Instr && Instr->IsVirtualFile) {
		struct File_Info finfo;
		/*
		 * give parser into extra_head of get_info to parsing continually
		 */
		if (parser->scanner.msg->length - parser->scanner.cursor < 512) {
			// the buffer run out, and all of header may not receive yet.
			// try to read again.
			char	*buffer;
			buffer = (char*)osal_malloc(2048);
			if (!buffer) {printc(BG_RED("%s %d:ERROR\n"), __func__, __LINE__);return 0;}
			num_read = sock_read(info, buffer, 2048, &Timeout);
			if (num_read > 0) {
				membuffer_append(&parser->msg.msg, buffer, (size_t)num_read);
			}
			osal_free(buffer);
		}
		virtualDirCallback.get_info(filename, &parser->msg, (char**)parser, &finfo, NULL); // to tell our HTTP Request Message
		Fp = (virtualDirCallback.open) (filename, UPNP_WRITE);
		if (Fp == NULL)
			return HTTP_INTERNAL_SERVER_ERROR;
	} else {
		Fp = osal_fopen(filename, "wb");
		if (Fp == NULL)
			return HTTP_UNAUTHORIZED;
		parser->position = POS_ENTITY;
	}
	wrcb = 0;
	/* read more if necessary entity */
	if (Instr && Instr->IsVirtualFile) {
		// get current total written size
		wrcb = virtualDirCallback.write(Fp, NULL, 0);
		// the wrcb is total size of data that had been written after the file opend
		// Instr->RecvWriteSize is Content-Length
		if (wrcb == Instr->RecvWriteSize) {
			// got all of data
			ret_code = HTTP_OK;
			parser->position = POS_COMPLETE;
		}
	}
	Buf = osal_malloc(1024);
	while (wrcb < Instr->RecvWriteSize) {
			num_read = sock_read(info, Buf, 1024, &Timeout);
		if (num_read > 0) {
			if (Instr && Instr->IsVirtualFile) {
				// 
				wrcb = virtualDirCallback.write(Fp, Buf, num_read);
				if (wrcb < 0) {
					ret_code = HTTP_INTERNAL_SERVER_ERROR;
					goto ExitFunction;
				}
				// the wrcb is total size of data that had been written after the file opend
				// Instr->RecvWriteSize is Content-Length
				if (wrcb == Instr->RecvWriteSize) {
					// got all of data
					ret_code = HTTP_OK;
					parser->position = POS_COMPLETE;
					}
				} else {
					size_t n = osal_fwrite(Buf, 1, num_read, Fp);
				if (n != num_read) {
					ret_code = HTTP_INTERNAL_SERVER_ERROR;
					goto ExitFunction;
				}
				}
			} else if (num_read == 0) {
				if (ok_on_close) {
					UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
						"<<< (RECVD) <<<\n%s\n-----------------\n",
						parser->msg.msg.buf);
					print_http_headers(&parser->msg);
					parser->position = POS_COMPLETE;
				} else {
					/* partial msg or response */
					parser->http_error_code = HTTP_BAD_REQUEST;
					ret_code = HTTP_BAD_REQUEST;
					goto ExitFunction;
				}
			} else {
				//ret_code = num_read;
			printc("TIME OUT total read %d\n", wrcb);
			ret_code = HTTP_PARTIAL_CONTENT;
			goto ExitFunction;
		}
	}
ExitFunction:
	if (Instr && Instr->IsVirtualFile) {
		virtualDirCallback.close(Fp);
	} else {
		osal_fclose(Fp);
	}

	if (Buf) osal_free(Buf);
	return ret_code;
}
Пример #7
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;
}