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#include "config.h" 33#if EXCLUDE_GENA == 0 34#ifdef INCLUDE_CLIENT_APIS 35 36#include "gena.h" 37#include "sysdep.h" 38#include "uuid.h" 39#include "upnpapi.h" 40#include "parsetools.h" 41#include "statcodes.h" 42#include "httpparser.h" 43#include "httpreadwrite.h" 44 45extern ithread_mutex_t GlobalClientSubscribeMutex; 46 47/************************************************************************ 48* Function : GenaAutoRenewSubscription 49* 50* Parameters: 51* IN void *input: Thread data(upnp_timeout *) needed to send the renewal 52* 53* Description: 54* This is a thread function to send the renewal just before the 55* subscription times out. 56* 57* Returns: VOID 58* 59***************************************************************************/ 60static void 61GenaAutoRenewSubscription( IN void *input ) 62{ 63 upnp_timeout *event = ( upnp_timeout * ) input; 64 void *cookie; 65 Upnp_FunPtr callback_fun; 66 struct Handle_Info *handle_info; 67 struct Upnp_Event_Subscribe *sub_struct = 68 ( struct Upnp_Event_Subscribe * ) 69 event->Event; 70 71 int send_callback = 0; 72 int eventType = 0; 73 74 if( AUTO_RENEW_TIME == 0 ) { 75 DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, 76 "GENA SUB EXPIRED" ) ); 77 sub_struct->ErrCode = UPNP_E_SUCCESS; 78 send_callback = 1; 79 eventType = UPNP_EVENT_SUBSCRIPTION_EXPIRED; 80 } else { 81 DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, 82 "GENA AUTO RENEW" ) ); 83 if( ( ( sub_struct->ErrCode = genaRenewSubscription( event->handle, 84 sub_struct-> 85 Sid, 86 &sub_struct-> 87 TimeOut ) ) != 88 UPNP_E_SUCCESS ) 89 && ( sub_struct->ErrCode != GENA_E_BAD_SID ) 90 && ( sub_struct->ErrCode != GENA_E_BAD_HANDLE ) ) { 91 send_callback = 1; 92 eventType = UPNP_EVENT_AUTORENEWAL_FAILED; 93 } 94 } 95 if( send_callback ) { 96 HandleLock( ); 97 if( GetHandleInfo( event->handle, &handle_info ) != HND_CLIENT ) { 98 HandleUnlock( ); 99 free_upnp_timeout( event ); 100 return; 101 } 102 DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, 103 "HANDLE IS VALID" ) ); 104 callback_fun = handle_info->Callback; 105 cookie = handle_info->Cookie; 106 HandleUnlock( ); 107 //make callback 108 109 callback_fun( eventType, event->Event, cookie ); 110 } 111 112 free_upnp_timeout( event ); 113} 114 115/************************************************************************ 116* Function : ScheduleGenaAutoRenew 117* 118* Parameters: 119* IN int client_handle: Handle that also contains the subscription list 120* IN int TimeOut: The time out value of the subscription 121* IN client_subscription * sub: Subscription being renewed 122* 123* Description: 124* This function schedules a job to renew the subscription just before 125* time out. 126* 127* Returns: int 128* return GENA_E_SUCCESS if successful else returns appropriate error 129***************************************************************************/ 130static int 131ScheduleGenaAutoRenew( IN int client_handle, 132 IN int TimeOut, 133 IN client_subscription * sub ) 134{ 135 struct Upnp_Event_Subscribe *RenewEventStruct = NULL; 136 upnp_timeout *RenewEvent = NULL; 137 int return_code = GENA_SUCCESS; 138 ThreadPoolJob job; 139 140 if( TimeOut == UPNP_INFINITE ) { 141 return GENA_SUCCESS; 142 } 143 144 RenewEventStruct = ( struct Upnp_Event_Subscribe * )malloc( sizeof 145 ( struct 146 Upnp_Event_Subscribe ) ); 147 148 if( RenewEventStruct == NULL ) { 149 return UPNP_E_OUTOF_MEMORY; 150 } 151 152 RenewEvent = ( upnp_timeout * ) malloc( sizeof( upnp_timeout ) ); 153 154 if( RenewEvent == NULL ) { 155 free( RenewEventStruct ); 156 return UPNP_E_OUTOF_MEMORY; 157 } 158 //schedule expire event 159 strcpy( RenewEventStruct->Sid, sub->sid ); 160 RenewEventStruct->ErrCode = UPNP_E_SUCCESS; 161 strncpy( RenewEventStruct->PublisherUrl, sub->EventURL, 162 NAME_SIZE - 1 ); 163 RenewEventStruct->TimeOut = TimeOut; 164 165 //RenewEvent->EventType=UPNP_EVENT_SUBSCRIPTION_EXPIRE; 166 RenewEvent->handle = client_handle; 167 RenewEvent->Event = RenewEventStruct; 168 169 TPJobInit( &job, ( start_routine ) GenaAutoRenewSubscription, 170 RenewEvent ); 171 TPJobSetFreeFunction( &job, ( free_routine ) free_upnp_timeout ); 172 TPJobSetPriority( &job, MED_PRIORITY ); 173 174 //Schedule the job 175 if( ( return_code = TimerThreadSchedule( &gTimerThread, 176 TimeOut - AUTO_RENEW_TIME, 177 REL_SEC, &job, SHORT_TERM, 178 &( RenewEvent-> 179 eventId ) ) ) != 180 UPNP_E_SUCCESS ) { 181 free( RenewEvent ); 182 free( RenewEventStruct ); 183 return return_code; 184 } 185 186 sub->RenewEventId = RenewEvent->eventId; 187 return GENA_SUCCESS; 188} 189 190/************************************************************************ 191* Function : gena_unsubscribe 192* 193* Parameters: 194* IN char *url: Event URL of the service 195* IN char *sid: The subcription ID. 196* OUT http_parser_t* response: The UNSUBCRIBE response from the device 197* 198* Description: 199* This function sends the UNSUBCRIBE gena request and recieves the 200* response from the device and returns it as a parameter 201* 202* Returns: int 203* return 0 if successful else returns appropriate error 204***************************************************************************/ 205static int 206gena_unsubscribe( IN char *url, 207 IN char *sid, 208 OUT http_parser_t * response ) 209{ 210 int return_code; 211 uri_type dest_url; 212 membuffer request; 213 214 // parse url 215 return_code = http_FixStrUrl( url, strlen( url ), &dest_url ); 216 if( return_code != 0 ) { 217 return return_code; 218 } 219 // make request msg 220 membuffer_init( &request ); 221 request.size_inc = 30; 222 return_code = http_MakeMessage( &request, 1, 1, 223 "q" "ssc" "U" "c", 224 HTTPMETHOD_UNSUBSCRIBE, &dest_url, 225 "SID: ", sid ); 226 227 //Not able to make the message so destroy the existing buffer 228 if( return_code != 0 ) { 229 membuffer_destroy( &request ); 230 return return_code; 231 } 232 // send request and get reply 233 return_code = http_RequestAndResponse( &dest_url, request.buf, 234 request.length, 235 HTTPMETHOD_UNSUBSCRIBE, 236 HTTP_DEFAULT_TIMEOUT, 237 response ); 238 239 membuffer_destroy( &request ); 240 241 if( return_code != 0 ) 242 httpmsg_destroy( &response->msg ); 243 244 if( return_code == 0 && response->msg.status_code != HTTP_OK ) { 245 return_code = UPNP_E_UNSUBSCRIBE_UNACCEPTED; 246 httpmsg_destroy( &response->msg ); 247 } 248 249 return return_code; 250} 251 252/************************************************************************ 253* Function : gena_subscribe 254* 255* Parameters: 256* IN char *url: url of service to subscribe 257* INOUT int* timeout:subscription time desired (in secs) 258* IN char* renewal_sid:for renewal, this contains a currently h 259* held subscription SID. For first time 260* subscription, this must be NULL 261* OUT char** sid: SID returned by the subscription or renew msg 262* 263* Description: 264* This function subscribes or renew subscription 265* 266* Returns: int 267* return 0 if successful else returns appropriate error 268***************************************************************************/ 269static int 270gena_subscribe( IN char *url, 271 INOUT int *timeout, 272 IN char *renewal_sid, 273 OUT char **sid ) 274{ 275 int return_code; 276 memptr sid_hdr, 277 timeout_hdr; 278 char timeout_str[25]; 279 membuffer request; 280 uri_type dest_url; 281 http_parser_t response; 282 283 *sid = NULL; // init 284 285 // request timeout to string 286 if( ( timeout == NULL ) || 287 ( ( *timeout > 0 ) 288 && ( *timeout < CP_MINIMUM_SUBSCRIPTION_TIME ) ) ) { 289 sprintf( timeout_str, "%d", CP_MINIMUM_SUBSCRIPTION_TIME ); 290 } else if( *timeout >= 0 ) { 291 sprintf( timeout_str, "%d", *timeout ); 292 } else { 293 strcpy( timeout_str, "infinite" ); 294 } 295 296 // parse url 297 return_code = http_FixStrUrl( url, strlen( url ), &dest_url ); 298 if( return_code != 0 ) { 299 return return_code; 300 } 301 // make request msg 302 membuffer_init( &request ); 303 request.size_inc = 30; 304 if( renewal_sid ) { 305 // renew subscription 306 return_code = http_MakeMessage( &request, 1, 1, 307 "q" "ssc" "ssc" "c", 308 HTTPMETHOD_SUBSCRIBE, &dest_url, 309 "SID: ", renewal_sid, 310 "TIMEOUT: Second-", timeout_str ); 311 } else { 312 // subscribe 313 return_code = http_MakeMessage( &request, 1, 1, 314 "q" "sssdsscc", 315 HTTPMETHOD_SUBSCRIBE, &dest_url, 316 "CALLBACK: <http://", LOCAL_HOST, 317 ":", LOCAL_PORT, 318 "/>\r\n" "NT: upnp:event\r\n" 319 "TIMEOUT: Second-", timeout_str ); 320 } 321 if( return_code != 0 ) { 322 return return_code; 323 } 324 // send request and get reply 325 return_code = http_RequestAndResponse( &dest_url, request.buf, 326 request.length, 327 HTTPMETHOD_SUBSCRIBE, 328 HTTP_DEFAULT_TIMEOUT, 329 &response ); 330 331 membuffer_destroy( &request ); 332 333 if( return_code != 0 ) { 334 httpmsg_destroy( &response.msg ); 335 return return_code; 336 } 337 if( response.msg.status_code != HTTP_OK ) { 338 httpmsg_destroy( &response.msg ); 339 return UPNP_E_SUBSCRIBE_UNACCEPTED; 340 } 341 // get SID and TIMEOUT 342 if( httpmsg_find_hdr( &response.msg, HDR_SID, &sid_hdr ) == NULL || 343 sid_hdr.length == 0 || 344 httpmsg_find_hdr( &response.msg, 345 HDR_TIMEOUT, &timeout_hdr ) == NULL || 346 timeout_hdr.length == 0 ) { 347 httpmsg_destroy( &response.msg ); 348 return UPNP_E_BAD_RESPONSE; 349 } 350 // save timeout 351 if( matchstr( timeout_hdr.buf, timeout_hdr.length, "%iSecond-%d%0", 352 timeout ) == PARSE_OK ) { 353 // nothing 354 } else if( memptr_cmp_nocase( &timeout_hdr, "Second-infinite" ) == 0 ) { 355 *timeout = -1; 356 } else { 357 httpmsg_destroy( &response.msg ); 358 return UPNP_E_BAD_RESPONSE; 359 } 360 361 // save SID 362 *sid = str_alloc( sid_hdr.buf, sid_hdr.length ); 363 if( *sid == NULL ) { 364 httpmsg_destroy( &response.msg ); 365 return UPNP_E_OUTOF_MEMORY; 366 } 367 368 httpmsg_destroy( &response.msg ); 369 return UPNP_E_SUCCESS; 370} 371 372/************************************************************************ 373* Function : genaUnregisterClient 374* 375* Parameters: 376* IN UpnpClient_Handle client_handle: Handle containing all the control 377* point related information 378* 379* Description: 380* This function unsubcribes all the outstanding subscriptions and cleans 381* the subscription list. This function is called when control point 382* unregisters. 383* 384* Returns: int 385* return UPNP_E_SUCCESS if successful else returns appropriate error 386***************************************************************************/ 387int 388genaUnregisterClient( IN UpnpClient_Handle client_handle ) 389{ 390 client_subscription sub_copy; 391 int return_code = UPNP_E_SUCCESS; 392 struct Handle_Info *handle_info = NULL; 393 http_parser_t response; 394 395 while( TRUE ) { 396 HandleLock( ); 397 if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) { 398 HandleUnlock( ); 399 return GENA_E_BAD_HANDLE; 400 } 401 402 if( handle_info->ClientSubList == NULL ) { 403 return_code = UPNP_E_SUCCESS; 404 break; 405 } 406 407 return_code = copy_client_subscription( handle_info->ClientSubList, 408 &sub_copy ); 409 if( return_code != HTTP_SUCCESS ) { 410 break; 411 } 412 413 RemoveClientSubClientSID( &handle_info->ClientSubList, 414 sub_copy.sid ); 415 416 HandleUnlock( ); 417 418 return_code = gena_unsubscribe( sub_copy.EventURL, 419 sub_copy.ActualSID, &response ); 420 if( return_code == 0 ) { 421 httpmsg_destroy( &response.msg ); 422 } 423 424 free_client_subscription( &sub_copy ); 425 } 426 427 freeClientSubList( handle_info->ClientSubList ); 428 HandleUnlock( ); 429 return return_code; 430} 431 432/************************************************************************ 433* Function : genaUnSubscribe 434* 435* Parameters: 436* IN UpnpClient_Handle client_handle: UPnP client handle 437* IN SID in_sid: The subscription ID 438* 439* Description: 440* This function unsubscribes a SID. It first validates the SID and 441* client_handle,copies the subscription, sends UNSUBSCRIBE http request 442* to service processes request and finally removes the subscription 443* 444* Returns: int 445* return UPNP_E_SUCCESS if service response is OK else 446* returns appropriate error 447***************************************************************************/ 448int 449genaUnSubscribe( IN UpnpClient_Handle client_handle, 450 IN const Upnp_SID in_sid ) 451{ 452 client_subscription *sub; 453 int return_code = GENA_SUCCESS; 454 struct Handle_Info *handle_info; 455 client_subscription sub_copy; 456 http_parser_t response; 457 458 HandleLock( ); 459 460 // validate handle and sid 461 462 if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) { 463 HandleUnlock( ); 464 return GENA_E_BAD_HANDLE; 465 } 466 467 if( ( sub = 468 GetClientSubClientSID( handle_info->ClientSubList, in_sid ) ) 469 == NULL ) { 470 HandleUnlock( ); 471 return GENA_E_BAD_SID; 472 } 473 474 return_code = copy_client_subscription( sub, &sub_copy ); 475 476 HandleUnlock( ); 477 478 return_code = gena_unsubscribe( sub_copy.EventURL, sub_copy.ActualSID, 479 &response ); 480 481 if( return_code == 0 ) { 482 httpmsg_destroy( &response.msg ); 483 } 484 485 free_client_subscription( &sub_copy ); 486 487 HandleLock( ); 488 489 if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) { 490 HandleUnlock( ); 491 return GENA_E_BAD_HANDLE; 492 } 493 494 RemoveClientSubClientSID( &handle_info->ClientSubList, in_sid ); 495 496 HandleUnlock( ); 497 498 return return_code; 499} 500 501/************************************************************************ 502* Function : genaSubscribe 503* 504* Parameters: 505* IN UpnpClient_Handle client_handle: 506* IN char * PublisherURL: NULL Terminated, of the form : 507* "http://134.134.156.80:4000/RedBulb/Event" 508* INOUT int * TimeOut: requested Duration, if -1, then "infinite". 509* in the OUT case: actual Duration granted 510* by Service, -1 for infinite 511* OUT Upnp_SID out_sid:sid of subscription, memory passed in by caller 512* 513* Description: 514* This function subscribes to a PublisherURL ( also mentioned as EventURL 515* some places). It sends SUBSCRIBE http request to service processes 516* request. Finally adds a Subscription to 517* the clients subscription list, if service responds with OK 518* 519* Returns: int 520* return UPNP_E_SUCCESS if service response is OK else 521* returns appropriate error 522***************************************************************************/ 523int 524genaSubscribe( IN UpnpClient_Handle client_handle, 525 IN char *PublisherURL, 526 INOUT int *TimeOut, 527 OUT Upnp_SID out_sid ) 528{ 529 int return_code = GENA_SUCCESS; 530 client_subscription *newSubscription = NULL; 531 uuid_upnp uid; 532 Upnp_SID temp_sid; 533 char *ActualSID = NULL; 534 struct Handle_Info *handle_info; 535 char *EventURL = NULL; 536 537 DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, 538 "GENA SUBSCRIBE BEGIN" ) ); 539 HandleLock( ); 540 541 memset( out_sid, 0, sizeof( Upnp_SID ) ); 542 543 // validate handle 544 if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) { 545 HandleUnlock( ); 546 return GENA_E_BAD_HANDLE; 547 } 548 HandleUnlock( ); 549 550 // subscribe 551 SubscribeLock( ); 552 return_code = 553 gena_subscribe( PublisherURL, TimeOut, NULL, &ActualSID ); 554 HandleLock( ); 555 if( return_code != UPNP_E_SUCCESS ) { 556 DBGONLY( UpnpPrintf( UPNP_CRITICAL, GENA, __FILE__, __LINE__, 557 "SUBSCRIBE FAILED in transfer error code: %d returned\n", 558 return_code ) ); 559 goto error_handler; 560 } 561 562 if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) { 563 return_code = GENA_E_BAD_HANDLE; 564 goto error_handler; 565 } 566 // generate client SID 567 uuid_create( &uid ); 568 uuid_unpack( &uid, temp_sid ); 569 sprintf( out_sid, "uuid:%s", temp_sid ); 570 571 // create event url 572 EventURL = ( char * )malloc( strlen( PublisherURL ) + 1 ); 573 if( EventURL == NULL ) { 574 return_code = UPNP_E_OUTOF_MEMORY; 575 goto error_handler; 576 } 577 578 strcpy( EventURL, PublisherURL ); 579 580 // fill subscription 581 newSubscription = 582 ( client_subscription * ) malloc( sizeof( client_subscription ) ); 583 if( newSubscription == NULL ) { 584 return_code = UPNP_E_OUTOF_MEMORY; 585 goto error_handler; 586 } 587 newSubscription->EventURL = EventURL; 588 newSubscription->ActualSID = ActualSID; 589 strcpy( newSubscription->sid, out_sid ); 590 newSubscription->RenewEventId = -1; 591 newSubscription->next = handle_info->ClientSubList; 592 handle_info->ClientSubList = newSubscription; 593 594 // schedule expiration event 595 return_code = ScheduleGenaAutoRenew( client_handle, *TimeOut, 596 newSubscription ); 597 598 error_handler: 599 if( return_code != UPNP_E_SUCCESS ) { 600 free( ActualSID ); 601 free( EventURL ); 602 free( newSubscription ); 603 } 604 HandleUnlock( ); 605 SubscribeUnlock( ); 606 return return_code; 607} 608 609/************************************************************************ 610* Function : genaRenewSubscription 611* 612* Parameters: 613* IN UpnpClient_Handle client_handle: Client handle 614* IN const Upnp_SID in_sid: subscription ID 615* INOUT int * TimeOut: requested Duration, if -1, then "infinite". 616* in the OUT case: actual Duration granted 617* by Service, -1 for infinite 618* 619* Description: 620* This function renews a SID. It first validates the SID and 621* client_handle and copies the subscription. It sends RENEW 622* (modified SUBSCRIBE) http request to service and processes 623* the response. 624* 625* Returns: int 626* return UPNP_E_SUCCESS if service response is OK else 627* returns appropriate error 628***************************************************************************/ 629int 630genaRenewSubscription( IN UpnpClient_Handle client_handle, 631 IN const Upnp_SID in_sid, 632 INOUT int *TimeOut ) 633{ 634 int return_code = GENA_SUCCESS; 635 client_subscription *sub; 636 client_subscription sub_copy; 637 struct Handle_Info *handle_info; 638 639 char *ActualSID; 640 ThreadPoolJob tempJob; 641 642 HandleLock( ); 643 644 // validate handle and sid 645 if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) { 646 HandleUnlock( ); 647 return GENA_E_BAD_HANDLE; 648 } 649 650 if( ( sub = GetClientSubClientSID( handle_info->ClientSubList, 651 in_sid ) ) == NULL ) { 652 HandleUnlock( ); 653 return GENA_E_BAD_SID; 654 } 655 // remove old events 656 if( TimerThreadRemove( &gTimerThread, sub->RenewEventId, &tempJob ) == 657 0 ) { 658 659 free_upnp_timeout( ( upnp_timeout * ) tempJob.arg ); 660 } 661 662 DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, 663 "REMOVED AUTO RENEW EVENT" ) ); 664 665 sub->RenewEventId = -1; 666 return_code = copy_client_subscription( sub, &sub_copy ); 667 668 HandleUnlock( ); 669 670 if( return_code != HTTP_SUCCESS ) { 671 return return_code; 672 } 673 674 return_code = gena_subscribe( sub_copy.EventURL, TimeOut, 675 sub_copy.ActualSID, &ActualSID ); 676 HandleLock( ); 677 678 if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) { 679 HandleUnlock( ); 680 if( return_code == UPNP_E_SUCCESS ) { 681 if(ActualSID) 682 free( ActualSID ); 683 } 684 return GENA_E_BAD_HANDLE; 685 } 686 // we just called GetHandleInfo, so we don't check for return value 687 //GetHandleInfo(client_handle, &handle_info); 688 689 if( return_code != UPNP_E_SUCCESS ) { 690 // network failure (remove client sub) 691 RemoveClientSubClientSID( &handle_info->ClientSubList, in_sid ); 692 free_client_subscription( &sub_copy ); 693 HandleUnlock( ); 694 return return_code; 695 } 696 // get subscription 697 if( ( sub = GetClientSubClientSID( handle_info->ClientSubList, 698 in_sid ) ) == NULL ) { 699 if(ActualSID) 700 free( ActualSID ); 701 free_client_subscription( &sub_copy ); 702 HandleUnlock( ); 703 return GENA_E_BAD_SID; 704 } 705 // store actual sid 706 if(sub->ActualSID) 707 free( sub->ActualSID ); 708 sub->ActualSID = ActualSID; 709 710 // start renew subscription timer 711 return_code = ScheduleGenaAutoRenew( client_handle, *TimeOut, sub ); 712 if( return_code != GENA_SUCCESS ) { 713 RemoveClientSubClientSID( &handle_info->ClientSubList, sub->sid ); 714 } 715 free_client_subscription( &sub_copy ); 716 HandleUnlock( ); 717 return return_code; 718} 719 720/************************************************************************ 721* Function : gena_process_notification_event 722* 723* Parameters: 724* IN SOCKINFO *info: Socket structure containing the device socket 725* information 726* IN http_message_t* event: The http message contains the GENA 727* notification 728* 729* Description: 730* This function processes NOTIFY events that are sent by devices. 731* called by genacallback() 732* 733* Returns: void 734* 735* Note : called by genacallback() 736****************************************************************************/ 737void 738gena_process_notification_event( IN SOCKINFO * info, 739 IN http_message_t * event ) 740{ 741 struct Upnp_Event event_struct; 742 int eventKey; 743 token sid; 744 client_subscription *subscription_1; 745 IXML_Document *ChangedVars; 746 struct Handle_Info *handle_info; 747 void *cookie; 748 Upnp_FunPtr callback; 749 UpnpClient_Handle client_handle; 750 751 memptr sid_hdr; 752 memptr nt_hdr, 753 nts_hdr; 754 memptr seq_hdr; 755 756 // get SID 757 if( httpmsg_find_hdr( event, HDR_SID, &sid_hdr ) == NULL ) { 758 error_respond( info, HTTP_PRECONDITION_FAILED, event ); 759 760 return; 761 } 762 sid.buff = sid_hdr.buf; 763 sid.size = sid_hdr.length; 764 765 // get event key 766 if( httpmsg_find_hdr( event, HDR_SEQ, &seq_hdr ) == NULL || 767 matchstr( seq_hdr.buf, seq_hdr.length, "%d%0", &eventKey ) 768 != PARSE_OK ) { 769 error_respond( info, HTTP_BAD_REQUEST, event ); 770 771 return; 772 } 773 // get NT and NTS headers 774 if( httpmsg_find_hdr( event, HDR_NT, &nt_hdr ) == NULL || 775 httpmsg_find_hdr( event, HDR_NTS, &nts_hdr ) == NULL ) { 776 error_respond( info, HTTP_BAD_REQUEST, event ); 777 778 return; 779 } 780 // verify NT and NTS headers 781 if( memptr_cmp( &nt_hdr, "upnp:event" ) != 0 || 782 memptr_cmp( &nts_hdr, "upnp:propchange" ) != 0 ) { 783 error_respond( info, HTTP_PRECONDITION_FAILED, event ); 784 785 return; 786 } 787 // parse the content (should be XML) 788 if( !has_xml_content_type( event ) || 789 event->msg.length == 0 || 790 ( ixmlParseBufferEx( event->entity.buf, &ChangedVars ) ) != 791 IXML_SUCCESS ) { 792 error_respond( info, HTTP_BAD_REQUEST, event ); 793 794 return; 795 } 796 797 HandleLock( ); 798 799 // get client info 800 if( GetClientHandleInfo( &client_handle, &handle_info ) != HND_CLIENT ) { 801 error_respond( info, HTTP_PRECONDITION_FAILED, event ); 802 HandleUnlock( ); 803 ixmlDocument_free( ChangedVars ); 804 805 return; 806 } 807 // get subscription based on SID 808 if( ( subscription_1 = GetClientSubActualSID( handle_info->ClientSubList, 809 &sid ) ) == NULL ) { 810 if( eventKey == 0 ) { 811 // wait until we've finished processing a subscription 812 // (if we are in the middle) 813 // this is to avoid mistakenly rejecting the first event if we 814 // receive it before the subscription response 815 HandleUnlock( ); 816 817 // try and get Subscription Lock 818 // (in case we are in the process of subscribing) 819 SubscribeLock( ); 820 821 // get HandleLock again 822 HandleLock( ); 823 824 if( GetClientHandleInfo( &client_handle, &handle_info ) 825 != HND_CLIENT ) { 826 error_respond( info, HTTP_PRECONDITION_FAILED, event ); 827 SubscribeUnlock( ); 828 HandleUnlock( ); 829 ixmlDocument_free( ChangedVars ); 830 831 return; 832 } 833 834 if( ( subscription_1 = 835 GetClientSubActualSID( handle_info->ClientSubList, 836 &sid ) ) == NULL ) { 837 error_respond( info, HTTP_PRECONDITION_FAILED, event ); 838 SubscribeUnlock( ); 839 HandleUnlock( ); 840 ixmlDocument_free( ChangedVars ); 841 842 return; 843 } 844 845 SubscribeUnlock( ); 846 } else { 847 error_respond( info, HTTP_PRECONDITION_FAILED, event ); 848 HandleUnlock( ); 849 ixmlDocument_free( ChangedVars ); 850 851 return; 852 } 853 } 854 855 error_respond( info, HTTP_OK, event ); // success 856 857 // fill event struct 858 strcpy( event_struct.Sid, subscription_1->sid ); 859 event_struct.EventKey = eventKey; 860 event_struct.ChangedVariables = ChangedVars; 861 862 // copy callback 863 callback = handle_info->Callback; 864 cookie = handle_info->Cookie; 865 866 HandleUnlock( ); 867 868 // make callback with event struct 869 // In future, should find a way of mainting 870 // that the handle is not unregistered in the middle of a 871 // callback 872 callback( UPNP_EVENT_RECEIVED, &event_struct, cookie ); 873 874 ixmlDocument_free( ChangedVars ); 875} 876 877#endif // INCLUDE_CLIENT_APIS 878#endif // EXCLUDE_GENA 879