void gena_process_unsubscribe_request( SOCKINFO *info, http_message_t *request) { Upnp_SID sid; service_info *service; struct Handle_Info *handle_info; UpnpDevice_Handle device_handle; memptr temp_hdr; membuffer event_url_path; /* if a CALLBACK or NT header is present, then it is an error */ if( httpmsg_find_hdr( request, HDR_CALLBACK, NULL ) != NULL || httpmsg_find_hdr( request, HDR_NT, NULL ) != NULL ) { error_respond( info, HTTP_BAD_REQUEST, request ); return; } /* get SID */ if( httpmsg_find_hdr( request, HDR_SID, &temp_hdr ) == NULL || temp_hdr.length > SID_SIZE ) { error_respond( info, HTTP_PRECONDITION_FAILED, request ); return; } memcpy( sid, temp_hdr.buf, temp_hdr.length ); sid[temp_hdr.length] = '\0'; /* lookup service by eventURL */ membuffer_init( &event_url_path ); if( membuffer_append( &event_url_path, request->uri.pathquery.buff, request->uri.pathquery.size ) != 0 ) { error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request ); return; } HandleLock(); if( GetDeviceHandleInfoForPath(event_url_path.buf, info->foreign_sockaddr.ss_family, &device_handle, &handle_info, &service) != HND_DEVICE ) { error_respond( info, HTTP_PRECONDITION_FAILED, request ); membuffer_destroy( &event_url_path ); HandleUnlock(); return; } membuffer_destroy( &event_url_path ); /* validate service */ if( service == NULL || !service->active || GetSubscriptionSID( sid, service ) == NULL ) { error_respond( info, HTTP_PRECONDITION_FAILED, request ); HandleUnlock(); return; } RemoveSubscriptionSID(sid, service); error_respond(info, HTTP_OK, request); /* success */ HandleUnlock(); }
/*! * \brief Thread job to Notify a control point. * * It validates the subscription and copies the subscription. Also make sure * that events are sent in order. * * \note calls the genaNotify to do the actual work. */ static void genaNotifyThread( /*! [in] notify thread structure containing all the headers and property set info. */ void *input) { subscription *sub; service_info *service; subscription sub_copy; notify_thread_struct *in = (notify_thread_struct *) input; int return_code; struct Handle_Info *handle_info; /* This should be a HandleLock and not a HandleReadLock otherwise if there * is a lot of notifications, then multiple threads will acquire a read * lock and the thread which sends the notification will be blocked forever * on the HandleLock at the end of this function. */ /*HandleReadLock(); */ HandleLock(); /* validate context */ if (GetHandleInfo(in->device_handle, &handle_info) != HND_DEVICE) { free_notify_struct(in); HandleUnlock(); return; } if (!(service = FindServiceId(&handle_info->ServiceTable, in->servId, in->UDN)) || !service->active || !(sub = GetSubscriptionSID(in->sid, service)) || copy_subscription(sub, &sub_copy) != HTTP_SUCCESS) { free_notify_struct(in); HandleUnlock(); return; } HandleUnlock(); /* send the notify */ return_code = genaNotify(in->headers, in->propertySet, &sub_copy); freeSubscription(&sub_copy); HandleLock(); if (GetHandleInfo(in->device_handle, &handle_info) != HND_DEVICE) { free_notify_struct(in); HandleUnlock(); return; } /* validate context */ if (!(service = FindServiceId(&handle_info->ServiceTable, in->servId, in->UDN)) || !service->active || !(sub = GetSubscriptionSID(in->sid, service))) { free_notify_struct(in); HandleUnlock(); return; } sub->ToSendEventKey++; if (sub->ToSendEventKey < 0) /* wrap to 1 for overflow */ sub->ToSendEventKey = 1; /* Remove head of event queue. Possibly activate next */ { ListNode *node = ListHead(&sub->outgoing); if (node) ListDelNode(&sub->outgoing, node, 1); if (ListSize(&sub->outgoing) > 0) { ThreadPoolJob *job; ListNode *node = ListHead(&sub->outgoing); job = (ThreadPoolJob*)node->item; /* The new head of queue should not have already been added to the pool, else something is very wrong */ assert(job->jobId != STALE_JOBID); ThreadPoolAdd(&gSendThreadPool, job, NULL); job->jobId = STALE_JOBID; } } if (return_code == GENA_E_NOTIFY_UNACCEPTED_REMOVE_SUB) RemoveSubscriptionSID(in->sid, service); free_notify_struct(in); HandleUnlock(); }
void gena_process_subscription_renewal_request( SOCKINFO *info, http_message_t *request) { Upnp_SID sid; subscription *sub; int time_out = 1801; service_info *service; struct Handle_Info *handle_info; UpnpDevice_Handle device_handle; memptr temp_hdr; membuffer event_url_path; memptr timeout_hdr; /* if a CALLBACK or NT header is present, then it is an error */ if( httpmsg_find_hdr( request, HDR_CALLBACK, NULL ) != NULL || httpmsg_find_hdr( request, HDR_NT, NULL ) != NULL ) { error_respond( info, HTTP_BAD_REQUEST, request ); return; } /* get SID */ if( httpmsg_find_hdr( request, HDR_SID, &temp_hdr ) == NULL || temp_hdr.length > SID_SIZE ) { error_respond( info, HTTP_PRECONDITION_FAILED, request ); return; } memcpy( sid, temp_hdr.buf, temp_hdr.length ); sid[temp_hdr.length] = '\0'; /* lookup service by eventURL */ membuffer_init( &event_url_path ); if( membuffer_append( &event_url_path, request->uri.pathquery.buff, request->uri.pathquery.size ) != 0 ) { error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request ); return; } HandleLock(); if (GetDeviceHandleInfoForPath(event_url_path.buf, info->foreign_sockaddr.ss_family, &device_handle, &handle_info, &service) != HND_DEVICE ) { error_respond( info, HTTP_PRECONDITION_FAILED, request ); membuffer_destroy( &event_url_path ); HandleUnlock(); return; } membuffer_destroy( &event_url_path ); /* get subscription */ if( service == NULL || !service->active || ( ( sub = GetSubscriptionSID( sid, service ) ) == NULL ) ) { error_respond( info, HTTP_PRECONDITION_FAILED, request ); HandleUnlock(); return; } UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, "Renew request: Number of subscriptions already: %d\n " "Max Subscriptions allowed:%d\n", service->TotalSubscriptions, handle_info->MaxSubscriptions ); /* too many subscriptions */ if( handle_info->MaxSubscriptions != -1 && service->TotalSubscriptions > handle_info->MaxSubscriptions ) { error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request ); RemoveSubscriptionSID( sub->sid, service ); HandleUnlock(); return; } /* set the timeout */ if( httpmsg_find_hdr( request, HDR_TIMEOUT, &timeout_hdr ) != NULL ) { if( matchstr( timeout_hdr.buf, timeout_hdr.length, "%iSecond-%d%0", &time_out ) == PARSE_OK ) { /*nothing */ } else if( memptr_cmp_nocase( &timeout_hdr, "Second-infinite" ) == 0 ) { time_out = -1; /* inifinite timeout */ } else { time_out = DEFAULT_TIMEOUT; /* default is > 1800 seconds */ } } /* replace infinite timeout with max timeout, if possible */ if( handle_info->MaxSubscriptionTimeOut != -1 ) { if( time_out == -1 || time_out > handle_info->MaxSubscriptionTimeOut ) { time_out = handle_info->MaxSubscriptionTimeOut; } } if( time_out == -1 ) { sub->expireTime = 0; } else { sub->expireTime = time( NULL ) + time_out; } if( respond_ok( info, time_out, sub, request ) != UPNP_E_SUCCESS ) { RemoveSubscriptionSID( sub->sid, service ); } HandleUnlock(); }
/*! * \brief Thread job to Notify a control point. * * It validates the subscription and copies the subscription. Also make sure * that events are sent in order. * * \note calls the genaNotify to do the actual work. */ static void genaNotifyThread( /*! [in] notify thread structure containing all the headers and property set info. */ void *input) { subscription *sub; service_info *service; subscription sub_copy; notify_thread_struct *in = (notify_thread_struct *) input; int return_code; struct Handle_Info *handle_info; ThreadPoolJob job; memset(&job, 0, sizeof(job)); /* This should be a HandleLock and not a HandleReadLock otherwise if there * is a lot of notifications, then multiple threads will acquire a read * lock and the thread which sends the notification will be blocked forever * on the HandleLock at the end of this function. */ /*HandleReadLock(); */ HandleLock(); /* validate context */ if (GetHandleInfo(in->device_handle, &handle_info) != HND_DEVICE) { free_notify_struct(in); HandleUnlock(); return; } if (!(service = FindServiceId(&handle_info->ServiceTable, in->servId, in->UDN)) || !service->active || !(sub = GetSubscriptionSID(in->sid, service)) || copy_subscription(sub, &sub_copy) != HTTP_SUCCESS) { free_notify_struct(in); HandleUnlock(); return; } #ifdef UPNP_ENABLE_NOTIFICATION_REORDERING /*If the event is out of order push it back to the job queue */ if (in->eventKey != sub->ToSendEventKey) { TPJobInit(&job, (start_routine) genaNotifyThread, input); TPJobSetFreeFunction(&job, (free_function) free_notify_struct); TPJobSetPriority(&job, MED_PRIORITY); /* Sleep a little before creating another thread otherwise if there is * a lot of notifications to send, the device will take 100% of the CPU * to create threads and push them back to the job queue. */ imillisleep(1); ThreadPoolAdd(&gSendThreadPool, &job, NULL); freeSubscription(&sub_copy); HandleUnlock(); return; } #endif HandleUnlock(); /* send the notify */ return_code = genaNotify(in->headers, in->propertySet, &sub_copy); freeSubscription(&sub_copy); HandleLock(); if (GetHandleInfo(in->device_handle, &handle_info) != HND_DEVICE) { free_notify_struct(in); HandleUnlock(); return; } /* validate context */ if (!(service = FindServiceId(&handle_info->ServiceTable, in->servId, in->UDN)) || !service->active || !(sub = GetSubscriptionSID(in->sid, service))) { free_notify_struct(in); HandleUnlock(); return; } sub->ToSendEventKey++; if (sub->ToSendEventKey < 0) /* wrap to 1 for overflow */ sub->ToSendEventKey = 1; if (return_code == GENA_E_NOTIFY_UNACCEPTED_REMOVE_SUB) RemoveSubscriptionSID(in->sid, service); free_notify_struct(in); HandleUnlock(); }