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 SipSubscriptionMgr::updateDialogInfo(const SipMessage& subscribeRequest, UtlString& resourceId, UtlString& eventTypeKey, UtlString& eventType, UtlString& subscribeDialogHandle, UtlBoolean& isNew, UtlBoolean& isSubscriptionExpired, SipMessage& subscribeResponse, SipSubscribeServerEventHandler& handler) { isNew = FALSE; UtlBoolean subscriptionSucceeded = FALSE; UtlString dialogHandle; subscribeRequest.getDialogHandle(dialogHandle); SubscriptionServerState* state = NULL; int expiration = -1; isSubscriptionExpired = TRUE; // Double check the sanity of the class attributes if(mMaxExpiration < mMinExpiration) { // This is an error case. Switch the values so that we do not // run into any negative expiration times. int tmp = mMaxExpiration; mMaxExpiration = mMinExpiration; mMinExpiration = tmp; OsSysLog::add(FAC_SIP, PRI_WARNING, "Swapping values as mMinExpiration => %d is greater than mMaxExpiration => %d", mMinExpiration, mMaxExpiration); } if(mMaxExpiration < mDefaultExpiration) { // This is an error case. Switch the values so that we do not // run into any negative expiration times. int tmp = mMaxExpiration; mMaxExpiration = mDefaultExpiration; mDefaultExpiration = tmp; OsSysLog::add(FAC_SIP, PRI_WARNING, "Swapping values as mDefaultExpiration => %d is greater than mMaxExpiration => %d", mDefaultExpiration, mMaxExpiration); } // Set the expires period randomly int spreadFloor = mMinExpiration*2; if(!subscribeRequest.getExpiresField(&expiration)) { // no expires field // spread it between the default expiration and max allowed expiration expiration = ( (rand() % (mMaxExpiration - mDefaultExpiration)) + mDefaultExpiration); } else if ( expiration >= mMaxExpiration ) { if (mMaxExpiration > spreadFloor) { // - spread it between the spreadFloor and the max allowed expiration expiration = ( (rand() % (mMaxExpiration - spreadFloor)) + spreadFloor); } else { // Max Expiration is smaller than the spreadFloor, hence // spread it between the min and the max allowed expiration expiration = ( (rand() % (mMaxExpiration - mMinExpiration)) + mMinExpiration); } } else if ( expiration > spreadFloor ) { // a normal (long) expiration // - spread it between the spreadFloor and the longest they asked for expiration = ( (rand() % (expiration - spreadFloor)) + spreadFloor); } else if ( expiration > mMinExpiration ) { // a short but greater than minimum expiration // - spread it between the min and the longest they asked for expiration = ( (rand() % (expiration - mMinExpiration)) + mMinExpiration); } // Cases where the expiration is less than the min value is handled below. // If this is an early dialog we need to make it an established dialog. if(SipDialog::isEarlyDialog(dialogHandle)) { UtlString establishedDialogHandle; if(mDialogMgr.getEstablishedDialogHandleFor(dialogHandle, establishedDialogHandle)) { OsSysLog::add(FAC_SIP, PRI_WARNING, "Incoming early SUBSCRIBE dialog: %s matches established dialog: %s", dialogHandle.data(), establishedDialogHandle.data()); } // make up a To tag and set it UtlString toTag; CallId::getNewTag(dialogHandle.data(), toTag); // Acceptable expiration, create a subscription and dialog if(expiration >= mMinExpiration || expiration == 0 || // :WORKAROUND: Also allow expiration == 1, to support the // 1-second subscriptions the pick-up agent makes because // current Snom phones do not respond to 0-second subscriptions. // See XPB-399 and ENG-319. expiration == 1) { // Call the event-specific function to determine the resource ID // and event type for this SUBSCRIBE. handler.getKeys(subscribeRequest, resourceId, eventTypeKey, eventType); // Create a dialog and subscription state even if // the expiration is zero as we need the dialog info // to route the one-time NOTIFY. The immediately // expired dialog will be garbage collected. SipMessage* subscribeCopy = new SipMessage(subscribeRequest); subscribeCopy->setToFieldTag(toTag); // Re-get the dialog handle now that the To tag is set subscribeCopy->getDialogHandle(dialogHandle); // Create the dialog mDialogMgr.createDialog(*subscribeCopy, FALSE, dialogHandle); isNew = TRUE; // Create a subscription state state = new SubscriptionServerState(); *((UtlString*) state) = dialogHandle; state->mEventTypeKey = eventTypeKey; state->mpLastSubscribeRequest = subscribeCopy; state->mResourceId = resourceId; subscribeCopy->getAcceptField(state->mAcceptHeaderValue); long now = OsDateTime::getSecsSinceEpoch(); state->mExpirationDate = now + expiration; // TODO: currently the SipSubsribeServer does not handle timeout // events to send notifications that the subscription has ended. // So we do not set a timer at the end of the subscription state->mpExpirationTimer = NULL; // Create the index by resourceId and eventTypeKey key SubscriptionServerStateIndex* stateKey = new SubscriptionServerStateIndex; *((UtlString*) stateKey) = resourceId; stateKey->append(eventTypeKey); stateKey->mpState = state; subscribeResponse.setResponseData(subscribeCopy, SIP_ACCEPTED_CODE, SIP_ACCEPTED_TEXT, NULL); subscribeResponse.setExpiresField(expiration); subscribeCopy->getDialogHandle(subscribeDialogHandle); lock(); mSubscriptionStatesByDialogHandle.insert(state); mSubscriptionStateResourceIndex.insert(stateKey); if (OsSysLog::willLog(FAC_SIP, PRI_DEBUG)) { UtlString requestContact; subscribeRequest.getContactField(0, requestContact); OsSysLog::add(FAC_SIP, PRI_DEBUG, "SipSubscriptionMgr::updateDialogInfo insert early-dialog subscription for dialog handle '%s', key '%s', contact '%s', mExpirationDate %ld", state->data(), stateKey->data(), requestContact.data(), state->mExpirationDate); } // Not safe to touch these after we unlock stateKey = NULL; state = NULL; subscribeCopy = NULL; unlock(); subscriptionSucceeded = TRUE; // One time subscribe? isSubscriptionExpired = expiration == 0; } // Expiration too small else { // Set expiration too small error subscribeResponse.setResponseData(&subscribeRequest, SIP_TOO_BRIEF_CODE, SIP_SUB_TOO_BRIEF_TEXT); subscribeResponse.setMinExpiresField(mMinExpiration); isSubscriptionExpired = TRUE; } } // Not an early dialog handle -- The dialog for this message should already exist else { // Acceptable expiration, create a subscription and dialog if(expiration > mMinExpiration || expiration == 0) { // Update the dialog state mDialogMgr.updateDialog(subscribeRequest, dialogHandle); // Get the subscription state and update that // TODO: This assumes that no one reuses the same dialog // to subscribe to more than one event type. mSubscriptionStatesByDialogHandle // will need to be changed to a HashBag and we will need to // search through to find a matching event type lock(); state = (SubscriptionServerState*) mSubscriptionStatesByDialogHandle.find(&dialogHandle); if(state) { // Update the expiration time. long now = OsDateTime::getSecsSinceEpoch(); state->mExpirationDate = now + expiration; // Record this SUBSCRIBE as the latest SUBSCRIBE request. if(state->mpLastSubscribeRequest) { delete state->mpLastSubscribeRequest; } state->mpLastSubscribeRequest = new SipMessage(subscribeRequest); subscribeRequest.getAcceptField(state->mAcceptHeaderValue); // Set our Contact to the same request URI that came in UtlString contact; subscribeRequest.getRequestUri(&contact); // Add the angle brackets to Contact, since it is a name-addr. Url url(contact); url.includeAngleBrackets(); contact = url.toString(); subscribeResponse.setResponseData(&subscribeRequest, SIP_ACCEPTED_CODE, SIP_ACCEPTED_TEXT, contact); subscribeResponse.setExpiresField(expiration); subscriptionSucceeded = TRUE; isSubscriptionExpired = FALSE; subscribeDialogHandle = dialogHandle; // Set the resource information so our caller can generate a NOTIFY. resourceId = state->mResourceId; eventTypeKey = state->mEventTypeKey; // Unfortuantely, we don't record the eventType separately. eventType = state->mEventTypeKey; } // No state, but SUBSCRIBE had a to-tag. else { // Unknown subscription. subscribeResponse.setResponseData(&subscribeRequest, SIP_BAD_SUBSCRIPTION_CODE, SIP_BAD_SUBSCRIPTION_TEXT); } unlock(); } // Expiration too small else { // Set expiration too small error subscribeResponse.setResponseData(&subscribeRequest, SIP_TOO_BRIEF_CODE, SIP_SUB_TOO_BRIEF_TEXT); subscribeResponse.setMinExpiresField(mMinExpiration); isSubscriptionExpired = isExpired(dialogHandle); } } return(subscriptionSucceeded); }