libupnp/upnp/src/genlib/net/http/httpreadwrite.c
2010-11-16 11:57:37 -02:00

2520 lines
74 KiB
C

/*******************************************************************************
*
* Copyright (c) 2000-2003 Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither name of Intel Corporation nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************/
/************************************************************************
* Purpose: This file defines the functionality making use of the http
* It defines functions to receive messages, process messages, send
* messages
************************************************************************/
#include "config.h"
#include "httpreadwrite.h"
#include "unixutil.h"
#include "upnp.h"
#include "upnpapi.h"
#include "membuffer.h"
#include "uri.h"
#include "statcodes.h"
#include "sock.h"
#include "UpnpInet.h"
#include "webserver.h"
#include <assert.h>
#include <stdarg.h>
#ifndef UPNP_USE_BCBPP
#ifndef UPNP_USE_MSVCPP
#include <inttypes.h>
#include <stdint.h>
#endif
#endif
#ifdef WIN32
#include <malloc.h>
#define fseeko fseek
#else
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/utsname.h>
#endif
/*
* Please, do not change these to const int while MSVC cannot understand
* const int in array dimensions.
*/
/*
const int CHUNK_HEADER_SIZE = 10;
const int CHUNK_TAIL_SIZE = 10;
*/
#define CHUNK_HEADER_SIZE 10
#define CHUNK_TAIL_SIZE 10
#ifndef UPNP_ENABLE_BLOCKING_TCP_CONNECTIONS
/* in seconds */
#define DEFAULT_TCP_CONNECT_TIMEOUT 5
/************************************************************************
* Function : Make_Socket_NoBlocking
*
* Parameters:
* IN int sock: socket
*
* Description:
* This function makes socket non-blocking.
*
* Returns: int
* 0 if successful else -1
***************************************************************************/
static int Make_Socket_NoBlocking(SOCKET sock)
{
#ifdef WIN32
u_long val = 1;
return ioctlsocket(sock, FIONBIO, &val);
#else
int val;
val = fcntl(sock, F_GETFL, 0);
if (fcntl(sock, F_SETFL, val | O_NONBLOCK) == -1) {
return -1;
}
#endif
return 0;
}
/************************************************************************
* Function : Make_Socket_Blocking
*
* Parameters:
* IN int sock: socket
*
* Description:
* This function makes socket blocking.
*
* Returns: int
* 0 if successful else -1
***************************************************************************/
static int Make_Socket_Blocking(int sock)
{
#ifdef WIN32
u_long val = 0;
return ioctlsocket(sock, FIONBIO, &val);
#else
int val;
val = fcntl(sock, F_GETFL, 0);
if (fcntl(sock, F_SETFL, val & ~O_NONBLOCK) == -1) {
return -1;
}
#endif
return 0;
}
/************************************************************************
* Function : Check_Connect_And_Wait_Connection
*
* Parameters:
* IN int sock: socket
* IN int connect_res: result of connect
*
* Description:
* This function checks socket connection and wait if it is not connected
* It should be called just after connect
*
* Returns: int
* 0 if successful else -1
***************************************************************************/
static int Check_Connect_And_Wait_Connection(int sock, int connect_res)
{
struct timeval tmvTimeout = {DEFAULT_TCP_CONNECT_TIMEOUT, 0};
int result;
#ifdef WIN32
struct fd_set fdSet;
#else
fd_set fdSet;
#endif
FD_ZERO(&fdSet);
FD_SET(sock, &fdSet);
if (connect_res < 0) {
#ifdef WIN32
if (WSAEWOULDBLOCK == WSAGetLastError() ) {
#else
if (EINPROGRESS == errno ) {
#endif
result = select(sock + 1, NULL, &fdSet, NULL, &tmvTimeout);
if (result < 0) {
#ifdef WIN32
/* WSAGetLastError(); */
#else
/* errno */
#endif
return -1;
} else if (result == 0) {
/* timeout */
return -1;
#ifndef WIN32
} else {
int valopt = 0;
socklen_t len = 0;
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (void *) &valopt, &len) < 0) {
/* failed to read delayed error */
return -1;
} else if (valopt) {
/* delayed error = valopt */
return -1;
}
#endif
}
}
}
return 0;
}
#endif /* UPNP_ENABLE_BLOCKING_TCP_CONNECTIONS */
static int private_connect(
SOCKET sockfd,
const struct sockaddr *serv_addr,
socklen_t addrlen)
{
#ifndef UPNP_ENABLE_BLOCKING_TCP_CONNECTIONS
int ret = Make_Socket_NoBlocking(sockfd);
if (ret != - 1) {
ret = connect(sockfd, serv_addr, addrlen);
ret = Check_Connect_And_Wait_Connection(sockfd, ret);
if (ret != - 1) {
ret = Make_Socket_Blocking(sockfd);
}
}
return ret;
#else
return connect(sockfd, serv_addr, addrlen);
#endif /* UPNP_ENABLE_BLOCKING_TCP_CONNECTIONS */
}
/************************************************************************
* Function: http_FixUrl
*
* Parameters:
* IN uri_type* url; URL to be validated and fixed
* OUT uri_type* fixed_url; URL after being fixed.
*
* Description:
* Validates URL
*
* Returns:
* UPNP_E_INVALID_URL
* UPNP_E_SUCCESS
************************************************************************/
int http_FixUrl(IN uri_type *url, OUT uri_type *fixed_url)
{
char *temp_path = "/";
*fixed_url = *url;
if (token_string_casecmp(&fixed_url->scheme, "http") != 0) {
return UPNP_E_INVALID_URL;
}
if( fixed_url->hostport.text.size == 0 ) {
return UPNP_E_INVALID_URL;
}
/* set pathquery to "/" if it is empty */
if (fixed_url->pathquery.size == 0) {
fixed_url->pathquery.buff = temp_path;
fixed_url->pathquery.size = 1;
}
return UPNP_E_SUCCESS;
}
/************************************************************************
* Function: http_FixStrUrl
*
* Parameters:
* IN const char* urlstr; Character string as a URL
* IN int urlstrlen; Length of the character string
* OUT uri_type* fixed_url; Fixed and corrected URL
*
* Description:
* Parses URL and then validates URL
*
* Returns:
* UPNP_E_INVALID_URL
* UPNP_E_SUCCESS
************************************************************************/
int http_FixStrUrl(
IN const char *urlstr,
IN int urlstrlen,
OUT uri_type * fixed_url)
{
uri_type url;
if (parse_uri(urlstr, urlstrlen, &url) != HTTP_SUCCESS) {
return UPNP_E_INVALID_URL;
}
return http_FixUrl(&url, fixed_url);
}
/************************************************************************
* Function: http_Connect
*
* Parameters:
* IN uri_type* destination_url; URL containing destination information
* OUT uri_type *url; Fixed and corrected URL
*
* Description:
* Gets destination address from URL and then connects to the remote end
*
* Returns:
* socket descriptor on success
* UPNP_E_OUTOF_SOCKET
* UPNP_E_SOCKET_CONNECT on error
************************************************************************/
SOCKET http_Connect(
IN uri_type *destination_url,
OUT uri_type *url)
{
SOCKET connfd;
int sockaddr_len;
int ret_connect;
http_FixUrl(destination_url, url);
connfd = socket(url->hostport.IPaddress.ss_family, SOCK_STREAM, 0);
if (connfd == -1) {
return UPNP_E_OUTOF_SOCKET;
}
sockaddr_len = url->hostport.IPaddress.ss_family == AF_INET6 ?
sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
ret_connect = private_connect(connfd,
(struct sockaddr *)&url->hostport.IPaddress, sockaddr_len);
if (ret_connect == -1) {
#ifdef WIN32
UpnpPrintf(UPNP_CRITICAL, HTTP, __FILE__, __LINE__,
"connect error: %d\n", WSAGetLastError());
#endif
shutdown(connfd, SD_BOTH);
UpnpCloseSocket(connfd);
return UPNP_E_SOCKET_CONNECT;
}
return connfd;
}
/*!
* \brief Get the data on the socket and take actions based on the read data to
* modify the parser objects buffer.
*
* If an error is reported while parsing the data, the error code is passed in
* the http_errr_code parameter.
*
* Parameters:
* IN SOCKINFO *info; Socket information object
* OUT http_parser_t* parser; HTTP parser object
* IN http_method_t request_method; HTTP request method
* IN OUT int* timeout_secs; time out
* OUT int* http_error_code; HTTP error code returned
*
* \return
* UPNP_E_SUCCESS
* UPNP_E_BAD_HTTPMSG
*/
int http_RecvMessage(
IN SOCKINFO *info,
OUT http_parser_t *parser,
IN http_method_t request_method,
IN OUT int *timeout_secs,
OUT int *http_error_code)
{
int ret = UPNP_E_SUCCESS;
int line = 0;
parse_status_t status;
int num_read;
xboolean ok_on_close = FALSE;
char buf[2 * 1024];
if (request_method == HTTPMETHOD_UNKNOWN) {
parser_request_init(parser);
} else {
parser_response_init(parser, request_method);
}
while (TRUE) {
num_read = sock_read(info, buf, sizeof buf, timeout_secs);
if (num_read > 0) {
/* got data */
status = parser_append(parser, buf, num_read);
if (status == PARSE_SUCCESS) {
UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
"<<< (RECVD) <<<\n%s\n-----------------\n",
parser->msg.msg.buf );
print_http_headers( &parser->msg );
if (g_maxContentLength > 0 && parser->content_length > (unsigned int)g_maxContentLength) {
*http_error_code = HTTP_REQ_ENTITY_TOO_LARGE;
line = __LINE__;
ret = UPNP_E_OUTOF_BOUNDS;
goto ExitFunction;
}
line = __LINE__;
ret = 0;
goto ExitFunction;
} else if (status == PARSE_FAILURE) {
*http_error_code = parser->http_error_code;
line = __LINE__;
ret = UPNP_E_BAD_HTTPMSG;
goto ExitFunction;
} else if (status == PARSE_INCOMPLETE_ENTITY) {
/* read until close */
ok_on_close = TRUE;
} else if (status == PARSE_CONTINUE_1) {
/* Web post request. */
line = __LINE__;
ret = PARSE_SUCCESS;
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);
line = __LINE__;
ret = 0;
goto ExitFunction;
} else {
/* partial msg */
*http_error_code = HTTP_BAD_REQUEST; /* or response */
line = __LINE__;
ret = UPNP_E_BAD_HTTPMSG;
goto ExitFunction;
}
} else {
*http_error_code = parser->http_error_code;
line = __LINE__;
ret = num_read;
goto ExitFunction;
}
}
ExitFunction:
if (ret != UPNP_E_SUCCESS) {
UpnpPrintf(UPNP_ALL, HTTP, __FILE__, line,
"(http_RecvMessage): Error %d, http_error_code = %d.\n",
ret,
*http_error_code);
}
return ret;
}
/************************************************************************
* Function: http_SendMessage
*
* Parameters:
* IN SOCKINFO *info ; Socket information object
* IN OUT int * TimeOut ; time out value
* IN const char* fmt, ... Pattern format to take actions upon
*
* Description:
* Sends a message to the destination based on the
* IN const char* fmt parameter
* fmt types:
* 'f': arg = const char * file name
* 'm': arg1 = const char * mem_buffer; arg2= size_t buf_length
* E.g.:
* char *buf = "POST /xyz.cgi http/1.1\r\n\r\n";
* char *filename = "foo.dat";
* int status = http_SendMessage( tcpsock, "mf",
* buf, strlen(buf),
* filename );
*
* Returns:
* UPNP_E_OUTOF_MEMORY
* UPNP_E_FILE_READ_ERROR
* UPNP_E_SUCCESS
************************************************************************/
int
http_SendMessage( IN SOCKINFO * info,
IN OUT int *TimeOut,
IN const char *fmt,
... )
{
char c;
char *buf = NULL;
size_t buf_length;
char *filename = NULL;
FILE *Fp;
int num_read;
int num_written;
off_t amount_to_be_read = 0;
va_list argp;
char *file_buf = NULL;
char *ChunkBuf = NULL;
struct SendInstruction *Instr = NULL;
char Chunk_Header[CHUNK_HEADER_SIZE];
int RetVal = 0;
/* 10 byte allocated for chunk header. */
int Data_Buf_Size = WEB_SERVER_BUF_SIZE;
va_start( argp, fmt );
while( ( c = *fmt++ ) != 0 ) {
if( c == 'I' ) {
Instr = va_arg(argp, struct SendInstruction *);
assert( Instr );
if( Instr->ReadSendSize >= 0 ) {
amount_to_be_read = Instr->ReadSendSize;
} else {
amount_to_be_read = Data_Buf_Size;
}
if( amount_to_be_read < WEB_SERVER_BUF_SIZE ) {
Data_Buf_Size = amount_to_be_read;
}
ChunkBuf = (char *)malloc(
Data_Buf_Size + CHUNK_HEADER_SIZE + CHUNK_TAIL_SIZE);
if( !ChunkBuf ) {
return UPNP_E_OUTOF_MEMORY;
}
file_buf = ChunkBuf + CHUNK_HEADER_SIZE;
} else if( c == 'f' ) {
/* file name */
filename = va_arg(argp, char *);
if( Instr && Instr->IsVirtualFile ) {
Fp = (virtualDirCallback.open)( filename, UPNP_READ );
} else {
Fp = fopen( filename, "rb" );
}
if( Fp == NULL ) {
free( ChunkBuf );
return UPNP_E_FILE_READ_ERROR;
}
if( Instr && Instr->IsRangeActive && Instr->IsVirtualFile ) {
if( virtualDirCallback.seek( Fp, Instr->RangeOffset,
SEEK_CUR ) != 0 ) {
free( ChunkBuf );
return UPNP_E_FILE_READ_ERROR;
}
} else if( Instr && Instr->IsRangeActive ) {
if( fseeko( Fp, Instr->RangeOffset, SEEK_CUR ) != 0 ) {
free( ChunkBuf );
return UPNP_E_FILE_READ_ERROR;
}
}
while( amount_to_be_read ) {
if( Instr ) {
int n = (amount_to_be_read >= Data_Buf_Size) ?
Data_Buf_Size : amount_to_be_read;
if( Instr->IsVirtualFile ) {
num_read = virtualDirCallback.read( Fp, file_buf, n );
} else {
num_read = fread( file_buf, 1, n, Fp );
}
amount_to_be_read = amount_to_be_read - num_read;
if( Instr->ReadSendSize < 0 ) {
/* read until close */
amount_to_be_read = Data_Buf_Size;
}
} else {
num_read = fread( file_buf, 1, Data_Buf_Size, Fp );
}
if( num_read == 0 ) {
/* EOF so no more to send. */
if( Instr && Instr->IsChunkActive ) {
char *str = "0\r\n\r\n";
num_written = sock_write(info, str, strlen(str), TimeOut);
} else {
RetVal = UPNP_E_FILE_READ_ERROR;
}
goto Cleanup_File;
}
/* Create chunk for the current buffer. */
if( Instr && Instr->IsChunkActive ) {
/* Copy CRLF at the end of the chunk */
memcpy( file_buf + num_read, "\r\n", 2 );
/* Hex length for the chunk size. */
sprintf( Chunk_Header, "%x", num_read );
/*itoa(num_read,Chunk_Header,16); */
strcat( Chunk_Header, "\r\n" );
/* Copy the chunk size header */
memcpy( file_buf - strlen( Chunk_Header ),
Chunk_Header, strlen( Chunk_Header ) );
/* on the top of the buffer. */
/*file_buf[num_read+strlen(Chunk_Header)] = NULL; */
/*printf("Sending %s\n",file_buf-strlen(Chunk_Header)); */
num_written = sock_write(
info, file_buf - strlen( Chunk_Header ),
num_read + strlen( Chunk_Header ) + 2, TimeOut );
if( num_written !=
num_read + ( int )strlen( Chunk_Header ) + 2 ) {
/* Send error nothing we can do. */
goto Cleanup_File;
}
} else {
/* write data */
num_written = sock_write( info, file_buf, num_read, TimeOut );
UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
">>> (SENT) >>>\n%.*s\n------------\n",
( int )num_written, file_buf );
/* Send error nothing we can do */
if( num_written != num_read ) {
goto Cleanup_File;
}
}
} /* while */
Cleanup_File:
va_end( argp );
if( Instr && Instr->IsVirtualFile ) {
virtualDirCallback.close( Fp );
} else {
fclose( Fp );
}
free( ChunkBuf );
return RetVal;
} else if( c == 'b' ) {
/* memory buffer */
buf = va_arg(argp, char *);
buf_length = va_arg(argp, size_t);
if( buf_length > 0 ) {
num_written = sock_write( info, buf, buf_length, TimeOut );
UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
">>> (SENT) >>>\n"
"%.*s\nbuf_length=%d, num_written=%d\n"
"------------\n",
(int)buf_length, buf, (int)buf_length, num_written );
if( (size_t)num_written != buf_length ) {
goto end;
}
}
}
}
end:
va_end( argp );
free( ChunkBuf );
return 0;
}
/************************************************************************
* Function: http_RequestAndResponse
*
* Parameters:
* IN uri_type* destination; Destination URI object which contains
* remote IP address among other elements
* IN const char* request; Request to be sent
* IN size_t request_length; Length of the request
* IN http_method_t req_method; HTTP Request method
* IN int timeout_secs; time out value
* OUT http_parser_t* response; Parser object to receive the repsonse
*
* Description:
* Initiates socket, connects to the destination, sends a
* request and waits for the response from the remote end
*
* Returns:
* UPNP_E_SOCKET_ERROR
* UPNP_E_SOCKET_CONNECT
* Error Codes returned by http_SendMessage
* Error Codes returned by http_RecvMessage
************************************************************************/
int http_RequestAndResponse(
IN uri_type *destination,
IN const char *request,
IN size_t request_length,
IN http_method_t req_method,
IN int timeout_secs,
OUT http_parser_t *response)
{
SOCKET tcp_connection;
int ret_code;
int sockaddr_len;
int http_error_code;
SOCKINFO info;
tcp_connection = socket(
destination->hostport.IPaddress.ss_family, SOCK_STREAM, 0);
if (tcp_connection == -1) {
parser_response_init(response, req_method);
return UPNP_E_SOCKET_ERROR;
}
if (sock_init(&info, tcp_connection) != UPNP_E_SUCCESS) {
parser_response_init(response, req_method);
ret_code = UPNP_E_SOCKET_ERROR;
goto end_function;
}
/* connect */
sockaddr_len = destination->hostport.IPaddress.ss_family == AF_INET6 ?
sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
ret_code = private_connect(info.socket,
(struct sockaddr *)&(destination->hostport.IPaddress), sockaddr_len);
if (ret_code == -1) {
parser_response_init(response, req_method);
ret_code = UPNP_E_SOCKET_CONNECT;
goto end_function;
}
/* send request */
ret_code = http_SendMessage(&info, &timeout_secs, "b",
request, request_length);
if (ret_code != 0) {
parser_response_init(response, req_method);
goto end_function;
}
/* recv response */
ret_code = http_RecvMessage(&info, response, req_method,
&timeout_secs, &http_error_code);
end_function:
/* should shutdown completely */
sock_destroy(&info, SD_BOTH);
return ret_code;
}
/************************************************************************
* Function: http_Download
*
* Parameters:
* IN const char* url_str; String as a URL
* IN int timeout_secs; time out value
* OUT char** document; buffer to store the document extracted
* from the donloaded message.
* OUT int* doc_length; length of the extracted document
* OUT char* content_type; Type of content
*
* Description:
* Download the document message and extract the document
* from the message.
*
* Return: int
* UPNP_E_SUCCESS
* UPNP_E_INVALID_URL
************************************************************************/
int
http_Download( IN const char *url_str,
IN int timeout_secs,
OUT char **document,
OUT int *doc_length,
OUT char *content_type )
{
int ret_code;
uri_type url;
char *msg_start;
char *entity_start;
char *hoststr;
char *temp;
http_parser_t response;
size_t msg_length;
size_t hostlen;
memptr ctype;
size_t copy_len;
membuffer request;
char *urlPath = alloca( strlen( url_str ) + 1 );
/*ret_code = parse_uri( (char*)url_str, strlen(url_str), &url ); */
UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__, "DOWNLOAD URL : %s\n",
url_str );
ret_code =
http_FixStrUrl( ( char * )url_str, strlen( url_str ), &url );
if( ret_code != UPNP_E_SUCCESS ) {
return ret_code;
}
/* make msg */
membuffer_init( &request );
strcpy( urlPath, url_str );
hoststr = strstr( urlPath, "//" );
if( hoststr == NULL ) {
return UPNP_E_INVALID_URL;
}
hoststr += 2;
temp = strchr( hoststr, '/' );
if( temp == NULL ) {
return UPNP_E_INVALID_URL;
}
*temp = '\0';
hostlen = strlen( hoststr );
*temp = '/';
UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
"HOSTNAME : %s Length : %"PRIzu"\n", hoststr, hostlen );
ret_code = http_MakeMessage(
&request, 1, 1,
"Q" "s" "bcDCUc",
HTTPMETHOD_GET, url.pathquery.buff, url.pathquery.size,
"HOST: ",
hoststr, hostlen );
if( ret_code != 0 ) {
UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
"HTTP Makemessage failed\n" );
membuffer_destroy( &request );
return ret_code;
}
UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
"HTTP Buffer:\n%s\n" "----------END--------\n",
request.buf);
/* get doc msg */
ret_code =
http_RequestAndResponse( &url, request.buf, request.length,
HTTPMETHOD_GET, timeout_secs, &response );
if( ret_code != 0 ) {
httpmsg_destroy( &response.msg );
membuffer_destroy( &request );
return ret_code;
}
UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__, "Response\n" );
print_http_headers( &response.msg );
/* optional content-type */
if( content_type ) {
if( httpmsg_find_hdr( &response.msg, HDR_CONTENT_TYPE, &ctype ) ==
NULL ) {
*content_type = '\0'; /* no content-type */
} else {
/* safety */
copy_len = ctype.length < LINE_SIZE - 1 ?
ctype.length : LINE_SIZE - 1;
memcpy( content_type, ctype.buf, copy_len );
content_type[copy_len] = '\0';
}
}
/* */
/* extract doc from msg */
/* */
if( ( *doc_length = ( int )response.msg.entity.length ) == 0 ) {
/* 0-length msg */
*document = NULL;
} else if( response.msg.status_code == HTTP_OK ) {
/*LEAK_FIX_MK */
/* copy entity */
entity_start = response.msg.entity.buf; /* what we want */
msg_length = response.msg.msg.length; /* save for posterity */
msg_start = membuffer_detach( &response.msg.msg ); /* whole msg */
/* move entity to the start; copy null-terminator too */
memmove( msg_start, entity_start, *doc_length + 1 );
/* save mem for body only */
*document = realloc( msg_start, *doc_length + 1 ); /*LEAK_FIX_MK */
/* *document = Realloc( msg_start,msg_length, *doc_length + 1 ); LEAK_FIX_MK */
/* shrink can't fail */
assert( ( int )msg_length > *doc_length );
assert( *document != NULL );
}
if( response.msg.status_code == HTTP_OK ) {
ret_code = 0; /* success */
} else {
/* server sent error msg (not requested doc) */
ret_code = response.msg.status_code;
}
httpmsg_destroy( &response.msg );
membuffer_destroy( &request );
return ret_code;
}
typedef struct HTTPPOSTHANDLE {
SOCKINFO sock_info;
int contentLength;
} http_post_handle_t;
/************************************************************************
* Function: MakePostMessage
*
* Parameters:
* const char *url_str; String as a URL
* membuffer *request; Buffer containing the request
* uri_type *url; URI object containing the scheme,
* path query token, etc.
* int contentLength; length of content
* const char *contentType; Type of content
*
* Description:
* Makes the message for the HTTP POST message
*
* Returns:
* UPNP_E_INVALID_URL
* UPNP_E_INVALID_PARAM
* UPNP_E_SUCCESS
************************************************************************/
int
MakePostMessage( const char *url_str,
membuffer * request,
uri_type * url,
int contentLength,
const char *contentType )
{
int ret_code = 0;
char *urlPath = alloca( strlen( url_str ) + 1 );
size_t hostlen = 0;
char *hoststr,
*temp;
UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
"DOWNLOAD URL : %s\n", url_str );
ret_code =
http_FixStrUrl( ( char * )url_str, strlen( url_str ), url );
if( ret_code != UPNP_E_SUCCESS ) {
return ret_code;
}
/* make msg */
membuffer_init( request );
strcpy( urlPath, url_str );
hoststr = strstr( urlPath, "//" );
if( hoststr == NULL ) {
return UPNP_E_INVALID_URL;
}
hoststr += 2;
temp = strchr( hoststr, '/' );
if( temp == NULL ) {
return UPNP_E_INVALID_URL;
}
*temp = '\0';
hostlen = strlen( hoststr );
*temp = '/';
UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
"HOSTNAME : %s Length : %"PRIzu"\n", hoststr, hostlen );
if( contentLength >= 0 ) {
ret_code = http_MakeMessage(
request, 1, 1,
"Q" "s" "bcDCU" "T" "Nc",
HTTPMETHOD_POST, url->pathquery.buff, url->pathquery.size,
"HOST: ",
hoststr, hostlen,
contentType,
(off_t)contentLength );
} else if( contentLength == UPNP_USING_CHUNKED ) {
ret_code = http_MakeMessage(
request, 1, 1,
"Q" "s" "bcDCU" "TKc",
HTTPMETHOD_POST, url->pathquery.buff, url->pathquery.size,
"HOST: ",
hoststr, hostlen,
contentType );
} else if( contentLength == UPNP_UNTIL_CLOSE ) {
ret_code = http_MakeMessage(
request, 1, 1,
"Q" "s" "bcDCU" "Tc",
HTTPMETHOD_POST, url->pathquery.buff, url->pathquery.size,
"HOST: ",
hoststr, hostlen,
contentType );
} else {
ret_code = UPNP_E_INVALID_PARAM;
}
if( ret_code != 0 ) {
UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
"HTTP Makemessage failed\n" );
membuffer_destroy( request );
return ret_code;
}
UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
"HTTP Buffer:\n%s\n" "----------END--------\n",
request->buf);
return UPNP_E_SUCCESS;
}
/************************************************************************
* Function: http_WriteHttpPost
*
* Parameters:
* IN void *Handle: Handle to the http post object
* IN char *buf: Buffer to send to peer, if format used
* is not UPNP_USING_CHUNKED,
* IN unsigned int *size: Size of the data to be sent.
* IN int timeout: time out value
*
* Description:
* Formats data if format used is UPNP_USING_CHUNKED.
* Writes data on the socket connected to the peer.
*
* Return: int
* UPNP_E_SUCCESS - On Success
* UPNP_E_INVALID_PARAM - Invalid Parameter
* -1 - On Socket Error.
************************************************************************/
int
http_WriteHttpPost( IN void *Handle,
IN char *buf,
IN unsigned int *size,
IN int timeout )
{
http_post_handle_t *handle = ( http_post_handle_t * ) Handle;
char *tempbuf = NULL;
int tempbufSize = 0;
int freeTempbuf = 0;
int numWritten = 0;
if( ( !handle ) || ( !size ) || ( ( ( *size ) > 0 ) && !buf )
|| ( ( *size ) < 0 ) ) {
if(size) ( *size ) = 0;
return UPNP_E_INVALID_PARAM;
}
if( handle->contentLength == UPNP_USING_CHUNKED ) {
if( ( *size ) ) {
int tempSize = 0;
tempbuf = ( char * )malloc(
*size + CHUNK_HEADER_SIZE + CHUNK_TAIL_SIZE );
if ( tempbuf == NULL) {
return UPNP_E_OUTOF_MEMORY;
}
/* begin chunk */
sprintf( tempbuf, "%x\r\n", ( *size ) );
tempSize = strlen( tempbuf );
memcpy( tempbuf + tempSize, buf, ( *size ) );
memcpy( tempbuf + tempSize + ( *size ), "\r\n", 2 );
/* end of chunk */
tempbufSize = tempSize + ( *size ) + 2;
freeTempbuf = 1;
}
} else {
tempbuf = buf;
tempbufSize = ( *size );
}
numWritten =
sock_write( &handle->sock_info, tempbuf, tempbufSize, &timeout );
/*(*size) = sock_write(&handle->sock_info,tempbuf,tempbufSize,&timeout); */
if( freeTempbuf ) {
free( tempbuf );
}
if( numWritten < 0 ) {
( *size ) = 0;
return numWritten;
} else {
( *size ) = numWritten;
return UPNP_E_SUCCESS;
}
}
/************************************************************************
* Function: http_CloseHttpPost
*
* Parameters:
* IN void *Handle; Handle to the http post object
* IN OUT int *httpStatus; HTTP status returned on receiving a
* response message
* IN int timeout; time out value
*
* Description:
* Sends remaining data if using UPNP_USING_CHUNKED
* format. Receives any more messages. Destroys socket and any socket
* associated memory. Frees handle associated with the HTTP POST msg.
*
* Return: int
* UPNP_E_SUCCESS - On success
* UPNP_E_INVALID_PARAM - Invalid Parameter
************************************************************************/
int
http_CloseHttpPost( IN void *Handle,
IN OUT int *httpStatus,
IN int timeout )
{
int retc = 0;
http_parser_t response;
int http_error_code;
http_post_handle_t *handle = Handle;
if( ( !handle ) || ( !httpStatus ) ) {
return UPNP_E_INVALID_PARAM;
}
if( handle->contentLength == UPNP_USING_CHUNKED ) {
retc = sock_write( &handle->sock_info, "0\r\n\r\n", strlen( "0\r\n\r\n" ), &timeout ); /*send last chunk */
}
/*read response */
parser_response_init( &response, HTTPMETHOD_POST );
retc =
http_RecvMessage( &handle->sock_info, &response, HTTPMETHOD_POST,
&timeout, &http_error_code );
( *httpStatus ) = http_error_code;
sock_destroy( &handle->sock_info, SD_BOTH ); /*should shutdown completely */
httpmsg_destroy( &response.msg );
free( handle );
return retc;
}
/************************************************************************
* Function: http_OpenHttpPost
*
* Parameters:
* IN const char *url_str; String as a URL
* IN OUT void **Handle; Pointer to buffer to store HTTP
* post handle
* IN const char *contentType; Type of content
* IN int contentLength; length of content
* IN int timeout; time out value
*
* Description:
* Makes the HTTP POST message, connects to the peer,
* sends the HTTP POST request. Adds the post handle to buffer of
* such handles
*
* Return : int;
* UPNP_E_SUCCESS - On success
* UPNP_E_INVALID_PARAM - Invalid Parameter
* UPNP_E_OUTOF_MEMORY
* UPNP_E_SOCKET_ERROR
* UPNP_E_SOCKET_CONNECT
************************************************************************/
int http_OpenHttpPost(
IN const char *url_str,
IN OUT void **Handle,
IN const char *contentType,
IN int contentLength,
IN int timeout)
{
int ret_code;
int sockaddr_len;
SOCKET tcp_connection;
membuffer request;
http_post_handle_t *handle = NULL;
uri_type url;
if ( !url_str || !Handle || !contentType) {
return UPNP_E_INVALID_PARAM;
}
*Handle = handle;
ret_code = MakePostMessage(url_str, &request, &url,
contentLength, contentType);
if (ret_code != UPNP_E_SUCCESS) {
return ret_code;
}
handle = (http_post_handle_t *) malloc(sizeof(http_post_handle_t));
if (!handle) {
return UPNP_E_OUTOF_MEMORY;
}
handle->contentLength = contentLength;
tcp_connection = socket(url.hostport.IPaddress.ss_family, SOCK_STREAM, 0);
if (tcp_connection == -1) {
ret_code = UPNP_E_SOCKET_ERROR;
goto errorHandler;
}
if (sock_init(&handle->sock_info, tcp_connection) != UPNP_E_SUCCESS) {
sock_destroy( &handle->sock_info, SD_BOTH );
ret_code = UPNP_E_SOCKET_ERROR;
goto errorHandler;
}
sockaddr_len = url.hostport.IPaddress.ss_family == AF_INET6 ?
sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
ret_code = private_connect(handle->sock_info.socket,
(struct sockaddr *)&(url.hostport.IPaddress), sockaddr_len);
if (ret_code == -1) {
sock_destroy(&handle->sock_info, SD_BOTH);
ret_code = UPNP_E_SOCKET_CONNECT;
goto errorHandler;
}
/* send request */
ret_code = http_SendMessage(&handle->sock_info, &timeout, "b",
request.buf, request.length);
if (ret_code != 0) {
sock_destroy(&handle->sock_info, SD_BOTH);
}
errorHandler:
membuffer_destroy(&request);
*Handle = handle;
return ret_code;
}
typedef struct HTTPGETHANDLE {
http_parser_t response;
SOCKINFO sock_info;
int entity_offset;
int cancel;
} http_get_handle_t;
/************************************************************************
* Function: MakeGetMessage
*
* Parameters:
* const char *url_str ; String as a URL
* const char *proxy_str ; String as a URL of proxy to use
* membuffer *request ; Buffer containing the request
* uri_type *url ; URI object containing the scheme, path
* query token, etc.
*
* Description:
* Makes the message for the HTTP GET method
*
* Returns:
* UPNP_E_INVALID_URL
* Error Codes returned by http_MakeMessage
* UPNP_E_SUCCESS
************************************************************************/
int
MakeGetMessage( const char *url_str,
const char *proxy_str,
membuffer * request,
uri_type * url )
{
int ret_code;
char *urlPath = alloca( strlen( url_str ) + 1 );
size_t querylen = 0;
const char *querystr;
size_t hostlen = 0;
char *hoststr,
*temp;
UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
"DOWNLOAD URL : %s\n", url_str );
ret_code =
http_FixStrUrl( ( char * )url_str, strlen( url_str ), url );
if( ret_code != UPNP_E_SUCCESS ) {
return ret_code;
}
/* make msg */
membuffer_init( request );
strcpy( urlPath, url_str );
hoststr = strstr( urlPath, "//" );
if( hoststr == NULL ) {
return UPNP_E_INVALID_URL;
}
hoststr += 2;
temp = strchr( hoststr, '/' );
if( temp == NULL ) {
return UPNP_E_INVALID_URL;
}
*temp = '\0';
hostlen = strlen( hoststr );
*temp = '/';
UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
"HOSTNAME : %s Length : %"PRIzu"\n", hoststr, hostlen );
if( proxy_str ) {
querystr = url_str;
querylen = strlen( querystr );
} else {
querystr = url->pathquery.buff;
querylen = url->pathquery.size;
}
ret_code = http_MakeMessage(
request, 1, 1,
"Q" "s" "bcDCUc",
HTTPMETHOD_GET, querystr, querylen,
"HOST: ",
hoststr, hostlen );
if( ret_code != 0 ) {
UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
"HTTP Makemessage failed\n" );
membuffer_destroy( request );
return ret_code;
}
UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
"HTTP Buffer:\n%s\n" "----------END--------\n",
request->buf);
return UPNP_E_SUCCESS;
}
/*!
* \brief Parses already exiting data. If not complete reads more
* data on the connected socket. The read data is then parsed. The
* same methid is carried out for headers.
*
* \return integer:
* \li \c PARSE_OK - On Success
* \li \c PARSE_FAILURE - Failure to parse data correctly
* \li \c UPNP_E_BAD_HTTPMSG - Socker read() returns an error
*/
static int ReadResponseLineAndHeaders(
/*! Socket information object. */
IN SOCKINFO *info,
/*! HTTP Parser object. */
IN OUT http_parser_t *parser,
/*! Time out value. */
IN OUT int *timeout_secs,
/*! HTTP errror code returned. */
IN OUT int *http_error_code)
{
parse_status_t status;
int num_read;
char buf[2 * 1024];
int done = 0;
int ret_code = 0;
/*read response line */
status = parser_parse_responseline( parser );
if( status == PARSE_OK ) {
done = 1;
} else if( status == PARSE_INCOMPLETE ) {
done = 0;
} else {
/*error */
return status;
}
while( !done ) {
num_read = sock_read( info, buf, sizeof( buf ), timeout_secs );
if( num_read > 0 ) {
/* append data to buffer */
ret_code = membuffer_append( &parser->msg.msg, buf, num_read );
if( ret_code != 0 ) {
/* set failure status */
parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
return PARSE_FAILURE;
}
status = parser_parse_responseline( parser );
if( status == PARSE_OK ) {
done = 1;
} else if( status == PARSE_INCOMPLETE ) {
done = 0;
} else {
/*error */
return status;
}
} else if( num_read == 0 ) {
/* partial msg */
*http_error_code = HTTP_BAD_REQUEST; /* or response */
return UPNP_E_BAD_HTTPMSG;
} else {
*http_error_code = parser->http_error_code;
return num_read;
}
}
done = 0;
status = parser_parse_headers( parser );
if( ( status == PARSE_OK ) && ( parser->position == POS_ENTITY ) ) {
done = 1;
} else if( status == PARSE_INCOMPLETE ) {
done = 0;
} else {
/*error */
return status;
}
/*read headers */
while( !done ) {
num_read = sock_read( info, buf, sizeof( buf ), timeout_secs );
if( num_read > 0 ) {
/* append data to buffer */
ret_code = membuffer_append( &parser->msg.msg, buf, num_read );
if( ret_code != 0 ) {
/* set failure status */
parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
return PARSE_FAILURE;
}
status = parser_parse_headers( parser );
if( ( status == PARSE_OK )
&& ( parser->position == POS_ENTITY ) ) {
done = 1;
} else if( status == PARSE_INCOMPLETE ) {
done = 0;
} else {
/*error */
return status;
}
} else if( num_read == 0 ) {
/* partial msg */
*http_error_code = HTTP_BAD_REQUEST; /* or response */
return UPNP_E_BAD_HTTPMSG;
} else {
*http_error_code = parser->http_error_code;
return num_read;
}
}
return PARSE_OK;
}
/************************************************************************
* Function: http_ReadHttpGet
*
* Parameters:
* IN void *Handle; Handle to the HTTP get object
* IN OUT char *buf; Buffer to get the read and parsed data
* IN OUT size_t *size; Size of the buffer passed
* IN int timeout; time out value
*
* Description:
* Parses already existing data, then gets new data.
* Parses and extracts information from the new data.
*
* Return: int
* UPNP_E_SUCCESS - On success
* UPNP_E_INVALID_PARAM - Invalid Parameter
* UPNP_E_BAD_RESPONSE
* UPNP_E_BAD_HTTPMSG
* UPNP_E_CANCELED
************************************************************************/
int http_ReadHttpGet(
IN void *Handle,
IN OUT char *buf,
IN OUT size_t *size,
IN int timeout)
{
http_get_handle_t *handle = Handle;
parse_status_t status;
int num_read;
xboolean ok_on_close = FALSE;
char tempbuf[2 * 1024];
int ret_code = 0;
if( !handle || !size || (*size > 0 && !buf) || *size < 0) {
if(size) {
*size = 0;
}
return UPNP_E_INVALID_PARAM;
}
/*first parse what has already been gotten */
if( handle->response.position != POS_COMPLETE ) {
status = parser_parse_entity( &handle->response );
} else {
status = PARSE_SUCCESS;
}
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 */
*size = 0;
return UPNP_E_BAD_RESPONSE;
}
/*read more if necessary entity */
while( ( ( handle->response.msg.entity_offset + *size ) >
handle->response.msg.entity.length )
&& ( ! handle->cancel )
&& ( handle->response.position != POS_COMPLETE ) ) {
num_read =
sock_read( &handle->sock_info, tempbuf, sizeof( tempbuf ),
&timeout );
if( num_read > 0 ) {
/* append data to buffer */
ret_code = membuffer_append( &handle->response.msg.msg,
tempbuf, num_read );
if( ret_code != 0 ) {
/* set failure status */
handle->response.http_error_code =
HTTP_INTERNAL_SERVER_ERROR;
*size = 0;
return PARSE_FAILURE;
}
status = parser_parse_entity( &handle->response );
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 */
*size = 0;
return UPNP_E_BAD_RESPONSE;
}
} else if( num_read == 0 ) {
if( ok_on_close ) {
UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
"<<< (RECVD) <<<\n%s\n-----------------\n",
handle->response.msg.msg.buf );
handle->response.position = POS_COMPLETE;
} else {
/* partial msg */
*size = 0;
handle->response.http_error_code = HTTP_BAD_REQUEST; /* or response */
return UPNP_E_BAD_HTTPMSG;
}
} else {
*size = 0;
return num_read;
}
}
if ((handle->response.msg.entity_offset + *size) > handle->response.msg.entity.length) {
*size = handle->response.msg.entity.length - handle->response.msg.entity_offset;
}
memcpy(buf,
&handle->response.msg.msg.buf[handle->response.entity_start_position],
*size);
if (*size > 0) {
membuffer_delete(&handle->response.msg.msg,
handle->response.entity_start_position,
*size);
}
handle->response.msg.entity_offset += *size;
if (handle->cancel) {
return UPNP_E_CANCELED;
}
return UPNP_E_SUCCESS;
}
/************************************************************************
* Function: http_HttpGetProgress
*
* Parameters:
* IN void *Handle; Handle to the HTTP get object
* OUT size_t *length; Buffer to get the read and parsed data
* OUT size_t *total; Size of tge buffer passed
*
* Description:
* Extracts information from the Handle to the HTTP get object.
*
* Return: int
* UPNP_E_SUCCESS - On Sucess
* UPNP_E_INVALID_PARAM - Invalid Parameter
************************************************************************/
int http_HttpGetProgress(
IN void *Handle,
OUT size_t *length,
OUT size_t *total)
{
http_get_handle_t *handle = Handle;
if (!handle || !length || !total) {
return UPNP_E_INVALID_PARAM;
}
*length = handle->response.msg.entity.length;
*total = handle->response.content_length;
return UPNP_E_SUCCESS;
}
/************************************************************************
* Function: http_CancelHttpGet
*
* Parameters:
* IN void *Handle; Handle to HTTP get object
*
* Description:
* Set the cancel flag of the HttpGet handle
*
* Return: int
* UPNP_E_SUCCESS - On Success
* UPNP_E_INVALID_PARAM - Invalid Parameter
************************************************************************/
int
http_CancelHttpGet( IN void *Handle )
{
http_get_handle_t *handle = Handle;
if( !handle ) {
return UPNP_E_INVALID_PARAM;
}
handle->cancel = 1;
return UPNP_E_SUCCESS;
}
/************************************************************************
* Function: http_CloseHttpGet
*
* Parameters:
* IN void *Handle; Handle to HTTP get object
*
* Description:
* Clears the handle allocated for the HTTP GET operation
* Clears socket states and memory allocated for socket operations.
*
* Return: int
* UPNP_E_SUCCESS - On Success
* UPNP_E_INVALID_PARAM - Invalid Parameter
************************************************************************/
int
http_CloseHttpGet( IN void *Handle )
{
http_get_handle_t *handle = Handle;
if( !handle ) {
return UPNP_E_INVALID_PARAM;
}
sock_destroy( &handle->sock_info, SD_BOTH ); /*should shutdown completely */
httpmsg_destroy( &handle->response.msg );
free( handle );
return UPNP_E_SUCCESS;
}
/************************************************************************
* Function: http_OpenHttpGet
*
* Parameters:
* IN const char *url_str: String as a URL
* IN OUT void **Handle: Pointer to buffer to store HTTP
* post handle
* IN OUT char **contentType: Type of content
* OUT int *contentLength: length of content
* OUT int *httpStatus: HTTP status returned on receiving a
* response message
* IN int timeout: time out value
*
* Description:
* Makes the HTTP GET message, connects to the peer,
* sends the HTTP GET request, gets the response and parses the
* response.
*
* Return: int
* UPNP_E_SUCCESS - On Success
* UPNP_E_INVALID_PARAM - Invalid Paramters
* UPNP_E_OUTOF_MEMORY
* UPNP_E_SOCKET_ERROR
* UPNP_E_BAD_RESPONSE
************************************************************************/
int
http_OpenHttpGet( IN const char *url_str,
IN OUT void **Handle,
IN OUT char **contentType,
OUT int *contentLength,
OUT int *httpStatus,
IN int timeout )
{
return http_OpenHttpGetProxy(url_str, NULL, Handle, contentType, contentLength, httpStatus, timeout);
}
/************************************************************************
* Function: http_OpenHttpGetProxy
*
* Parameters:
* IN const char *url_str; String as a URL
* IN const char *proxy_str; String as a URL
* IN OUT void **Handle; Pointer to buffer to store HTTP
* post handle
* IN OUT char **contentType; Type of content
* OUT int *contentLength; length of content
* OUT int *httpStatus; HTTP status returned on receiving a
* response message
* IN int timeout: time out value
*
* Description:
* Makes the HTTP GET message, connects to the peer,
* sends the HTTP GET request, gets the response and parses the response.
* If a proxy URL is defined then the connection is made there.
*
* Return: int
* UPNP_E_SUCCESS - On Success
* UPNP_E_INVALID_PARAM - Invalid Paramters
* UPNP_E_OUTOF_MEMORY
* UPNP_E_SOCKET_ERROR
* UPNP_E_BAD_RESPONSE
************************************************************************/
int http_OpenHttpGetProxy(
IN const char *url_str,
IN const char *proxy_str,
IN OUT void **Handle,
IN OUT char **contentType,
OUT int *contentLength,
OUT int *httpStatus,
IN int timeout)
{
int ret_code;
int sockaddr_len;
int http_error_code;
memptr ctype;
SOCKET tcp_connection;
membuffer request;
http_get_handle_t *handle = NULL;
uri_type url;
uri_type proxy;
uri_type *peer;
parse_status_t status;
if (!url_str || !Handle || !contentType || !httpStatus ) {
return UPNP_E_INVALID_PARAM;
}
*httpStatus = 0;
*Handle = handle;
*contentType = NULL;
*contentLength = 0;
ret_code = MakeGetMessage(url_str, proxy_str, &request, &url);
if (ret_code != UPNP_E_SUCCESS) {
return ret_code;
}
if (proxy_str) {
ret_code = http_FixStrUrl((char *)proxy_str, strlen(proxy_str), &proxy);
peer = &proxy;
} else {
peer = &url;
}
handle = (http_get_handle_t *)malloc(sizeof(http_get_handle_t));
if (!handle) {
return UPNP_E_OUTOF_MEMORY;
}
handle->cancel = 0;
parser_response_init(&handle->response, HTTPMETHOD_GET);
tcp_connection = socket(peer->hostport.IPaddress.ss_family, SOCK_STREAM, 0);
if (tcp_connection == -1) {
ret_code = UPNP_E_SOCKET_ERROR;
goto errorHandler;
}
if (sock_init(&handle->sock_info, tcp_connection) != UPNP_E_SUCCESS) {
sock_destroy(&handle->sock_info, SD_BOTH);
ret_code = UPNP_E_SOCKET_ERROR;
goto errorHandler;
}
sockaddr_len = peer->hostport.IPaddress.ss_family == AF_INET6 ?
sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
ret_code = private_connect(handle->sock_info.socket,
(struct sockaddr *)&(peer->hostport.IPaddress), sockaddr_len);
if (ret_code == -1) {
sock_destroy(&handle->sock_info, SD_BOTH);
ret_code = UPNP_E_SOCKET_CONNECT;
goto errorHandler;
}
/* send request */
ret_code = http_SendMessage(&handle->sock_info, &timeout, "b",
request.buf, request.length);
if (ret_code) {
sock_destroy(&handle->sock_info, SD_BOTH);
goto errorHandler;
}
status = ReadResponseLineAndHeaders(&handle->sock_info,
&handle->response, &timeout, &http_error_code);
if (status != PARSE_OK) {
ret_code = UPNP_E_BAD_RESPONSE;
goto errorHandler;
}
status = parser_get_entity_read_method(&handle->response);
if (status != PARSE_CONTINUE_1 && status != PARSE_SUCCESS) {
ret_code = UPNP_E_BAD_RESPONSE;
goto errorHandler;
}
*httpStatus = handle->response.msg.status_code;
ret_code = UPNP_E_SUCCESS;
if (!httpmsg_find_hdr(&handle->response.msg, HDR_CONTENT_TYPE, &ctype)) {
/* no content-type */
*contentType = NULL;
} else {
*contentType = ctype.buf;
}
if (handle->response.position == POS_COMPLETE) {
*contentLength = 0;
} else if (handle->response.ent_position == ENTREAD_USING_CHUNKED) {
*contentLength = UPNP_USING_CHUNKED;
} else if (handle->response.ent_position == ENTREAD_USING_CLEN) {
*contentLength = handle->response.content_length;
} else if (handle->response.ent_position == ENTREAD_UNTIL_CLOSE) {
*contentLength = UPNP_UNTIL_CLOSE;
}
errorHandler:
*Handle = handle;
membuffer_destroy(&request);
if (ret_code != UPNP_E_SUCCESS) {
httpmsg_destroy(&handle->response.msg);
}
return ret_code;
}
/************************************************************************
* Function: http_SendStatusResponse
*
* Parameters:
* IN SOCKINFO *info; Socket information object
* IN int http_status_code; error code returned while making
* or sending the response message
* IN int request_major_version; request major version
* IN int request_minor_version; request minor version
*
* Description:
* Generate a response message for the status query and send the
* status response.
*
* Return: int
* 0 -- success
* UPNP_E_OUTOF_MEMORY
* UPNP_E_SOCKET_WRITE
* UPNP_E_TIMEDOUT
************************************************************************/
int
http_SendStatusResponse( IN SOCKINFO * info,
IN int http_status_code,
IN int request_major_version,
IN int request_minor_version )
{
int response_major,
response_minor;
membuffer membuf;
int ret;
int timeout;
http_CalcResponseVersion( request_major_version, request_minor_version,
&response_major, &response_minor );
membuffer_init( &membuf );
membuf.size_inc = 70;
ret = http_MakeMessage(
&membuf, response_major, response_minor,
"RSCB",
http_status_code, /* response start line */
http_status_code ); /* body */
if( ret == 0 ) {
timeout = HTTP_DEFAULT_TIMEOUT;
ret = http_SendMessage( info, &timeout, "b",
membuf.buf, membuf.length );
}
membuffer_destroy( &membuf );
return ret;
}
/************************************************************************
* Function: http_MakeMessage
*
* Parameters:
* INOUT membuffer* buf; buffer with the contents of the
* message
* IN int http_major_version; HTTP major version
* IN int http_minor_version; HTTP minor version
* IN const char* fmt; Pattern format
* ...;
*
* Description:
* Generate an HTTP message based on the format that is specified
* in the input parameters.
*
* fmt types:
* 'B': arg = int status_code
* appends content-length, content-type and HTML body
* for given code
* 'b': arg1 = const char* buf;
* arg2 = size_t buf_length memory ptr
* 'C': (no args) appends a HTTP CONNECTION: close header
* depending on major,minor version
* 'c': (no args) appends CRLF "\r\n"
* 'D': (no args) appends HTTP DATE: header
* 'd': arg = int number -- appends decimal number
* 'G': arg = range information -- add range header
* 'h': arg = off_t number -- appends off_t number
* 'K': (no args) -- add chunky header
* 'L': arg = language information -- add Content-Language header if
* Accept-Language header is not empty and if
* WEB_SERVER_CONTENT_LANGUAGE is not empty
* 'N': arg1 = off_t content_length -- content-length header
* 'q': arg1 = http_method_t -- request start line and HOST header
* arg2 = (uri_type *)
* 'Q': arg1 = http_method_t; -- start line of request
* arg2 = char* url;
* arg3 = size_t url_length
* 'R': arg = int status_code -- adds a response start line
* 'S': (no args) appends HTTP SERVER: header
* 's': arg = const char* C_string
* 'T': arg = char * content_type; format
* e.g: "text/html"; content-type header
* 't': arg = time_t * gmt_time -- appends time in RFC 1123 fmt
* 'U': (no args) appends HTTP USER-AGENT: header
* 'X': arg = const char useragent; "redsonic" HTTP X-User-Agent: useragent
*
* Return: int
* 0 - On Success
* UPNP_E_OUTOF_MEMORY
* UPNP_E_INVALID_URL
************************************************************************/
int
http_MakeMessage( INOUT membuffer * buf,
IN int http_major_version,
IN int http_minor_version,
IN const char *fmt,
... )
{
char c;
char *s = NULL;
size_t num;
off_t bignum;
size_t length;
time_t *loc_time;
time_t curr_time;
struct tm *date;
char *start_str,
*end_str;
int status_code;
const char *status_msg;
http_method_t method;
const char *method_str;
const char *url_str;
const char *temp_str;
uri_type url;
uri_type *uri_ptr;
int error_code = UPNP_E_OUTOF_MEMORY;
va_list argp;
char tempbuf[200];
const char *weekday_str = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat";
const char *month_str = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0"
"Jul\0Aug\0Sep\0Oct\0Nov\0Dec";
va_start( argp, fmt );
while( ( c = *fmt++ ) != 0 ) {
if( c == 's' ) {
/* C string */
s = ( char * )va_arg( argp, char * );
assert( s );
UpnpPrintf(UPNP_ALL,HTTP,__FILE__,__LINE__,"Adding a string : %s\n", s);
if( membuffer_append( buf, s, strlen( s ) ) != 0 ) {
goto error_handler;
}
} else if( c == 'K' ) {
/* Add Chunky header */
if( membuffer_append
( buf, "TRANSFER-ENCODING: chunked\r\n",
strlen( "Transfer-Encoding: chunked\r\n" ) ) != 0 ) {
goto error_handler;
}
} else if( c == 'G' ) {
/* Add Range header */
struct SendInstruction *RespInstr;
RespInstr = (struct SendInstruction *)
va_arg( argp, struct SendInstruction *);
assert( RespInstr );
/* connection header */
if( membuffer_append
( buf, RespInstr->RangeHeader,
strlen( RespInstr->RangeHeader ) ) != 0 ) {
goto error_handler;
}
} else if( c == 'b' ) {
/* mem buffer */
s = ( char * )va_arg( argp, char * );
UpnpPrintf(UPNP_ALL,HTTP,__FILE__,__LINE__,
"Adding a char Buffer starting with: %c\n", s[0]);
assert( s );
length = ( size_t ) va_arg( argp, size_t );
if( membuffer_append( buf, s, length ) != 0 ) {
goto error_handler;
}
}
else if( c == 'c' ) {
/* crlf */
if( membuffer_append( buf, "\r\n", 2 ) != 0 ) {
goto error_handler;
}
}
else if( c == 'd' ) {
/* integer */
num = ( int )va_arg( argp, int );
sprintf( tempbuf, "%"PRIzu, num );
if( membuffer_append( buf, tempbuf, strlen( tempbuf ) ) != 0 ) {
goto error_handler;
}
}
else if( c == 'h' ) {
/* off_t */
bignum = ( off_t )va_arg( argp, off_t );
sprintf( tempbuf, "%"PRId64, (int64_t)bignum );
if( membuffer_append( buf, tempbuf, strlen( tempbuf ) ) != 0 ) {
goto error_handler;
}
}
else if( c == 't' || c == 'D' ) {
/* date */
if( c == 'D' ) {
/* header */
start_str = "DATE: ";
end_str = "\r\n";
curr_time = time( NULL );
date = gmtime( &curr_time );
} else {
/* date value only */
start_str = end_str = "";
loc_time = ( time_t * ) va_arg( argp, time_t * );
assert( loc_time );
date = gmtime( loc_time );
}
sprintf( tempbuf, "%s%s, %02d %s %d %02d:%02d:%02d GMT%s",
start_str,
&weekday_str[date->tm_wday * 4], date->tm_mday,
&month_str[date->tm_mon * 4], date->tm_year + 1900,
date->tm_hour, date->tm_min, date->tm_sec, end_str );
if( membuffer_append( buf, tempbuf, strlen( tempbuf ) ) != 0 ) {
goto error_handler;
}
} else if ( c == 'L' ) {
/* Add CONTENT-LANGUAGE header only if WEB_SERVER_CONTENT_LANGUAGE */
/* is not empty and if Accept-Language header is not empty */
struct SendInstruction *RespInstr;
RespInstr = (struct SendInstruction *)
va_arg( argp, struct SendInstruction *);
assert( RespInstr );
if (strcmp( RespInstr->AcceptLanguageHeader, "" ) &&
strcmp( WEB_SERVER_CONTENT_LANGUAGE, "" ) && http_MakeMessage(
buf, http_major_version, http_minor_version,
"ssc",
"CONTENT-LANGUAGE: ", WEB_SERVER_CONTENT_LANGUAGE ) != 0 ) {
goto error_handler;
}
} else if( c == 'C' ) {
if( ( http_major_version > 1 ) ||
( http_major_version == 1 && http_minor_version == 1 )
) {
/* connection header */
if( membuffer_append_str( buf, "CONNECTION: close\r\n" ) !=
0 ) {
goto error_handler;
}
}
} else if( c == 'N' ) {
/* content-length header */
bignum = ( off_t )va_arg( argp, off_t );
assert( bignum >= 0 );
if (http_MakeMessage(
buf, http_major_version, http_minor_version,
"shc",
"CONTENT-LENGTH: ", bignum ) != 0 ) {
goto error_handler;
}
} else if( c == 'S' || c == 'U' ) {
/* SERVER or USER-AGENT header */
temp_str = ( c == 'S' ) ? "SERVER: " : "USER-AGENT: ";
get_sdk_info( tempbuf );
if (http_MakeMessage(
buf, http_major_version, http_minor_version,
"ss",
temp_str, tempbuf ) != 0 ) {
goto error_handler;
}
} else if( c == 'X' ) {
/* C string */
s = ( char * )va_arg( argp, char * );
assert( s );
if( membuffer_append_str( buf, "X-User-Agent: ") != 0 ) {
goto error_handler;
}
if( membuffer_append( buf, s, strlen( s ) ) != 0 ) {
goto error_handler;
}
} else if( c == 'R' ) {
/* response start line */
/* e.g.: 'HTTP/1.1 200 OK' */
/* */
/* code */
status_code = ( int )va_arg( argp, int );
assert( status_code > 0 );
sprintf( tempbuf, "HTTP/%d.%d %d ",
http_major_version, http_minor_version, status_code );
/* str */
status_msg = http_get_code_text( status_code );
if (http_MakeMessage(
buf, http_major_version, http_minor_version,
"ssc",
tempbuf,
status_msg ) != 0 ) {
goto error_handler;
}
} else if( c == 'B' ) {
/* body of a simple reply */
/* */
status_code = ( int )va_arg( argp, int );
sprintf( tempbuf, "%s%d %s%s",
"<html><body><h1>",
status_code, http_get_code_text( status_code ),
"</h1></body></html>" );
bignum = strlen( tempbuf );
if (http_MakeMessage(
buf, http_major_version, http_minor_version,
"NTcs",
bignum, /* content-length */
"text/html", /* content-type */
tempbuf ) != 0 /* body */
) {
goto error_handler;
}
} else if( c == 'Q' ) {
/* request start line */
/* GET /foo/bar.html HTTP/1.1\r\n */
method = ( http_method_t ) va_arg( argp, http_method_t );
method_str = method_to_str( method );
url_str = ( const char * )va_arg( argp, const char * );
num = ( size_t )va_arg( argp, size_t ); /* length of url_str */
if (http_MakeMessage(
buf, http_major_version, http_minor_version,
"ssbsdsdc",
method_str, /* method */
" ", url_str, num, /* url */
" HTTP/", http_major_version, ".", http_minor_version ) != 0 ) {
goto error_handler;
}
} else if( c == 'q' ) {
/* request start line and HOST header */
method = ( http_method_t ) va_arg( argp, http_method_t );
uri_ptr = ( uri_type * ) va_arg( argp, uri_type * );
assert( uri_ptr );
if( http_FixUrl( uri_ptr, &url ) != 0 ) {
error_code = UPNP_E_INVALID_URL;
goto error_handler;
}
if (http_MakeMessage(
buf, http_major_version, http_minor_version,
"Q" "sbc",
method, url.pathquery.buff, url.pathquery.size,
"HOST: ", url.hostport.text.buff, url.hostport.text.size ) != 0 ) {
goto error_handler;
}
} else if( c == 'T' ) {
/* content type header */
temp_str = ( const char * )va_arg( argp, const char * ); /* type/subtype format */
if (http_MakeMessage(
buf, http_major_version, http_minor_version,
"ssc",
"CONTENT-TYPE: ", temp_str ) != 0 ) {
goto error_handler;
}
} else {
assert( 0 );
}
}
return 0;
error_handler:
va_end( argp );
membuffer_destroy( buf );
return error_code;
}
/************************************************************************
* Function: http_CalcResponseVersion
*
* Parameters:
* IN int request_major_vers; Request major version
* IN int request_minor_vers; Request minor version
* OUT int* response_major_vers; Response mojor version
* OUT int* response_minor_vers; Response minor version
*
* Description:
* Calculate HTTP response versions based on the request versions.
*
* Return: void
************************************************************************/
void
http_CalcResponseVersion( IN int request_major_vers,
IN int request_minor_vers,
OUT int *response_major_vers,
OUT int *response_minor_vers )
{
if( ( request_major_vers > 1 ) ||
( request_major_vers == 1 && request_minor_vers >= 1 )
) {
*response_major_vers = 1;
*response_minor_vers = 1;
} else {
*response_major_vers = request_major_vers;
*response_minor_vers = request_minor_vers;
}
}
/************************************************************************
* Function: MakeGetMessageEx
*
* Parameters:
* const char *url_str; String as a URL
* membuffer *request; Buffer containing the request
* uri_type *url; URI object containing the scheme, path
* query token, etc.
*
* Description:
* Makes the message for the HTTP GET method
*
* Returns:
* UPNP_E_INVALID_URL
* Error Codes returned by http_MakeMessage
* UPNP_E_SUCCESS
************************************************************************/
int
MakeGetMessageEx( const char *url_str,
membuffer * request,
uri_type * url,
struct SendInstruction *pRangeSpecifier )
{
int errCode = UPNP_E_SUCCESS;
char *urlPath = NULL;
size_t hostlen = 0;
char *hoststr,
*temp;
do {
UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
"DOWNLOAD URL : %s\n", url_str );
if( ( errCode = http_FixStrUrl( ( char * )url_str,
strlen( url_str ), url ) ) != UPNP_E_SUCCESS ) {
break;
}
/* make msg */
membuffer_init( request );
urlPath = alloca( strlen( url_str ) + 1 );
if( !urlPath ) {
errCode = UPNP_E_OUTOF_MEMORY;
break;
}
memset( urlPath, 0, strlen( url_str ) + 1 );
strcpy( urlPath, url_str );
hoststr = strstr( urlPath, "//" );
if( hoststr == NULL ) {
errCode = UPNP_E_INVALID_URL;
break;
}
hoststr += 2;
temp = strchr( hoststr, '/' );
if( temp == NULL ) {
errCode = UPNP_E_INVALID_URL;
break;
}
*temp = '\0';
hostlen = strlen( hoststr );
*temp = '/';
UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
"HOSTNAME : %s Length : %"PRIzu"\n",
hoststr, hostlen );
errCode = http_MakeMessage(
request, 1, 1,
"Q" "s" "bc" "GDCUc",
HTTPMETHOD_GET, url->pathquery.buff, url->pathquery.size,
"HOST: ",
hoststr, hostlen,
pRangeSpecifier );
if( errCode != 0 ) {
UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
"HTTP Makemessage failed\n" );
membuffer_destroy( request );
return errCode;
}
} while( 0 );
UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
"HTTP Buffer:\n%s\n" "----------END--------\n",
request->buf);
return errCode;
}
#define SIZE_RANGE_BUFFER 50
/************************************************************************
* Function: http_OpenHttpGetEx
*
* Parameters:
* IN const char *url_str; String as a URL
* IN OUT void **Handle; Pointer to buffer to store HTTP
* post handle
* IN OUT char **contentType; Type of content
* OUT int *contentLength; length of content
* OUT int *httpStatus; HTTP status returned on receiving a
* response message
* IN int timeout; time out value
*
* Description:
* Makes the HTTP GET message, connects to the peer,
* sends the HTTP GET request, gets the response and parses the
* response.
*
* Return: int
* UPNP_E_SUCCESS - On Success
* UPNP_E_INVALID_PARAM - Invalid Paramters
* UPNP_E_OUTOF_MEMORY
* UPNP_E_SOCKET_ERROR
* UPNP_E_BAD_RESPONSE
************************************************************************/
int http_OpenHttpGetEx(
IN const char *url_str,
IN OUT void **Handle,
IN OUT char **contentType,
OUT int *contentLength,
OUT int *httpStatus,
IN int lowRange,
IN int highRange,
IN int timeout)
{
int http_error_code;
memptr ctype;
SOCKET tcp_connection;
int sockaddr_len;
membuffer request;
http_get_handle_t *handle = NULL;
uri_type url;
parse_status_t status;
int errCode = UPNP_E_SUCCESS;
/* char rangeBuf[SIZE_RANGE_BUFFER]; */
struct SendInstruction rangeBuf;
do {
/* Checking Input parameters */
if (!url_str || !Handle || !contentType || !httpStatus ) {
errCode = UPNP_E_INVALID_PARAM;
break;
}
/* Initialize output parameters */
*httpStatus = 0;
*Handle = handle;
*contentType = NULL;
*contentLength = 0;
if (lowRange > highRange) {
errCode = UPNP_E_INTERNAL_ERROR;
break;
}
memset(&rangeBuf, 0, sizeof(rangeBuf));
sprintf(rangeBuf.RangeHeader,
"Range: bytes=%d-%d\r\n", lowRange, highRange);
membuffer_init(&request);
errCode = MakeGetMessageEx(url_str, &request, &url, &rangeBuf);
if (errCode != UPNP_E_SUCCESS) {
break;
}
handle = (http_get_handle_t *)malloc(sizeof(http_get_handle_t));
if (!handle) {
errCode = UPNP_E_OUTOF_MEMORY;
break;
}
memset(handle, 0, sizeof(*handle));
parser_response_init(&handle->response, HTTPMETHOD_GET);
tcp_connection = socket(url.hostport.IPaddress.ss_family, SOCK_STREAM, 0);
if (tcp_connection == -1) {
errCode = UPNP_E_SOCKET_ERROR;
free(handle);
break;
}
if (sock_init(&handle->sock_info, tcp_connection) != UPNP_E_SUCCESS) {
sock_destroy(&handle->sock_info, SD_BOTH);
errCode = UPNP_E_SOCKET_ERROR;
free(handle);
break;
}
sockaddr_len = url.hostport.IPaddress.ss_family == AF_INET6 ?
sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
errCode = private_connect(handle->sock_info.socket,
(struct sockaddr *)&(url.hostport.IPaddress), sockaddr_len);
if (errCode == -1) {
sock_destroy(&handle->sock_info, SD_BOTH);
errCode = UPNP_E_SOCKET_CONNECT;
free(handle);
break;
}
/* send request */
errCode = http_SendMessage(&handle->sock_info, &timeout,
"b", request.buf, request.length);
if (errCode != UPNP_E_SUCCESS) {
sock_destroy(&handle->sock_info, SD_BOTH);
free(handle);
break;
}
status = ReadResponseLineAndHeaders(&handle->sock_info,
&handle->response, &timeout, &http_error_code);
if (status != PARSE_OK) {
errCode = UPNP_E_BAD_RESPONSE;
free(handle);
break;
}
status = parser_get_entity_read_method(&handle->response);
if (status != PARSE_CONTINUE_1 && status != PARSE_SUCCESS) {
errCode = UPNP_E_BAD_RESPONSE;
free(handle);
break;
}
*httpStatus = handle->response.msg.status_code;
errCode = UPNP_E_SUCCESS;
if (!httpmsg_find_hdr(&handle->response.msg, HDR_CONTENT_TYPE, &ctype)) {
/* no content-type */
*contentType = NULL;
} else {
*contentType = ctype.buf;
}
if (handle->response.position == POS_COMPLETE) {
*contentLength = 0;
} else if(handle->response.ent_position == ENTREAD_USING_CHUNKED) {
*contentLength = UPNP_USING_CHUNKED;
} else if(handle->response.ent_position == ENTREAD_USING_CLEN) {
*contentLength = handle->response.content_length;
} else if(handle->response.ent_position == ENTREAD_UNTIL_CLOSE) {
*contentLength = UPNP_UNTIL_CLOSE;
}
*Handle = handle;
} while (0);
membuffer_destroy(&request);
return errCode;
}
/************************************************************************
* Function: get_sdk_info
*
* Parameters:
* OUT char *info; buffer to store the operating system information
*
* Description:
* Returns the server information for the operating system
*
* Return:
* UPNP_INLINE void
************************************************************************/
/* 'info' should have a size of at least 100 bytes */
void
get_sdk_info( OUT char *info )
{
#ifdef WIN32
OSVERSIONINFO versioninfo;
versioninfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
if (GetVersionEx(&versioninfo)!=0) {
sprintf( info,
"%d.%d.%d %d/%s, UPnP/1.0, Portable SDK for UPnP devices/"PACKAGE_VERSION"\r\n",
versioninfo.dwMajorVersion,
versioninfo.dwMinorVersion,
versioninfo.dwBuildNumber,
versioninfo.dwPlatformId,
versioninfo.szCSDVersion );
} else {
*info = '\0';
}
#else
int ret_code;
struct utsname sys_info;
ret_code = uname( &sys_info );
if( ret_code == -1 ) {
*info = '\0';
}
sprintf( info,
"%s/%s, UPnP/1.0, Portable SDK for UPnP devices/"PACKAGE_VERSION "\r\n",
sys_info.sysname,
sys_info.release );
#endif
}