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_DEVICE_APIS
33#if EXCLUDE_SOAP == 0
34
35#define SOAP_BODY "Body"
36#define SOAP_URN "http://schemas.xmlsoap.org/soap/envelope/"
37
38#define QUERY_STATE_VAR_URN "urn:schemas-upnp-org:control-1-0"
39
40#include "config.h"
41#include "upnpapi.h"
42#include "parsetools.h"
43#include "statcodes.h"
44#include "httpparser.h"
45#include "httpreadwrite.h"
46#include "unixutil.h"
47
48// timeout duration in secs for transmission/reception
49#define SOAP_TIMEOUT UPNP_TIMEOUT
50
51#define SREQ_HDR_NOT_FOUND	 -1
52#define SREQ_BAD_HDR_FORMAT	 -2
53
54#define SOAP_INVALID_ACTION 401
55#define SOAP_INVALID_ARGS	402
56#define SOAP_OUT_OF_SYNC	403
57#define SOAP_INVALID_VAR	404
58#define SOAP_ACTION_FAILED	501
59
60static const char *Soap_Invalid_Action = "Invalid Action";
61
62//static const char* Soap_Invalid_Args = "Invalid Args";
63static const char *Soap_Action_Failed = "Action Failed";
64static const char *Soap_Invalid_Var = "Invalid Var";
65
66const char *ContentTypeHeader =
67    "CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n";
68
69/****************************************************************************
70*	Function :	get_request_type
71*
72*	Parameters :
73*			IN http_message_t* request :	HTTP request
74*			OUT memptr* action_name :	SOAP action name
75*
76*	Description :	This function retrives the name of the SOAP action
77*
78*	Return : int
79*		0 if successful else returns appropriate error.
80*	Note :
81****************************************************************************/
82static XINLINE int
83get_request_type( IN http_message_t * request,
84                  OUT memptr * action_name )
85{
86    memptr value;
87    memptr ns_value,
88      dummy_quote;
89    http_header_t *hdr;
90    char save_char;
91    char *s;
92    membuffer soap_action_name;
93
94    // find soapaction header
95    //
96    if( request->method == SOAPMETHOD_POST ) {
97        if( httpmsg_find_hdr( request, HDR_SOAPACTION, &value )
98            == NULL ) {
99            return SREQ_HDR_NOT_FOUND;
100        }
101    } else                      // M-POST
102    {
103        // get NS value from MAN header
104        hdr = httpmsg_find_hdr( request, HDR_MAN, &value );
105        if( hdr == NULL ) {
106            return SREQ_HDR_NOT_FOUND;
107        }
108
109        if( matchstr( value.buf, value.length, "%q%i ; ns = %s",
110                      &dummy_quote, &ns_value ) != 0 ) {
111            return SREQ_BAD_HDR_FORMAT;
112        }
113        // create soapaction name header
114        membuffer_init( &soap_action_name );
115        if( ( membuffer_assign( &soap_action_name,
116                                ns_value.buf, ns_value.length )
117              == UPNP_E_OUTOF_MEMORY ) ||
118            ( membuffer_append_str( &soap_action_name,
119                                    "-SOAPACTION" ) ==
120              UPNP_E_OUTOF_MEMORY )
121             ) {
122            membuffer_destroy( &soap_action_name );
123            return UPNP_E_OUTOF_MEMORY;
124        }
125
126        hdr = httpmsg_find_hdr_str( request, soap_action_name.buf );
127        membuffer_destroy( &soap_action_name );
128        if( hdr == NULL ) {
129            return SREQ_HDR_NOT_FOUND;
130        }
131
132        value.buf = hdr->value.buf;
133        value.length = hdr->value.length;
134    }
135
136    // determine type
137    //
138    save_char = value.buf[value.length];
139    value.buf[value.length] = '\0';
140
141    s = strchr( value.buf, '#' );
142    if( s == NULL ) {
143        value.buf[value.length] = save_char;
144        return SREQ_BAD_HDR_FORMAT;
145    }
146
147    s++;                        // move to value
148
149    if( matchstr( s, value.length - ( s - value.buf ), "%s",
150                  action_name ) != PARSE_OK ) {
151        value.buf[value.length] = save_char;
152        return SREQ_BAD_HDR_FORMAT;
153    }
154    // action name or variable ?
155    if( memptr_cmp( action_name, "QueryStateVariable" ) == 0 ) {
156        // query variable
157        action_name->buf = NULL;
158        action_name->length = 0;
159    }
160
161    value.buf[value.length] = save_char;    // restore
162    return 0;
163}
164
165/****************************************************************************
166*	Function :	send_error_response
167*
168*	Parameters :
169*			IN SOCKINFO *info :	socket info
170*			IN int error_code :	error code
171*			IN const char* err_msg :	error message
172*			IN http_message_t* hmsg :	HTTP request
173*
174*	Description :	This function sends SOAP error response
175*
176*	Return : void
177*
178*	Note :
179****************************************************************************/
180static void
181send_error_response( IN SOCKINFO * info,
182                     IN int error_code,
183                     IN const char *err_msg,
184                     IN http_message_t * hmsg )
185{
186    int content_length;
187    int timeout_secs = SOAP_TIMEOUT;
188    int major,
189      minor;
190    const char *start_body =
191        "<s:Envelope\n"
192        "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"\n"
193        "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
194        "<s:Body>\n"
195        "<s:Fault>\n"
196        "<faultcode>s:Client</faultcode>\n"
197        "<faultstring>UPnPError</faultstring>\n"
198        "<detail>\n"
199        "<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\">\n"
200        "<errorCode>";
201
202    const char *mid_body = "</errorCode>\n" "<errorDescription>";
203
204    const char *end_body =
205        "</errorDescription>\n"
206        "</UPnPError>\n"
207        "</detail>\n" "</s:Fault>\n" "</s:Body>\n" "</s:Envelope>\n";
208
209    char err_code_str[30];
210
211    membuffer headers;
212
213    sprintf( err_code_str, "%d", error_code );
214
215    // calc body len
216    content_length = strlen( start_body ) + strlen( err_code_str ) +
217        strlen( mid_body ) + strlen( err_msg ) + strlen( end_body );
218
219    http_CalcResponseVersion( hmsg->major_version, hmsg->minor_version,
220                              &major, &minor );
221
222    // make headers
223    membuffer_init( &headers );
224    if( http_MakeMessage( &headers, major, minor,
225                          "RNsDsSc" "sssss",
226                          500,
227                          content_length,
228                          ContentTypeHeader,
229                          "EXT:\r\n",
230                          start_body, err_code_str, mid_body, err_msg,
231                          end_body ) != 0 ) {
232        membuffer_destroy( &headers );
233        return;                 // out of mem
234    }
235    // send err msg
236    http_SendMessage( info, &timeout_secs, "b",
237                      headers.buf, headers.length );
238
239    membuffer_destroy( &headers );
240}
241
242/****************************************************************************
243*	Function :	send_var_query_response
244*
245*	Parameters :
246*			IN SOCKINFO *info :	socket info
247*			IN const char* var_value :	value of the state variable
248*			IN http_message_t* hmsg :	HTTP request
249*
250*	Description :	This function sends response of get var status
251*
252*	Return : void
253*
254*	Note :
255****************************************************************************/
256static XINLINE void
257send_var_query_response( IN SOCKINFO * info,
258                         IN const char *var_value,
259                         IN http_message_t * hmsg )
260{
261    int content_length;
262    int timeout_secs = SOAP_TIMEOUT;
263    int major,
264      minor;
265    const char *start_body =
266        "<s:Envelope\n"
267        "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"\n"
268        "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
269        "<s:Body>\n"
270        "<u:QueryStateVariableResponse "
271        "xmlns:u=\"urn:schemas-upnp-org:control-1-0\">\n" "<return>";
272
273    const char *end_body =
274        "</return>\n"
275        "</u:QueryStateVariableResponse>\n"
276        "</s:Body>\n" "</s:Envelope>\n";
277
278    membuffer response;
279
280    http_CalcResponseVersion( hmsg->major_version, hmsg->minor_version,
281                              &major, &minor );
282
283    content_length = strlen( start_body ) + strlen( var_value ) +
284        strlen( end_body );
285
286    // make headers
287    membuffer_init( &response );
288    if( http_MakeMessage( &response, major, minor,
289                          "RNsDsSc" "sss",
290                          HTTP_OK,
291                          content_length,
292                          ContentTypeHeader,
293                          "EXT:\r\n",
294                          start_body, var_value, end_body ) != 0 ) {
295        membuffer_destroy( &response );
296        return;                 // out of mem
297    }
298    // send msg
299    http_SendMessage( info, &timeout_secs, "b",
300                      response.buf, response.length );
301
302    membuffer_destroy( &response );
303}
304
305/****************************************************************************
306*	Function :	get_action_node
307*
308*	Parameters :
309*		IN IXML_Document *TempDoc :	The root DOM node.
310*		IN char *NodeName :	IXML_Node name to be searched.
311*		OUT IXML_Document ** RespNode :	Response/Output node.
312*
313*	Description :	This function separates the action node from
314*	the root DOM node.
315*
316*	Return :	static XINLINE int
317*		0 if successful, or -1 if fails.
318*
319*	Note :
320****************************************************************************/
321static XINLINE int
322get_action_node( IN IXML_Document * TempDoc,
323                 IN char *NodeName,
324                 OUT IXML_Document ** RespNode )
325{
326    IXML_Node *EnvpNode = NULL;
327    IXML_Node *BodyNode = NULL;
328    IXML_Node *ActNode = NULL;
329    DOMString ActNodeName = NULL;
330    const DOMString nodeName;
331    int ret_code = -1;          // error, by default
332    IXML_NodeList *nl = NULL;
333
334    DBGONLY( UpnpPrintf( UPNP_INFO, SOAP, __FILE__, __LINE__,
335                         "get_action_node(): node name =%s\n ", NodeName );
336         )
337
338        * RespNode = NULL;
339
340    // Got the Envelope node here
341    EnvpNode = ixmlNode_getFirstChild( ( IXML_Node * ) TempDoc );
342    if( EnvpNode == NULL ) {
343        goto error_handler;
344    }
345
346    nl = ixmlElement_getElementsByTagNameNS( ( IXML_Element * ) EnvpNode,
347                                             "*", "Body" );
348
349    if( nl == NULL ) {
350        goto error_handler;
351    }
352
353    BodyNode = ixmlNodeList_item( nl, 0 );
354
355    if( BodyNode == NULL ) {
356        goto error_handler;
357    }
358    // Got action node here
359    ActNode = ixmlNode_getFirstChild( BodyNode );
360    if( ActNode == NULL ) {
361        goto error_handler;
362    }
363    //Test whether this is the action node
364    nodeName = ixmlNode_getNodeName( ActNode );
365    if( nodeName == NULL ) {
366        goto error_handler;
367    }
368
369    if( strstr( nodeName, NodeName ) == NULL ) {
370        goto error_handler;
371    } else {
372        ActNodeName = ixmlPrintDocument( ActNode );
373        if( ActNodeName == NULL ) {
374            goto error_handler;
375        }
376
377        ret_code = ixmlParseBufferEx( ActNodeName, RespNode );
378        if( ret_code != IXML_SUCCESS ) {
379            ixmlFreeDOMString( ActNodeName );
380            ret_code = -1;
381            goto error_handler;
382        }
383    }
384
385    ret_code = 0;               // success
386
387  error_handler:
388
389    ixmlFreeDOMString( ActNodeName );
390
391    if( nl )
392        ixmlNodeList_free( nl );
393    return ret_code;
394}
395
396/****************************************************************************
397*	Function :	check_soap_body
398*
399*	Parameters :
400*		IN IXML_Document *doc :	soap body xml document
401*		    IN const char *urn :
402*		    IN const char *actionName : Name of the requested action
403*
404*	Description :	This function checks the soap body xml came in the
405*		SOAP request.
406*
407*	Return : int
408*		UPNP_E_SUCCESS if successful else returns appropriate error
409*
410*	Note :
411****************************************************************************/
412static int
413check_soap_body( IN IXML_Document * doc,
414                 IN const char *urn,
415                 IN const char *actionName )
416{
417    IXML_NodeList *nl = NULL;
418    IXML_Node *bodyNode = NULL;
419    IXML_Node *actionNode = NULL;
420    const DOMString ns = NULL;
421    const DOMString name = NULL;
422
423    int ret_code = UPNP_E_INVALID_ACTION;
424
425    nl = ixmlDocument_getElementsByTagNameNS( doc, SOAP_URN, SOAP_BODY );
426
427    if( nl ) {
428        bodyNode = ixmlNodeList_item( nl, 0 );
429        if( bodyNode ) {
430            actionNode = ixmlNode_getFirstChild( bodyNode );
431            if( actionNode ) {
432                ns = ixmlNode_getNamespaceURI( actionNode );
433                name = ixmlNode_getLocalName( actionNode );
434
435                if( ( !strcmp( actionName, name ) )
436                    && ( !strcmp( urn, ns ) ) ) {
437                    ret_code = UPNP_E_SUCCESS;
438                }
439            }
440        }
441        ixmlNodeList_free( nl );
442    }
443    return ret_code;
444
445}
446
447/****************************************************************************
448*	Function :	check_soap_action_header
449*
450*	Parameters :
451*		IN http_message_t *request : HTTP request
452*		IN const char *urn :
453*		OUT char **actionName :	 name of the SOAP action
454*
455*	Description :	This function checks the HTTP header of the SOAP request
456*		coming from the control point
457*
458*	Return :	static int
459*		UPNP_E_SUCCESS if successful else returns appropriate error
460*
461*	Note :
462****************************************************************************/
463static int
464check_soap_action_header( IN http_message_t * request,
465                          IN const char *urn,
466                          OUT char **actionName )
467{
468    memptr header_name;
469    http_header_t *soap_action_header = NULL;
470    char *ns_compare = NULL;
471    int tempSize = 0;
472    int ret_code = UPNP_E_SUCCESS;
473    char *temp_header_value = NULL;
474    char *temp = NULL;
475    char *temp2 = NULL;
476
477    //check soap action header
478
479    soap_action_header = httpmsg_find_hdr( request, HDR_SOAPACTION,
480                                           &header_name );
481
482    if( !soap_action_header ) {
483        ret_code = UPNP_E_INVALID_ACTION;
484        return ret_code;
485    }
486
487    if( soap_action_header->value.length <= 0 ) {
488        ret_code = UPNP_E_INVALID_ACTION;
489        return ret_code;
490    }
491
492    temp_header_value =
493        ( char * )malloc( soap_action_header->value.length + 1 );
494
495    if( !temp_header_value ) {
496        ret_code = UPNP_E_OUTOF_MEMORY;
497        return ret_code;
498    }
499
500    strncpy( temp_header_value, soap_action_header->value.buf,
501             soap_action_header->value.length );
502    temp_header_value[soap_action_header->value.length] = 0;
503
504    temp = strchr( temp_header_value, '#' );
505    if( !temp ) {
506        free( temp_header_value );
507        ret_code = UPNP_E_INVALID_ACTION;
508        return ret_code;
509    }
510
511    ( *temp ) = 0;              //temp make string
512
513    //check to see if it is Query State Variable or
514    //Service Action
515
516    tempSize = strlen( urn ) + 2;
517
518    ns_compare = ( char * )malloc( tempSize );
519
520    if( !ns_compare ) {
521        ret_code = UPNP_E_OUTOF_MEMORY;
522        free( temp_header_value );
523        return ret_code;
524    }
525
526//    snprintf( ns_compare, tempSize, "\"%s", urn ); Shearer
527	if (temp_header_value[0] == '\"')
528    snprintf( ns_compare, tempSize, "\"%s", urn );
529	else
530		snprintf(ns_compare, tempSize, "%s", urn);
531
532    if( strcmp( temp_header_value, ns_compare ) ) {
533        ret_code = UPNP_E_INVALID_ACTION;
534    } else {
535        ret_code = UPNP_E_SUCCESS;
536        temp++;
537        temp2 = strchr( temp, '\"' );
538
539        if( temp2 )             //remove ending " if present
540        {
541            ( *temp2 ) = 0;
542        }
543
544        if( *temp )
545            ( *actionName ) = strdup( temp );
546        if( !*actionName ) {
547            ret_code = UPNP_E_OUTOF_MEMORY;
548        }
549    }
550
551    free( temp_header_value );
552    free( ns_compare );
553    return ret_code;
554}
555
556/****************************************************************************
557*	Function :	get_device_info
558*
559*	Parameters :
560*		IN http_message_t* request :	HTTP request
561*		IN int isQuery :	flag for a querry
562*		IN IXML_Document *actionDoc :	action request document
563*		OUT char device_udn[LINE_SIZE] :	Device UDN string
564*		OUT char service_id[LINE_SIZE] :	Service ID string
565*		OUT Upnp_FunPtr *callback :	callback function of the device
566*									application
567*		OUT void** cookie :	cookie stored by device application
568*
569*	Description :	This function retrives all the information needed to
570*		process the incoming SOAP request. It finds the device and service info
571*		and also the callback function to hand-over the request to the device
572*		application.
573*
574*	Return : int
575*		UPNP_E_SUCCESS if successful else returns appropriate error
576*
577*	Note :
578****************************************************************************/
579static int
580get_device_info( IN http_message_t * request,
581                 IN int isQuery,
582                 IN IXML_Document * actionDoc,
583                 OUT char device_udn[LINE_SIZE],
584                 OUT char service_id[LINE_SIZE],
585                 OUT Upnp_FunPtr * callback,
586                 OUT void **cookie )
587{
588    struct Handle_Info *device_info;
589    int device_hnd;
590    service_info *serv_info;
591    char save_char;
592    int ret_code = -1;          // error by default
593    char *control_url;
594    char *actionName = NULL;
595
596    // null-terminate pathquery of url
597    control_url = request->uri.pathquery.buff;
598    save_char = control_url[request->uri.pathquery.size];
599    control_url[request->uri.pathquery.size] = '\0';
600
601    HandleLock(  );
602
603    if( GetDeviceHandleInfo( &device_hnd, &device_info ) != HND_DEVICE ) {
604        goto error_handler;
605    }
606
607    if( ( serv_info =
608          FindServiceControlURLPath( &device_info->ServiceTable,
609                                     control_url ) ) == NULL ) {
610        goto error_handler;
611    }
612
613    if( isQuery ) {
614        ret_code = check_soap_action_header( request, QUERY_STATE_VAR_URN,
615                                             &actionName );
616        if( ( ret_code != UPNP_E_SUCCESS )
617            && ( ret_code != UPNP_E_OUTOF_MEMORY ) ) {
618            ret_code = UPNP_E_INVALID_ACTION;
619            goto error_handler;
620        }
621        //check soap body
622        ret_code =
623            check_soap_body( actionDoc, QUERY_STATE_VAR_URN, actionName );
624        if(actionName)
625        free( actionName );
626        if( ret_code != UPNP_E_SUCCESS ) {
627            goto error_handler;
628        }
629    } else {
630        ret_code = check_soap_action_header( request,
631                                             serv_info->serviceType,
632                                             &actionName );
633        if( ( ret_code != UPNP_E_SUCCESS )
634            && ( ret_code != UPNP_E_OUTOF_MEMORY ) ) {
635            ret_code = UPNP_E_INVALID_SERVICE;
636            goto error_handler;
637        }
638        //check soap body
639        ret_code =
640            check_soap_body( actionDoc, serv_info->serviceType,
641                             actionName );
642		if(actionName)
643        free( actionName );
644        if( ret_code != UPNP_E_SUCCESS ) {
645            ret_code = UPNP_E_INVALID_SERVICE;
646            goto error_handler;
647        }
648    }
649
650    namecopy( service_id, serv_info->serviceId );
651    namecopy( device_udn, serv_info->UDN );
652    *callback = device_info->Callback;
653    *cookie = device_info->Cookie;
654
655    ret_code = 0;
656
657  error_handler:
658    control_url[request->uri.pathquery.size] = save_char;   // restore
659    HandleUnlock(  );
660    return ret_code;
661}
662
663/****************************************************************************
664*	Function :	send_action_response
665*
666*	Parameters :
667*		IN SOCKINFO *info :	socket info
668*		IN IXML_Document *action_resp : The response document
669*		IN http_message_t* request :	action request document
670*
671*	Description :	This function sends the SOAP response
672*
673*	Return : void
674*
675*	Note :
676****************************************************************************/
677static XINLINE void
678send_action_response( IN SOCKINFO * info,
679                      IN IXML_Document * action_resp,
680                      IN http_message_t * request )
681{
682    char *xml_response = NULL;
683    membuffer headers;
684    int major,
685      minor;
686    int err_code;
687    int content_length;
688    int ret_code;
689    int timeout_secs = SOAP_TIMEOUT;
690    static char *start_body =
691        "<s:Envelope xmlns:s=\"http://schemas.xmlsoap."
692        "org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap."
693        "org/soap/encoding/\"><s:Body>\n";
694    static char *end_body = "</s:Body> </s:Envelope>";
695
696    // init
697    http_CalcResponseVersion( request->major_version,
698                              request->minor_version, &major, &minor );
699    membuffer_init( &headers );
700    err_code = UPNP_E_OUTOF_MEMORY; // one error only
701    // get xml
702    xml_response = ixmlPrintDocument( ( IXML_Node * ) action_resp );
703    if( xml_response == NULL ) {
704        goto error_handler;
705    }
706
707    content_length = strlen( start_body ) + strlen( xml_response ) +
708        strlen( end_body );
709    // make headers
710    if( http_MakeMessage( &headers, major, minor, "RNsDsSc", HTTP_OK,   // status code
711                          content_length, ContentTypeHeader, "EXT:\r\n" // EXT header
712         ) != 0 ) {
713        goto error_handler;
714    }
715    // send whole msg
716    ret_code = http_SendMessage( info, &timeout_secs, "bbbb",
717                                 headers.buf, headers.length,
718                                 start_body, strlen( start_body ),
719                                 xml_response, strlen( xml_response ),
720                                 end_body, strlen( end_body ) );
721
722    DBGONLY( if( ret_code != 0 ) {
723             UpnpPrintf( UPNP_INFO, SOAP, __FILE__, __LINE__,
724                         "Failed to send response: err code = %d\n",
725                         ret_code );}
726     )
727
728        err_code = 0;
729
730  error_handler:
731    ixmlFreeDOMString( xml_response );
732    membuffer_destroy( &headers );
733    if( err_code != 0 ) {
734        // only one type of error to worry about - out of mem
735        send_error_response( info, SOAP_ACTION_FAILED, "Out of memory",
736                             request );
737    }
738}
739
740/****************************************************************************
741*	Function :	get_var_name
742*
743*	Parameters :
744*		IN IXML_Document *TempDoc :	Document containing variable request
745*		OUT char* VarName :	Name of the state varible
746*
747*	Description :	This function finds the name of the state variable
748*				asked in the SOAP request.
749*
750*	Return :	int
751*		returns 0 if successful else returns -1.
752*	Note :
753****************************************************************************/
754static XINLINE int
755get_var_name( IN IXML_Document * TempDoc,
756              OUT char *VarName )
757{
758    IXML_Node *EnvpNode = NULL;
759    IXML_Node *BodyNode = NULL;
760    IXML_Node *StNode = NULL;
761    IXML_Node *VarNameNode = NULL;
762    IXML_Node *VarNode = NULL;
763    const DOMString StNodeName = NULL;
764    DOMString Temp = NULL;
765    int ret_val = -1;
766
767    // Got the Envelop node here
768    EnvpNode = ixmlNode_getFirstChild( ( IXML_Node * ) TempDoc );
769    if( EnvpNode == NULL ) {
770        goto error_handler;
771    }
772    // Got Body here
773    BodyNode = ixmlNode_getFirstChild( EnvpNode );
774    if( BodyNode == NULL ) {
775        goto error_handler;
776    }
777    // Got action node here
778    StNode = ixmlNode_getFirstChild( BodyNode );
779    if( StNode == NULL ) {
780        goto error_handler;
781    }
782    //Test whether this is the action node
783    StNodeName = ixmlNode_getNodeName( StNode );
784    if( StNodeName == NULL || strstr( StNodeName,
785                                      "QueryStateVariable" ) == NULL ) {
786        goto error_handler;
787    }
788
789    VarNameNode = ixmlNode_getFirstChild( StNode );
790    if( VarNameNode == NULL ) {
791        goto error_handler;
792    }
793
794    VarNode = ixmlNode_getFirstChild( VarNameNode );
795    Temp = ixmlNode_getNodeValue( VarNode );
796    linecopy( VarName, Temp );
797
798    DBGONLY( UpnpPrintf( UPNP_INFO, SOAP, __FILE__, __LINE__,
799                         "Received query for variable  name %s\n",
800                         VarName );
801         )
802
803        ret_val = 0;            // success
804
805  error_handler:
806    return ret_val;
807}
808
809/****************************************************************************
810*	Function :	handle_query_variable
811*
812*	Parameters :
813*		IN SOCKINFO *info :	Socket info
814*		IN http_message_t* request : HTTP request
815*		IN IXML_Document *xml_doc :	Document containing the variable request
816*									SOAP message
817*
818*	Description :	This action handles the SOAP requests to querry the
819*				state variables. This functionality has been deprecated in
820*				the UPnP V1.0 architecture
821*
822*	Return :	void
823*
824*	Note :
825****************************************************************************/
826static XINLINE void
827handle_query_variable( IN SOCKINFO * info,
828                       IN http_message_t * request,
829                       IN IXML_Document * xml_doc )
830{
831    Upnp_FunPtr soap_event_callback;
832    void *cookie;
833    char var_name[LINE_SIZE];
834    struct Upnp_State_Var_Request variable;
835    const char *err_str;
836    int err_code;
837
838    // get var name
839    if( get_var_name( xml_doc, var_name ) != 0 ) {
840        send_error_response( info, SOAP_INVALID_VAR,
841                             Soap_Invalid_Var, request );
842        return;
843    }
844    // get info for event
845    if( get_device_info( request, 1, xml_doc, variable.DevUDN,
846                         variable.ServiceID,
847                         &soap_event_callback, &cookie ) != 0 ) {
848        send_error_response( info, SOAP_INVALID_VAR,
849                             Soap_Invalid_Var, request );
850        return;
851    }
852
853    linecopy( variable.ErrStr, "" );
854    variable.ErrCode = UPNP_E_SUCCESS;
855    namecopy( variable.StateVarName, var_name );
856    variable.CurrentVal = NULL;
857    variable.CtrlPtIPAddr = info->foreign_ip_addr;
858
859    // send event
860    soap_event_callback( UPNP_CONTROL_GET_VAR_REQUEST, &variable, cookie );
861
862    DBGONLY( UpnpPrintf( UPNP_INFO, SOAP, __FILE__, __LINE__,
863                         "Return from callback for var request\n" ) );
864
865    // validate, and handle result
866    if( variable.CurrentVal == NULL ) {
867        err_code = SOAP_ACTION_FAILED;
868        err_str = Soap_Action_Failed;
869        send_error_response( info, SOAP_INVALID_VAR,
870                             Soap_Invalid_Var, request );
871        return;
872    }
873    if( variable.ErrCode != UPNP_E_SUCCESS ) {
874        if( strlen( variable.ErrStr ) > 0 ) {
875            err_code = SOAP_INVALID_VAR;
876            err_str = Soap_Invalid_Var;
877        } else {
878            err_code = variable.ErrCode;
879            err_str = variable.ErrStr;
880        }
881        send_error_response( info, err_code, err_str, request );
882        return;
883    }
884    // send response
885    send_var_query_response( info, variable.CurrentVal, request );
886    ixmlFreeDOMString( variable.CurrentVal );
887
888}
889
890/****************************************************************************
891*	Function :	handle_invoke_action
892*
893*	Parameters :
894*		IN SOCKINFO *info :	Socket info
895*		IN http_message_t* request : HTTP Request
896*		IN memptr action_name :	 Name of the SOAP Action
897*		IN IXML_Document *xml_doc :	document containing the SOAP action
898*									request
899*
900*	Description :	This functions handle the SOAP action request. It checks
901*		the integrity of the SOAP action request and gives the call back to
902*		the device application.
903*
904*	Return : void
905*
906*	Note :
907****************************************************************************/
908static void
909handle_invoke_action( IN SOCKINFO * info,
910                      IN http_message_t * request,
911                      IN memptr action_name,
912                      IN IXML_Document * xml_doc )
913{
914    char save_char;
915    IXML_Document *resp_node = NULL;
916    struct Upnp_Action_Request action;
917    Upnp_FunPtr soap_event_callback;
918    void *cookie = NULL;
919    int err_code;
920    const char *err_str;
921
922    action.ActionResult = NULL;
923
924    // null-terminate
925    save_char = action_name.buf[action_name.length];
926    action_name.buf[action_name.length] = '\0';
927
928    // set default error
929    err_code = SOAP_INVALID_ACTION;
930    err_str = Soap_Invalid_Action;
931
932    // get action node
933    if( get_action_node( xml_doc, action_name.buf, &resp_node ) == -1 ) {
934        goto error_handler;
935    }
936    // get device info for action event
937    err_code = get_device_info( request, 0, xml_doc, action.DevUDN,
938                                action.ServiceID, &soap_event_callback,
939                                &cookie );
940
941    if( err_code != UPNP_E_SUCCESS ) {
942        goto error_handler;
943    }
944
945    namecopy( action.ActionName, action_name.buf );
946    linecopy( action.ErrStr, "" );
947    action.ActionRequest = resp_node;
948    action.ActionResult = NULL;
949    action.ErrCode = UPNP_E_SUCCESS;
950    action.CtrlPtIPAddr = info->foreign_ip_addr;
951
952    DBGONLY( UpnpPrintf( UPNP_INFO, SOAP, __FILE__, __LINE__,
953                         "Calling Callback\n" ) );
954    soap_event_callback( UPNP_CONTROL_ACTION_REQUEST, &action, cookie );
955    if( action.ErrCode != UPNP_E_SUCCESS ) {
956        if( strlen( action.ErrStr ) <= 0 ) {
957            err_code = SOAP_ACTION_FAILED;
958            err_str = Soap_Action_Failed;
959        } else {
960            err_code = action.ErrCode;
961            err_str = action.ErrStr;
962        }
963        goto error_handler;
964    }
965    // validate, and handle action error
966    if( action.ActionResult == NULL ) {
967        err_code = SOAP_ACTION_FAILED;
968        err_str = Soap_Action_Failed;
969        goto error_handler;
970    }
971    // send response
972    send_action_response( info, action.ActionResult, request );
973    err_code = 0;
974
975    // error handling and cleanup
976  error_handler:
977   	if(action.ActionResult)
978    ixmlDocument_free( action.ActionResult );
979    if(resp_node)
980    ixmlDocument_free( resp_node );
981    action_name.buf[action_name.length] = save_char;    // restore
982    if( err_code != 0 ) {
983        send_error_response( info, err_code, err_str, request );
984    }
985}
986
987/****************************************************************************
988*	Function :	soap_device_callback
989*
990*	Parameters :
991*		  IN http_parser_t *parser : Parsed request received by the device
992*		  IN http_message_t* request :	HTTP request
993*		  INOUT SOCKINFO *info :	socket info
994*
995*	Description :	This is a callback called by minisever after receiving
996*		the request from the control point. This function will start
997*		processing the request. It calls handle_invoke_action to handle the
998*		SOAP action
999*
1000*	Return :	void
1001*
1002*	Note :
1003****************************************************************************/
1004void
1005soap_device_callback( IN http_parser_t * parser,
1006                      IN http_message_t * request,
1007                      INOUT SOCKINFO * info )
1008{
1009    int err_code;
1010    const char *err_str;
1011    memptr action_name;
1012    IXML_Document *xml_doc = NULL;
1013
1014    // set default error
1015    err_code = SOAP_INVALID_ACTION;
1016    err_str = Soap_Invalid_Action;
1017
1018    // validate: content-type == text/xml
1019    if( !has_xml_content_type( request ) ) {
1020        goto error_handler;
1021    }
1022    // type of request
1023    if( get_request_type( request, &action_name ) != 0 ) {
1024        goto error_handler;
1025    }
1026    // parse XML
1027    err_code = ixmlParseBufferEx( request->entity.buf, &xml_doc );
1028    if( err_code != IXML_SUCCESS ) {
1029        if( err_code == IXML_INSUFFICIENT_MEMORY ) {
1030            err_code = UPNP_E_OUTOF_MEMORY;
1031        } else {
1032            err_code = SOAP_ACTION_FAILED;
1033        }
1034
1035        err_str = "XML error";
1036        goto error_handler;
1037    }
1038
1039    if( action_name.length == 0 ) {
1040        // query var
1041        handle_query_variable( info, request, xml_doc );
1042    } else {
1043        // invoke action
1044        handle_invoke_action( info, request, action_name, xml_doc );
1045    }
1046
1047    err_code = 0;               // no error
1048
1049  error_handler:
1050  	if(xml_doc)
1051    ixmlDocument_free( xml_doc );
1052    if( err_code != 0 ) {
1053        send_error_response( info, err_code, err_str, request );
1054    }
1055}
1056
1057#endif // EXCLUDE_SOAP
1058
1059#endif // INCLUDE_DEVICE_APIS
1060