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