1///////////////////////////////////////////////////////////////////////////
2//
3// Copyright (c) 2000-2003 Intel Corporation
4// All rights reserved.
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions are met:
8//
9// * Redistributions of source code must retain the above copyright notice,
10// this list of conditions and the following disclaimer.
11// * Redistributions in binary form must reproduce the above copyright notice,
12// this list of conditions and the following disclaimer in the documentation
13// and/or other materials provided with the distribution.
14// * Neither name of Intel Corporation nor the names of its contributors
15// may be used to endorse or promote products derived from this software
16// without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
22// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
26// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29//
30///////////////////////////////////////////////////////////////////////////
31
32#ifdef INCLUDE_CLIENT_APIS
33#if EXCLUDE_SOAP == 0
34
35#include <assert.h>
36#include <stdlib.h>
37#include <ctype.h>
38#include <stdio.h>
39#include <stdarg.h>
40
41#include "config.h"
42#include "miniserver.h"
43#include "membuffer.h"
44#include "httpparser.h"
45#include "httpreadwrite.h"
46#include "statcodes.h"
47#include "parsetools.h"
48#include "upnpapi.h"
49#include "soaplib.h"
50#include "uri.h"
51#include "upnp.h"
52
53#include "unixutil.h"
54
55#define SOAP_ACTION_RESP	1
56#define SOAP_VAR_RESP		2
57//#define SOAP_ERROR_RESP       3
58#define SOAP_ACTION_RESP_ERROR  3
59#define SOAP_VAR_RESP_ERROR   4
60
61/****************************************************************************
62*	Function :	dom_cmp_name
63*
64*	Parameters :
65*			IN char *name :	lookup name
66*			IN IXML_Node *node : xml node
67*
68*	Description : This function compares 'name' and node's name
69*
70*	Return : int
71*		0 if both are equal; 1 if not equal, and UPNP_E_OUTOF_MEMORY
72*
73*	Note :
74****************************************************************************/
75static int
76dom_cmp_name( IN char *name,
77              IN IXML_Node * node )
78{
79    const DOMString node_name = NULL;
80    memptr nameptr,
81      dummy;
82    int ret_code;
83
84    assert( name );
85    assert( node );
86
87    node_name = ixmlNode_getNodeName( node );
88    if( node_name == NULL ) {
89        return UPNP_E_OUTOF_MEMORY;
90    }
91
92    if( strcmp( name, node_name ) == 0 ) {
93        ret_code = 0;
94    } else if( matchstr( ( char * )node_name, strlen( node_name ),
95                         "%s:%s%0", &dummy, &nameptr ) == PARSE_OK &&
96               strcmp( nameptr.buf, name ) == 0 ) {
97        ret_code = 0;
98    } else {
99        ret_code = 1;           // names are not the same
100    }
101
102    return ret_code;
103}
104
105/****************************************************************************
106*	Function :	dom_find_node
107*
108*	Parameters :
109*			IN char* node_name : name of the node
110*			IN IXML_Node *start_node :	complete xml node
111*			OUT IXML_Node ** matching_node : matched node
112*
113*	Description :	This function goes thru each child of 'start_node'
114*		looking for a node having the name 'node_name'.
115*
116*	Return : int
117*		return UPNP_E_SUCCESS if successful else returns appropriate error
118*
119*	Note :
120****************************************************************************/
121static int
122dom_find_node( IN char *node_name,
123               IN IXML_Node * start_node,
124               OUT IXML_Node ** matching_node )
125{
126    IXML_Node *node;
127
128    // invalid args
129    if( node_name == NULL || start_node == NULL ) {
130        return UPNP_E_NOT_FOUND;
131    }
132
133    node = ixmlNode_getFirstChild( start_node );
134    while( node != NULL ) {
135        // match name
136        if( dom_cmp_name( node_name, node ) == 0 ) {
137            *matching_node = node;
138            return UPNP_E_SUCCESS;
139        }
140        // free and next node
141        node = ixmlNode_getNextSibling( node ); // next node
142    }
143
144    return UPNP_E_NOT_FOUND;
145}
146
147/****************************************************************************
148*	Function :	dom_find_deep_node
149*
150*	Parameters :
151*			IN char* names[] : array of names
152*			IN int num_names :	size of array
153*			IN IXML_Node *start_node : Node from where it should should be
154*										searched
155*			OUT IXML_Node ** matching_node : Node that matches the last name
156*											of the array
157*
158*	Description :	This function searches for the node specifed by the last
159*		name in the 'name' array.
160*
161*	Return : int
162*		return UPNP_E_SUCCESS if successful else returns appropriate error
163*	Note :
164****************************************************************************/
165static int
166dom_find_deep_node( IN char *names[],
167                    IN int num_names,
168                    IN IXML_Node * start_node,
169                    OUT IXML_Node ** matching_node )
170{
171    int i;
172    IXML_Node *node;
173    IXML_Node *match_node;
174
175    assert( num_names > 0 );
176
177    node = start_node;
178    if( dom_cmp_name( names[0], start_node ) == 0 ) {
179        if( num_names == 1 ) {
180            *matching_node = start_node;
181            return UPNP_E_SUCCESS;
182        }
183    }
184
185    for( i = 1; i < num_names; i++ ) {
186        if( dom_find_node( names[i], node, &match_node ) !=
187            UPNP_E_SUCCESS ) {
188            return UPNP_E_NOT_FOUND;
189        }
190
191        if( i == num_names - 1 ) {
192            *matching_node = match_node;
193            return UPNP_E_SUCCESS;
194        }
195
196        node = match_node;      // try again
197    }
198
199    return UPNP_E_NOT_FOUND;    // this line not reached
200}
201
202/****************************************************************************
203*	Function :	get_node_value
204*
205*	Parameters :
206*			IN IXML_Node *node : input node
207*
208*	Description :	This function returns the value of the text node
209*
210*	Return : DOMString
211*		string containing the node value
212*
213*	Note :The given node must have a text node as its first child
214****************************************************************************/
215static DOMString
216get_node_value( IN IXML_Node * node )
217{
218    IXML_Node *text_node = NULL;
219    DOMString text_value = NULL;
220
221    text_node = ixmlNode_getFirstChild( node );
222    if( text_node == NULL ) {
223        return NULL;
224    }
225
226    text_value = ixmlNode_getNodeValue( text_node );
227    return text_value;
228}
229
230/****************************************************************************
231*	Function :	get_host_and_path
232*
233*	Parameters :
234*			IN char *ctrl_url :	URL
235*			OUT memptr *host :	host string
236*			OUT memptr *path :	path string
237*			OUT uri_type* url :	URL type
238*
239*	Description :	This function retrives the host and path from the
240*		control URL
241*
242*	Return : int
243*		returns 0 on sucess; -1 on error
244*
245*	Note :
246****************************************************************************/
247static XINLINE int
248get_host_and_path( IN char *ctrl_url,
249                   OUT memptr * host,
250                   OUT memptr * path,
251                   OUT uri_type * url )
252{
253    if( parse_uri( ctrl_url, strlen( ctrl_url ), url ) != HTTP_SUCCESS ) {
254        return -1;
255    }
256    host->buf = url->hostport.text.buff;
257    host->length = url->hostport.text.size;
258
259    path->buf = url->pathquery.buff;
260    path->length = url->pathquery.size;
261
262    return 0;
263}
264
265/****************************************************************************
266*	Function :	get_action_name
267*
268*	Parameters :
269*			IN char* action :	string containing action name
270*			OUT memptr* name : name of the action
271*
272*	Description :	This functions retirves the action name in the buffer
273*
274*	Return : int
275*		returns 0 on success; -1 on error
276*
277*	Note :
278****************************************************************************/
279static XINLINE int
280get_action_name( IN char *action,
281                 OUT memptr * name )
282{
283    memptr dummy;
284    int ret_code;
285
286    ret_code =
287        matchstr( action, strlen( action ), " <%s:%s", &dummy, name );
288
289    return ret_code == PARSE_OK ? 0 : -1;
290}
291
292/****************************************************************************
293*	Function :	add_man_header
294*
295*	Parameters :
296*			INOUT membuffer* headers :	HTTP header
297*
298*	Description :	This function adds "MAN" field in the HTTP header
299*
300*	Return : int
301*		returns 0 on success; UPNP_E_OUTOFMEMORY on error
302*
303*	Note :
304****************************************************************************/
305static XINLINE int
306add_man_header( INOUT membuffer * headers )
307{
308    char *soap_action_hdr;
309    char *man_hdr = "MAN: \"http://schemas.xmlsoap.org/soap/envelope/\"; "
310        "ns=01\r\n01-";
311
312    // change POST to M-POST
313    if( membuffer_insert( headers, "M-", 2, 0 ) != 0 ) {
314        return UPNP_E_OUTOF_MEMORY;
315    }
316
317    soap_action_hdr = strstr( headers->buf, "SOAPACTION:" );
318    assert( soap_action_hdr != NULL );  // can't fail
319
320    // insert MAN header
321    if( membuffer_insert( headers, man_hdr, strlen( man_hdr ),
322                          soap_action_hdr - headers->buf ) != 0 ) {
323        return UPNP_E_OUTOF_MEMORY;
324    }
325
326    return 0;
327}
328
329/****************************************************************************
330*	Function :	soap_request_and_response
331*
332*	Parameters :
333*		IN membuffer* request :	request that will be sent to the device
334*		IN uri_type* destination_url :	destination address string
335*		OUT http_parser_t *response :	response from the device
336*
337*	Description :	This function sends the control point's request to the
338*		device and receives a response from it.
339*
340*	Return : int
341*
342*	Note :
343****************************************************************************/
344static int
345soap_request_and_response( IN membuffer * request,
346                           IN uri_type * destination_url,
347                           OUT http_parser_t * response )
348{
349    int ret_code;
350
351    ret_code = http_RequestAndResponse( destination_url, request->buf,
352                                        request->length,
353                                        SOAPMETHOD_POST,
354                                        UPNP_TIMEOUT, response );
355    if( ret_code != 0 ) {
356        httpmsg_destroy( &response->msg );
357        return ret_code;
358    }
359    // method-not-allowed error
360    if( response->msg.status_code == HTTP_METHOD_NOT_ALLOWED ) {
361        ret_code = add_man_header( request );   // change to M-POST msg
362        if( ret_code != 0 ) {
363            return ret_code;
364        }
365
366        httpmsg_destroy( &response->msg );  // about to reuse response
367
368        // try again
369        ret_code = http_RequestAndResponse( destination_url, request->buf,
370                                            HTTPMETHOD_MPOST,
371                                            request->length, UPNP_TIMEOUT,
372                                            response );
373        if( ret_code != 0 ) {
374            httpmsg_destroy( &response->msg );
375        }
376
377    }
378
379    return ret_code;
380}
381
382/****************************************************************************
383*	Function :	get_response_value
384*
385*	Parameters :
386*			IN http_message_t* hmsg :	HTTP response message
387*			IN int code :	return code in the HTTP response
388*			IN char*name :	name of the action
389*			OUT int *upnp_error_code :	UPnP error code
390*			OUT IXML_Node ** action_value :	SOAP response node
391*			OUT DOMString * str_value : state varible value ( in the case of
392*							querry state variable request)
393*
394*	Description :	This function handles the response coming back from the
395*		device. This function parses the response and gives back the SOAP
396*		response node.
397*
398*	Return : int
399*		return the type of the SOAP message if successful else returns
400*	appropriate error.
401*
402*	Note :
403****************************************************************************/
404static int
405get_response_value( IN http_message_t * hmsg,
406                    IN int code,
407                    IN char *name,
408                    OUT int *upnp_error_code,
409                    OUT IXML_Node ** action_value,
410                    OUT DOMString * str_value )
411{
412    IXML_Node *node = NULL;
413    IXML_Node *root_node = NULL;
414    IXML_Node *error_node = NULL;
415    IXML_Document *doc = NULL;
416    char *node_str = NULL;
417    char *temp_str = NULL;
418    DOMString error_node_str = NULL;
419    int err_code;
420    xboolean done = FALSE;
421    char *names[5];
422    DOMString nodeValue;
423
424    err_code = UPNP_E_BAD_RESPONSE; // default error
425
426    // only 200 and 500 status codes are relevant
427    if( ( hmsg->status_code != HTTP_OK &&
428          hmsg->status_code != HTTP_INTERNAL_SERVER_ERROR ) ||
429        !has_xml_content_type( hmsg ) ) {
430
431        goto error_handler;
432    }
433
434    if( ixmlParseBufferEx( hmsg->entity.buf, &doc ) != IXML_SUCCESS ) {
435
436        goto error_handler;
437    }
438
439    root_node = ixmlNode_getFirstChild( ( IXML_Node * ) doc );
440    if( root_node == NULL ) {
441
442        goto error_handler;
443    }
444
445    if( code == SOAP_ACTION_RESP ) {
446        //
447        // try reading soap action response
448        //
449        assert( action_value != NULL );
450
451        *action_value = NULL;
452
453        names[0] = "Envelope";
454        names[1] = "Body";
455        names[2] = name;
456        if( dom_find_deep_node( names, 3, root_node, &node ) ==
457            UPNP_E_SUCCESS ) {
458            node_str = ixmlPrintDocument( node );
459            if( node_str == NULL ) {
460                err_code = UPNP_E_OUTOF_MEMORY;
461                goto error_handler;
462            }
463
464            if( ixmlParseBufferEx( node_str,
465                                   ( IXML_Document ** ) action_value ) !=
466                IXML_SUCCESS ) {
467                err_code = UPNP_E_BAD_RESPONSE;
468                goto error_handler;
469            }
470
471            err_code = SOAP_ACTION_RESP;
472            done = TRUE;
473        }
474    } else if( code == SOAP_VAR_RESP ) {
475        // try reading var response
476        assert( str_value != NULL );
477        *str_value = NULL;
478
479        names[0] = "Envelope";
480        names[1] = "Body";
481        names[2] = "QueryStateVariableResponse";
482        names[3] = "return";
483        if( dom_find_deep_node( names, 4, root_node, &node )
484            == UPNP_E_SUCCESS ) {
485            nodeValue = get_node_value( node );
486            if( nodeValue == NULL ) {
487                goto error_handler;
488            }
489
490            *str_value = ixmlCloneDOMString( nodeValue );
491            err_code = SOAP_VAR_RESP;
492            done = TRUE;
493        }
494    }
495
496    if( !done ) {
497        // not action or var resp; read error code and description
498        *str_value = NULL;
499
500        names[0] = "Envelope";
501        names[1] = "Body";
502        names[2] = "Fault";
503        names[3] = "detail";
504        names[4] = "UPnPError";
505        if( dom_find_deep_node( names, 5, root_node, &error_node )
506            != UPNP_E_SUCCESS ) {
507            goto error_handler;
508        }
509
510        if( dom_find_node( "errorCode", error_node, &node )
511            != UPNP_E_SUCCESS ) {
512            goto error_handler;
513        }
514
515        temp_str = get_node_value( node );
516        if( temp_str == NULL ) {
517            goto error_handler;
518        }
519
520        *upnp_error_code = atoi( temp_str );
521        if( *upnp_error_code < 400 ) {
522            err_code = *upnp_error_code;
523            goto error_handler; // bad SOAP error code
524        }
525
526        if( code == SOAP_VAR_RESP ) {
527            if( dom_find_node( "errorDescription", error_node, &node )
528                != UPNP_E_SUCCESS ) {
529                goto error_handler;
530            }
531
532            nodeValue = get_node_value( node );
533            if( nodeValue == NULL ) {
534                goto error_handler;
535            }
536            *str_value = ixmlCloneDOMString( nodeValue );
537            if( *str_value == NULL ) {
538                goto error_handler;
539            }
540            err_code = SOAP_VAR_RESP_ERROR;
541        }
542
543        else if( code == SOAP_ACTION_RESP ) {
544            error_node_str = ixmlPrintDocument( error_node );
545            if( error_node_str == NULL ) {
546                err_code = UPNP_E_OUTOF_MEMORY;
547                goto error_handler;
548            }
549
550            if( ixmlParseBufferEx( error_node_str,
551                                   ( IXML_Document ** ) action_value ) !=
552                IXML_SUCCESS ) {
553                err_code = UPNP_E_BAD_RESPONSE;
554
555                goto error_handler;
556            }
557            err_code = SOAP_ACTION_RESP_ERROR;
558        }
559    }
560
561  error_handler:
562
563    ixmlDocument_free( doc );
564    ixmlFreeDOMString( node_str );
565    ixmlFreeDOMString( error_node_str );
566    return err_code;
567}
568
569/****************************************************************************
570*	Function :	SoapSendAction
571*
572*	Parameters :
573*		IN char* action_url :	device contrl URL
574*		IN char *service_type :	device service type
575*		IN IXML_Document *action_node : SOAP action node
576*		OUT IXML_Document **response_node :	SOAP response node
577*
578*	Description :	This function is called by UPnP API to send the SOAP
579*		action request and waits till it gets the response from the device
580*		pass the response to the API layer
581*
582*	Return :	int
583*		returns UPNP_E_SUCCESS if successful else returns appropriate error
584*	Note :
585****************************************************************************/
586int
587SoapSendAction( IN char *action_url,
588                IN char *service_type,
589                IN IXML_Document * action_node,
590                OUT IXML_Document ** response_node )
591{
592    char *action_str = NULL;
593    memptr name;
594    membuffer request;
595    membuffer responsename;
596    int err_code;
597    int ret_code;
598    http_parser_t response;
599    uri_type url;
600    int upnp_error_code;
601    char *upnp_error_str;
602    xboolean got_response = FALSE;
603
604    char *xml_start =
605        "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"\n"
606        "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
607        "<s:Body>";
608    char *xml_end = "</s:Body>\n" "</s:Envelope>\n";
609    int xml_start_len;
610    int xml_end_len;
611    int action_str_len;
612
613    *response_node = NULL;      // init
614
615    err_code = UPNP_E_OUTOF_MEMORY; // default error
616
617    DBGONLY( UpnpPrintf( UPNP_INFO, SOAP, __FILE__, __LINE__,
618                         "Inside SoapSendAction():" );
619         )
620        // init
621        membuffer_init( &request );
622    membuffer_init( &responsename );
623
624    // print action
625    action_str = ixmlPrintDocument( ( IXML_Node * ) action_node );
626    if( action_str == NULL ) {
627        goto error_handler;
628    }
629    // get action name
630    if( get_action_name( action_str, &name ) != 0 ) {
631        err_code = UPNP_E_INVALID_ACTION;
632        goto error_handler;
633    }
634    // parse url
635    if( http_FixStrUrl( action_url, strlen( action_url ), &url ) != 0 ) {
636        err_code = UPNP_E_INVALID_URL;
637        goto error_handler;
638    }
639
640    DBGONLY( UpnpPrintf( UPNP_INFO, SOAP, __FILE__, __LINE__,
641                         "path=%.*s, hostport=%.*s\n",
642                         url.pathquery.size, url.pathquery.buff,
643                         url.hostport.text.size,
644                         url.hostport.text.buff ); )
645
646        xml_start_len = strlen( xml_start );
647    xml_end_len = strlen( xml_end );
648    action_str_len = strlen( action_str );
649
650    // make request msg
651    request.size_inc = 50;
652    if( http_MakeMessage( &request, 1, 1, "q" "N" "s" "sssbs" "U" "c" "bbb", SOAPMETHOD_POST, &url, xml_start_len + action_str_len + xml_end_len,   // content-length
653                          ContentTypeHeader,
654                          "SOAPACTION: \"", service_type, "#", name.buf,
655                          name.length, "\"\r\n", xml_start, xml_start_len,
656                          action_str, action_str_len, xml_end,
657                          xml_end_len ) != 0 ) {
658        goto error_handler;
659    }
660
661    ret_code = soap_request_and_response( &request, &url, &response );
662    got_response = TRUE;
663    if( ret_code != UPNP_E_SUCCESS ) {
664        err_code = ret_code;
665        goto error_handler;
666    }
667
668    if( membuffer_append( &responsename, name.buf, name.length ) != 0 ||
669        membuffer_append_str( &responsename, "Response" ) != 0 ) {
670        goto error_handler;
671    }
672    // get action node from the response
673    ret_code = get_response_value( &response.msg, SOAP_ACTION_RESP,
674                                   responsename.buf, &upnp_error_code,
675                                   ( IXML_Node ** ) response_node,
676                                   &upnp_error_str );
677
678    if( ret_code == SOAP_ACTION_RESP ) {
679        err_code = UPNP_E_SUCCESS;
680    } else if( ret_code == SOAP_ACTION_RESP_ERROR ) {
681        err_code = upnp_error_code;
682    } else {
683        err_code = ret_code;
684    }
685
686  error_handler:
687    ixmlFreeDOMString( action_str );
688    membuffer_destroy( &request );
689    membuffer_destroy( &responsename );
690    if( got_response ) {
691        httpmsg_destroy( &response.msg );
692    }
693
694    return err_code;
695}
696
697/****************************************************************************
698*	Function :	SoapSendActionEx
699*
700*	Parameters :
701*		IN char* action_url :	device contrl URL
702*		IN char *service_type :	device service type
703		IN IXML_Document *Header: Soap header
704*		IN IXML_Document *action_node : SOAP action node ( SOAP body)
705*		OUT IXML_Document **response_node :	SOAP response node
706*
707*	Description :	This function is called by UPnP API to send the SOAP
708*		action request and waits till it gets the response from the device
709*		pass the response to the API layer. This action is similar to the
710*		the SoapSendAction with only difference that it allows users to
711*		pass the SOAP header along the SOAP body ( soap action request)
712*
713*	Return :	int
714*		returns UPNP_E_SUCCESS if successful else returns appropriate error
715*	Note :
716****************************************************************************/
717int
718SoapSendActionEx( IN char *action_url,
719                  IN char *service_type,
720                  IN IXML_Document * header,
721                  IN IXML_Document * action_node,
722                  OUT IXML_Document ** response_node )
723{
724    char *xml_header_str = NULL;
725    char *action_str = NULL;
726    memptr name;
727    membuffer request;
728    membuffer responsename;
729    int err_code;
730    int ret_code;
731    http_parser_t response;
732    uri_type url;
733    int upnp_error_code;
734    char *upnp_error_str;
735    xboolean got_response = FALSE;
736
737    char *xml_start =
738        "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"\n"
739        "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n";
740    char *xml_body_start = "<s:Body>";
741    char *xml_end = "</s:Body>\n" "</s:Envelope>\n";
742    int xml_start_len;
743    int xml_end_len;
744    char *xml_header_start = "<s:Header>\n";
745    char *xml_header_end = "</s:Header>\n";
746    int xml_header_start_len;
747    int xml_header_end_len;
748    int xml_header_str_len;
749    int action_str_len;
750    int xml_body_start_len;
751
752    *response_node = NULL;      // init
753
754    err_code = UPNP_E_OUTOF_MEMORY; // default error
755
756    DBGONLY( UpnpPrintf( UPNP_INFO, SOAP, __FILE__, __LINE__,
757                         "Inside SoapSendActionEx():" );
758         )
759        // init
760        membuffer_init( &request );
761    membuffer_init( &responsename );
762
763    // header string
764    xml_header_str = ixmlPrintDocument( ( IXML_Node * ) header );
765    if( xml_header_str == NULL ) {
766        goto error_handler;
767    }
768    // print action
769    action_str = ixmlPrintDocument( ( IXML_Node * ) action_node );
770    if( action_str == NULL ) {
771        goto error_handler;
772    }
773    // get action name
774    if( get_action_name( action_str, &name ) != 0 ) {
775        err_code = UPNP_E_INVALID_ACTION;
776        goto error_handler;
777    }
778    // parse url
779    if( http_FixStrUrl( action_url, strlen( action_url ), &url ) != 0 ) {
780        err_code = UPNP_E_INVALID_URL;
781        goto error_handler;
782    }
783
784    DBGONLY( UpnpPrintf( UPNP_INFO, SOAP, __FILE__, __LINE__,
785                         "path=%.*s, hostport=%.*s\n",
786                         url.pathquery.size, url.pathquery.buff,
787                         url.hostport.text.size,
788                         url.hostport.text.buff ); )
789
790        xml_start_len = strlen( xml_start );
791    xml_body_start_len = strlen( xml_body_start );
792    xml_end_len = strlen( xml_end );
793    action_str_len = strlen( action_str );
794
795    xml_header_start_len = strlen( xml_header_start );
796    xml_header_end_len = strlen( xml_header_end );
797    xml_header_str_len = strlen( xml_header_str );
798
799    // make request msg
800    request.size_inc = 50;
801    if( http_MakeMessage( &request, 1, 1, "q" "N" "s" "sssbs" "U" "c" "bbbbbbb", SOAPMETHOD_POST, &url, 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, // content-length
802                          ContentTypeHeader,
803                          "SOAPACTION: \"", service_type, "#", name.buf,
804                          name.length, "\"\r\n",
805                          xml_start, xml_start_len,
806                          xml_header_start, xml_header_start_len,
807                          xml_header_str, xml_header_str_len,
808                          xml_header_end, xml_header_end_len,
809                          xml_body_start, xml_body_start_len,
810                          action_str, action_str_len,
811                          xml_end, xml_end_len ) != 0 ) {
812        goto error_handler;
813    }
814
815    ret_code = soap_request_and_response( &request, &url, &response );
816    got_response = TRUE;
817    if( ret_code != UPNP_E_SUCCESS ) {
818        err_code = ret_code;
819        goto error_handler;
820    }
821
822    if( membuffer_append( &responsename, name.buf, name.length ) != 0 ||
823        membuffer_append_str( &responsename, "Response" ) != 0 ) {
824        goto error_handler;
825    }
826    // get action node from the response
827    ret_code = get_response_value( &response.msg, SOAP_ACTION_RESP,
828                                   responsename.buf, &upnp_error_code,
829                                   ( IXML_Node ** ) response_node,
830                                   &upnp_error_str );
831
832    if( ret_code == SOAP_ACTION_RESP ) {
833        err_code = UPNP_E_SUCCESS;
834    } else if( ret_code == SOAP_ACTION_RESP_ERROR ) {
835        err_code = upnp_error_code;
836    } else {
837        err_code = ret_code;
838    }
839
840  error_handler:
841
842    ixmlFreeDOMString( action_str );
843    ixmlFreeDOMString( xml_header_str );
844    membuffer_destroy( &request );
845    membuffer_destroy( &responsename );
846    if( got_response ) {
847        httpmsg_destroy( &response.msg );
848    }
849
850    return err_code;
851}
852
853/****************************************************************************
854*	Function :	SoapGetServiceVarStatus
855*
856*	Parameters :
857*			IN  char * action_url :	Address to send this variable
858*									query message.
859*			IN  char *var_name : Name of the variable.
860*			OUT char **var_value :	Output value.
861*
862*	Description :	This function creates a status variable query message
863*		send it to the specified URL. It also collect the response.
864*
865*	Return :	int
866*
867*	Note :
868****************************************************************************/
869int
870SoapGetServiceVarStatus( IN char *action_url,
871                         IN char *var_name,
872                         OUT char **var_value )
873{
874    memptr host;                // value for HOST header
875    memptr path;                // ctrl path in first line in msg
876    uri_type url;
877    membuffer request;
878    int ret_code;
879    http_parser_t response;
880    int upnp_error_code;
881
882    char *xml_start =
883        "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"\n"
884        "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
885        "<s:Body>\n"
886        "<u:QueryStateVariable xmlns:u=\"urn:schemas-upnp-org:control-1-0\">\n"
887        "<u:varName>";
888
889    char *xml_end = "</u:varName>\n"
890        "</u:QueryStateVariable>\n" "</s:Body>\n" "</s:Envelope>\n";
891
892    *var_value = NULL;          // return NULL in case of an error
893
894    membuffer_init( &request );
895
896    // get host hdr and url path
897    if( get_host_and_path( action_url, &host, &path, &url ) == -1 ) {
898        return UPNP_E_INVALID_URL;
899    }
900    // make headers
901    request.size_inc = 50;
902    if( http_MakeMessage( &request, 1, 1, "Q" "sbc" "N" "s" "s" "U" "c" "sss", SOAPMETHOD_POST, path.buf, path.length, "HOST: ", host.buf, host.length, strlen( xml_start ) + strlen( var_name ) + strlen( xml_end ),   // content-length
903                          ContentTypeHeader,
904                          "SOAPACTION: \"urn:schemas"
905                          "-upnp-org:control-1-0#QueryStateVariable\"\r\n",
906                          xml_start, var_name, xml_end ) != 0 ) {
907        return UPNP_E_OUTOF_MEMORY;
908    }
909    // send msg and get reply
910    ret_code = soap_request_and_response( &request, &url, &response );
911    membuffer_destroy( &request );
912    if( ret_code != UPNP_E_SUCCESS ) {
913        return ret_code;
914    }
915    // get variable value from the response
916    ret_code = get_response_value( &response.msg, SOAP_VAR_RESP, NULL,
917                                   &upnp_error_code, NULL, var_value );
918
919    httpmsg_destroy( &response.msg );
920
921    if( ret_code == SOAP_VAR_RESP ) {
922        return UPNP_E_SUCCESS;
923    } else if( ret_code == SOAP_VAR_RESP_ERROR ) {
924        return upnp_error_code;
925    } else {
926        return ret_code;
927    }
928}
929
930#endif // EXCLUDE_SOAP
931#endif // INCLUDE_CLIENT_APIS
932