UtlBoolean SipSubscribeServer::handleSubscribe(const SipMessage& subscribeRequest) { UtlBoolean handledSubscribe = FALSE; UtlString eventName; subscribeRequest.getEventField(&eventName, NULL); // Not modifying the SubscribeServerEventData, just reading it lockForRead(); // Get the event specific handler and information SubscribeServerEventData* eventPackageInfo = (SubscribeServerEventData*) mEventDefinitions.find(&eventName); // We handle this event type if(eventPackageInfo) { handledSubscribe = TRUE; UtlString resourceId; UtlString eventTypeKey, eventType; SipSubscribeServerEventHandler* handler = eventPackageInfo->mpEventSpecificHandler; // Get the keys used to identify the event state content handler->getKeys(subscribeRequest, resourceId, eventTypeKey, eventType); SipMessage subscribeResponse; // Check if authenticated (or if it needs to be authenticated) if(handler->isAuthenticated(subscribeRequest, resourceId, eventTypeKey, subscribeResponse)) { // Check if authorized (or if authorization is required) if(handler->isAuthorized(subscribeRequest, resourceId, eventTypeKey, subscribeResponse)) { // The subscription is allowed, so update the // subscription state. Set the To field tag if // this request initiated the dialog UtlString subscribeDialogHandle; UtlBoolean isNewDialog; UtlBoolean isExpiredSubscription; eventPackageInfo->mpEventSpecificSubscriptionMgr->updateDialogInfo( subscribeRequest, resourceId, eventTypeKey, getMessageQueue(), subscribeDialogHandle, isNewDialog, isExpiredSubscription, subscribeResponse); // Send the response ASAP to minimize resend handling of request eventPackageInfo->mpEventSpecificUserAgent->send(subscribeResponse); // Build a NOTIFY SipMessage notifyRequest; // Set the dialog information eventPackageInfo->mpEventSpecificSubscriptionMgr->getNotifyDialogInfo(subscribeDialogHandle, notifyRequest); // Set the NOTIFY content UtlString acceptHeaderValue; subscribeRequest.getAcceptField(acceptHeaderValue); handler->getNotifyContent(resourceId, eventTypeKey, eventType, *(eventPackageInfo->mpEventSpecificContentMgr), acceptHeaderValue, notifyRequest); // Send the notify request eventPackageInfo->mpEventSpecificUserAgent->send(notifyRequest); } // Not authorized else { // Send the response eventPackageInfo->mpEventSpecificUserAgent->send(subscribeResponse); } } // Not authenticated else { // Send the response eventPackageInfo->mpEventSpecificUserAgent->send(subscribeResponse); } } // We should not have received SUBSCRIBE requests for this event type // This event type has not been enabled in this SubscribeServer else { OsSysLog::add(FAC_SIP, PRI_ERR, "SipSubscribeServer::handleSubscribe event type: %s not enabled", eventName.data()); SipMessage eventTypeNotHandled; eventTypeNotHandled.setResponseData(&subscribeRequest, SIP_BAD_EVENT_CODE, SIP_BAD_EVENT_TEXT); mpDefaultUserAgent->send(eventTypeNotHandled); } unlockForRead(); return(handledSubscribe); }
UtlBoolean SipSubscribeServer::handleSubscribe(const SipMessage& subscribeRequest) { UtlBoolean handledSubscribe = FALSE; UtlString eventName; subscribeRequest.getEventField(&eventName, NULL); // Not modifying the SubscribeServerEventData, just reading it lockForRead(); // Get the event specific handler and information SubscribeServerEventData* eventPackageInfo = dynamic_cast <SubscribeServerEventData*> (mEventDefinitions.find(&eventName)); // We handle this event type if (eventPackageInfo) { handledSubscribe = TRUE; SipSubscribeServerEventHandler* handler = eventPackageInfo->mpEventSpecificHandler; SipMessage subscribeResponse; // Check if authenticated (or if it needs to be authenticated) if (handler->isAuthenticated(subscribeRequest, subscribeResponse)) { // Check if authorized (or if authorization is required) if (handler->isAuthorized(subscribeRequest, subscribeResponse)) { // The subscription is allowed, so update the // subscription state. Set the To field tag if // this request initiated the dialog UtlString subscribeDialogHandle; UtlBoolean isNewDialog; UtlBoolean isExpiredSubscription; UtlString resourceId, eventTypeKey, eventType; eventPackageInfo->mpEventSpecificSubscriptionMgr-> updateDialogInfo( subscribeRequest, resourceId, eventTypeKey, eventType, subscribeDialogHandle, isNewDialog, isExpiredSubscription, subscribeResponse, // The event-specific handler provides a getKeys method // which is used to determine the resource ID // and event type if this is a new subscription. *handler); // Build a NOTIFY SipMessage notifyRequest; // Set the dialog information into the NOTIFY. // Note that the dialog can have ended by now, because of // a race condition with the processing of dialog-ending // messages from the outside world. if (eventPackageInfo->mpEventSpecificSubscriptionMgr-> getNotifyDialogInfo(subscribeDialogHandle, notifyRequest, "active;expires=%ld")) { // We still have record of the dialog, so the // NOTIFY headers were set. // Set the NOTIFY content UtlString acceptHeaderValue; if (!subscribeRequest.getAcceptField(acceptHeaderValue)) { // No Accept header seen, set special value allowing any // content type. acceptHeaderValue = SipPublishContentMgr::acceptAllTypes; } // Note that since this NOTIFY is due to a SUBSCRIBE, // it should contain 'full' content. Hence, // the fullState parameter of getNotifyContent is TRUE, // and is not taken from // eventPackageInfo->mEventSpecificFullState. UtlString availableMediaTypes; if (handler->getNotifyContent(resourceId, eventTypeKey, eventType, *(eventPackageInfo->mpEventSpecificContentMgr), acceptHeaderValue, notifyRequest, TRUE, &availableMediaTypes)) { // Update the NOTIFY content if required for this event type. // Sets 'version' and 'eventTypeKey'. int version; eventPackageInfo->mpEventSpecificSubscriptionMgr-> updateNotifyVersion(eventPackageInfo->mpEventSpecificContentVersionCallback, notifyRequest, version, eventTypeKey); // Update the saved record of the NOTIFY CSeq and the // XML version number for the specified eventTypeKey, // as needed by the subscription manager. // In practice, this is only used by SipPersistentSubscriptionMgr // to write the NOTIFY Cseq and XML version into the IMDB. eventPackageInfo->mpEventSpecificSubscriptionMgr-> updateVersion(notifyRequest, version, eventTypeKey); // Send the NOTIFY request setContact(¬ifyRequest); eventPackageInfo->mpEventSpecificUserAgent->send(notifyRequest); if (OsSysLog::willLog(FAC_SIP, PRI_INFO)) { UtlString requestContact; subscribeRequest.getContactField(0, requestContact); OsSysLog::add(FAC_SIP, PRI_INFO, "SipSubscribeServer::handleSubscribe: %s has setup subscription to %s, eventTypeKey %s", requestContact.data(), resourceId.data(), eventTypeKey.data()); } } else { // No content was available, so the subscription fails. // Determine the reason and set the response code. if (availableMediaTypes.isNull()) { // No MIME types are available, so the resource does not exist. subscribeResponse.setResponseFirstHeaderLine(SIP_PROTOCOL_VERSION, SIP_NOT_FOUND_CODE, SIP_NOT_FOUND_TEXT); } else { // MIME types are available, so the resource exists. subscribeResponse.setResponseFirstHeaderLine(SIP_PROTOCOL_VERSION, SIP_BAD_MEDIA_CODE, SIP_BAD_MEDIA_TEXT); subscribeResponse.setAcceptField(availableMediaTypes); } // Destroy the subscription. eventPackageInfo->mpEventSpecificSubscriptionMgr-> endSubscription(subscribeDialogHandle, SipSubscriptionMgr::subscriptionTerminated); // Do not send the NOTIFY. } } else { // Oops, the subscription was destroyed while we looked. OsSysLog::add(FAC_SIP, PRI_WARNING, "SipSubscribeServer::handleSubscribe " "subscription '%s' vanished while being processed", subscribeDialogHandle.data()); } // Send the response ASAP to minimize resending. setContact(&subscribeResponse); eventPackageInfo->mpEventSpecificUserAgent->send(subscribeResponse); } // Not authorized else { // Send the response that was prepared by ::isAuthorized(). setContact(&subscribeResponse); eventPackageInfo->mpEventSpecificUserAgent->send(subscribeResponse); } } // Not authenticated else { // Send the response that was prepared by ::isAuthenticated(). setContact(&subscribeResponse); eventPackageInfo->mpEventSpecificUserAgent->send(subscribeResponse); } } // We should not have received SUBSCRIBE requests for this event type. // This event type has not been enabled in this SubscribeServer. else { OsSysLog::add(FAC_SIP, PRI_ERR, "SipSubscribeServer::handleSubscribe event type: %s not enabled", eventName.data()); SipMessage eventTypeNotHandled; eventTypeNotHandled.setResponseData(&subscribeRequest, SIP_BAD_EVENT_CODE, SIP_BAD_EVENT_TEXT); setContact(&eventTypeNotHandled); mpDefaultUserAgent->send(eventTypeNotHandled); } unlockForRead(); return(handledSubscribe); }
// Basic server functionality test. // Checks that server answers SUBSCRIBES, sends NOTIFY, sends NOTIFY // when content changes. void basicSubscriptionTest() { // Verify that authentication and authorization are // disabled by default. { SipSubscribeServerEventHandler* eventHandler = subServerp->getEventHandler(eventName); CPPUNIT_ASSERT(eventHandler); SipMessage bogusSubscribeRequest; SipMessage bogusSubscribeResponse; CPPUNIT_ASSERT(eventHandler->isAuthenticated(bogusSubscribeRequest, bogusSubscribeResponse)); CPPUNIT_ASSERT(eventHandler->isAuthorized(bogusSubscribeRequest, bogusSubscribeResponse)); } // Send a SUBSCRIBE to the notifier. SipMessage mwiSubscribeRequest; { UtlString c; CallId::getNewCallId(c); mwiSubscribeRequest.setSubscribeData(notifier_aor, // request URI subscriber_name_addr, // From notifier_name_addr, // To c, // Call-Id 0, // CSeq eventName, // Event mwiMimeType, // Accept NULL, // Event id subscriber_name_addr, // Contact NULL, // Route 3600 // Expires ); } CPPUNIT_ASSERT(subscriberUserAgentp->send(mwiSubscribeRequest)); // We should get a 202 response and a NOTIFY request in the queue OsTime messageTimeout(1, 0); // 1 second { const SipMessage* subscribeResponse; const SipMessage* notifyRequest; runListener(incomingClientMsgQueue, *subscriberUserAgentp, messageTimeout, messageTimeout, notifyRequest, subscribeResponse, SIP_OK_CODE, FALSE, 0, NULL); // We should have received a SUBSCRIBE response and a NOTIFY request. CPPUNIT_ASSERT(subscribeResponse); CPPUNIT_ASSERT(notifyRequest); // Check that the response code and CSeq method in the // subscribe response are OK. { CPPUNIT_ASSERT(subscribeResponse->getResponseStatusCode() == SIP_ACCEPTED_CODE); UtlString subscribeMethod; subscribeResponse->getCSeqField(NULL, &subscribeMethod); ASSERT_STR_EQUAL(SIP_SUBSCRIBE_METHOD, subscribeMethod.data()); } // Check that the method in the notify request is OK. { UtlString notifyMethod; notifyRequest->getRequestMethod(¬ifyMethod); ASSERT_STR_EQUAL(SIP_NOTIFY_METHOD, notifyMethod.data()); } // Check that the Event header in the NOTIFY is the same as the // one in the SUBSCRIBE. { UtlString notifyEventHeader; UtlString subscribeEventHeader; notifyRequest->getEventField(notifyEventHeader); mwiSubscribeRequest.getEventField(subscribeEventHeader); ASSERT_STR_EQUAL(subscribeEventHeader, notifyEventHeader); } // The NOTIFY should have no body because none has been published yet. { const HttpBody* bodyPtr = notifyRequest->getBody(); CPPUNIT_ASSERT(bodyPtr == NULL); } // The Contact in the subscribe response should be the notifier. ASSERT_STR_EQUAL(notifier_contact_name_addr, subscribeResponse-> getHeaderValue(0, SIP_CONTACT_FIELD)); // The Contact in the NOTIFY should be the notifier. CPPUNIT_ASSERT(notifyRequest-> getHeaderValue(0, SIP_CONTACT_FIELD)); ASSERT_STR_EQUAL(notifier_contact_name_addr, notifyRequest-> getHeaderValue(0, SIP_CONTACT_FIELD)); } // Publish some content (mwiStateString) for this resourceID { HttpBody* newMwiBodyPtr = new HttpBody(mwiStateString, strlen(mwiStateString), mwiMimeType); SipPublishContentMgr* publishMgr = subServerp->getPublishMgr(eventName); CPPUNIT_ASSERT(publishMgr); publishMgr->publish(notifier_resource_id, eventName, eventName, 1, &newMwiBodyPtr); } // Should get a NOTIFY queued up { const SipMessage* subscribeResponse; const SipMessage* secondNotify; runListener(incomingClientMsgQueue, *subscriberUserAgentp, messageTimeout, messageTimeout, secondNotify, subscribeResponse, SIP_OK_CODE, FALSE, 0, NULL); CPPUNIT_ASSERT(secondNotify); CPPUNIT_ASSERT(subscribeResponse == NULL); // Check that the body of the NOTIFY is what we expect (mwiStateString). { const HttpBody* secondNotifyBody = secondNotify->getBody(); CPPUNIT_ASSERT(secondNotifyBody); ssize_t notifyBodySize; const char* notifyBodyBytes; secondNotifyBody->getBytes(¬ifyBodyBytes, ¬ifyBodySize); CPPUNIT_ASSERT(notifyBodyBytes); ASSERT_STR_EQUAL(mwiStateString, notifyBodyBytes); } // Check that the Dialog Manager reports that the dialog handle is OK. { UtlString secondNotifyDialogHandle; secondNotify->getDialogHandle(secondNotifyDialogHandle); CPPUNIT_ASSERT(!secondNotifyDialogHandle.isNull()); CPPUNIT_ASSERT(dialogMgrp->dialogExists(secondNotifyDialogHandle)); CPPUNIT_ASSERT(dialogMgrp->countDialogs() == 1); } } // Create a new one-time SUBSCRIBE SipMessage oneTimeMwiSubscribeRequest; { UtlString c; CallId::getNewCallId(c); oneTimeMwiSubscribeRequest. setSubscribeData(notifier_aor, // request URI subscriber_name_addr, // From notifier_name_addr, // To c, // Call-Id 0, // CSeq eventName, // Event mwiMimeType, // Accept NULL, // Event id subscriber_name_addr, // Contact NULL, // Route 0 // Expires ); } CPPUNIT_ASSERT(subscriberUserAgentp->send(oneTimeMwiSubscribeRequest)); { const SipMessage* oneTimeNotifyRequest; const SipMessage* oneTimeSubscribeResponse; runListener(incomingClientMsgQueue, *subscriberUserAgentp, messageTimeout, messageTimeout, oneTimeNotifyRequest, oneTimeSubscribeResponse, SIP_OK_CODE, FALSE, 0, NULL); // Validate the one time subscribe response and notify request CPPUNIT_ASSERT(oneTimeSubscribeResponse); CPPUNIT_ASSERT(oneTimeNotifyRequest); { CPPUNIT_ASSERT(oneTimeSubscribeResponse->getResponseStatusCode() == SIP_ACCEPTED_CODE); UtlString oneTimeSubscribeMethod; oneTimeSubscribeResponse->getCSeqField(NULL, &oneTimeSubscribeMethod); ASSERT_STR_EQUAL(SIP_SUBSCRIBE_METHOD, oneTimeSubscribeMethod.data()); UtlString oneTimeSubscribeEventHeader; oneTimeSubscribeResponse->getEventField(oneTimeSubscribeEventHeader); // The Event: header never appears in responses -- see RFC 3265 section 7.2. // The "R" in the "where" column means that "Event" appears only in // requests -- see RFC 3261 table 2 and page 160. ASSERT_STR_EQUAL("", oneTimeSubscribeEventHeader); } { UtlString oneTimeNotifyMethod; oneTimeNotifyRequest->getRequestMethod(&oneTimeNotifyMethod); ASSERT_STR_EQUAL(SIP_NOTIFY_METHOD, oneTimeNotifyMethod.data()); UtlString oneTimeNotifyEventHeader; oneTimeNotifyRequest->getEventField(oneTimeNotifyEventHeader); // The Event: header should appear in the NOTIFY. ASSERT_STR_EQUAL(SIP_EVENT_MESSAGE_SUMMARY, oneTimeNotifyEventHeader); } { const HttpBody* oneTimeBodyPtr = oneTimeNotifyRequest->getBody(); CPPUNIT_ASSERT(oneTimeBodyPtr != NULL); const char* oneTimeBodyString; ssize_t oneTimeNotifyBodySize; oneTimeBodyPtr->getBytes(&oneTimeBodyString, &oneTimeNotifyBodySize); ASSERT_STR_EQUAL(mwiStateString, oneTimeBodyString); } { UtlString oneTimeNotifyDialogHandle; oneTimeNotifyRequest->getDialogHandle(oneTimeNotifyDialogHandle); CPPUNIT_ASSERT(!oneTimeNotifyDialogHandle.isNull()); CPPUNIT_ASSERT(dialogMgrp->dialogExists(oneTimeNotifyDialogHandle)); CPPUNIT_ASSERT(dialogMgrp->countDialogs() == 2); long now = OsDateTime::getSecsSinceEpoch(); subMgrp->removeOldSubscriptions(now + 1); // The one time subscription should get garbage collected // leaving only the persistant 3600 second subscription CPPUNIT_ASSERT(dialogMgrp->countDialogs() == 1); } } }