UtlBoolean SipDialogMgr::createDialog(const SipMessage& message, UtlBoolean messageIsFromLocalSide, const char* dialogHandle) { UtlBoolean createdDialog = FALSE; UtlString handle(dialogHandle ? dialogHandle : ""); // If the dialog handle was not set, get it from the message if(handle.isNull()) { message.getDialogHandle(handle); } // Check to see if the dialog exists if(dialogExists(handle) || earlyDialogExistsFor(handle)) { // Should not try to create a dialog for one that // already exists OsSysLog::add(FAC_SIP, PRI_ERR, "SipDialogMgr::createDialog called with handle: %s for existing dialog", handle.data()); } // Dialog needs to be created else { createdDialog = TRUE; SipDialog* dialog = new SipDialog(&message, messageIsFromLocalSide); lock(); mDialogs.insert(dialog); unlock(); } return(createdDialog); }
UtlBoolean SipDialogMgr::isLastLocalTransaction(const SipMessage& message, const char* dialogHandle) { UtlBoolean matchesTransaction = FALSE; UtlString handle(dialogHandle ? dialogHandle : ""); // If the dialog handle was not set, get it from the message if(handle.isNull()) { message.getDialogHandle(handle); } UtlString callId; UtlString fromTag; UtlString toTag; SipDialog::parseHandle(handle, callId, fromTag, toTag); lock(); // Looking for any dialog that matches this handle SipDialog* dialog = findDialog(handle, TRUE, // if established, match early dialog TRUE); // if early, match established dialog if(dialog && dialog->isTransactionLocallyInitiated(callId, fromTag, toTag) && dialog->isSameLocalCseq(message)) { matchesTransaction = TRUE; } unlock(); return(matchesTransaction); }
UtlBoolean SipDialogMgr::setNextLocalTransactionInfo(SipMessage& request, const char* method, const char* dialogHandle) { UtlBoolean requestSet = FALSE; UtlString dialogHandleString(dialogHandle ? dialogHandle : ""); if(dialogHandleString.isNull()) { request.getDialogHandle(dialogHandleString); } lock(); SipDialog* dialog = findDialog(dialogHandleString, FALSE, // If established only want exact match dialogs TRUE); // If message is from a prior transaction // when the dialog was in an early state // allow it to match an established // dialog if(dialog) { dialog->setRequestData(request, method); requestSet = TRUE; #ifdef TEST_PRINT UtlString dialogDump; dialog->toString(dialogDump); OsSysLog::add(FAC_SIP, PRI_DEBUG, "SipDialogMgr::setNextLocalTransactionInfo dialog: '%s'", dialogDump.data()); #endif } else { OsSysLog::add(FAC_SIP, PRI_ERR, "SipDialogMgr::setNextLocalTransactionInfo dialog not found for handle '%s'", dialogHandle); if (OsSysLog::willLog(FAC_SIP, PRI_DEBUG)) { SipDialog* dialog; UtlHashBagIterator iterator(mDialogs); while ((dialog = (SipDialog*) iterator())) { UtlString callId, localTag, remoteTag; dialog->getCallId(callId); dialog->getLocalTag(localTag); dialog->getRemoteTag(remoteTag); OsSysLog::add(FAC_SIP, PRI_DEBUG, "SipDialogMgr::setNextLocalTransactionInfo dialog call-id = '%s', local tag = '%s', remote tag = '%s'", callId.data(), localTag.data(), remoteTag.data()); } } } unlock(); return(requestSet); }
UtlBoolean SipSubscribeServer::handleNotifyResponse(const SipMessage& notifyResponse) { UtlBoolean handledNotifyResponse = FALSE; int responseCode = notifyResponse.getResponseStatusCode(); // Ignore provisional responses or success cases if(responseCode >= SIP_3XX_CLASS_CODE) { UtlString dialogHandle; notifyResponse.getDialogHandle(dialogHandle); // Not modifying the SubscribeServerEventData, just reading it lockForRead(); // Get the event specific handler and information SubscribeServerEventData* eventPackageInfo = NULL; UtlHashMapIterator iterator(mEventDefinitions); while((eventPackageInfo = (SubscribeServerEventData*) iterator())) { // End this subscription as we got an error response from // the NOTIFY request. // Returns TRUE if the SipSubscriptionMgr has this dialog handledNotifyResponse = eventPackageInfo->mpEventSpecificSubscriptionMgr->endSubscription( dialogHandle); if(handledNotifyResponse) { break; } } unlockForRead(); // Should not happen, first of all we should never get a // response which does not correspond to a request sent from // the SipUserAgent. Secondly, we should not get a response to // and event type that we do not support if(!handledNotifyResponse) { OsSysLog::add(FAC_SIP, PRI_ERR, "SipSubscribeServer::handleNotifyResponse NOTIFY response with no dialog. Handle: %s", dialogHandle.data()); } } // Provisional or 2XX class responses else { handledNotifyResponse = TRUE; } return(handledNotifyResponse); }
UtlBoolean SipDialog::isEarlyDialogFor(const SipMessage& message) const { UtlString handle; message.getDialogHandle(handle); UtlString callId; UtlString localTag; UtlString remoteTag; parseHandle(handle, callId, localTag, remoteTag); return(isEarlyDialogFor(callId, localTag, remoteTag)); }
enum SipDialogMgr::transactionSequence SipDialogMgr::isNewRemoteTransaction(const SipMessage& message) { enum transactionSequence ordering; UtlString handle; message.getDialogHandle(handle); UtlString callId; UtlString fromTag; UtlString toTag; SipDialog::parseHandle(handle, callId, fromTag, toTag); lock(); // Looking for any dialog that matches this handle SipDialog* dialog = findDialog(handle, TRUE, // if established, match early dialog TRUE); // if early, match established dialog if (dialog && dialog->isTransactionRemotelyInitiated(callId, fromTag, toTag)) { int messageCSeq; message.getCSeqField(&messageCSeq, NULL); int lastRemoteCSeq = dialog->getLastRemoteCseq(); ordering = messageCSeq < lastRemoteCSeq ? OUT_OF_ORDER : /** If this message was an exact duplicate of a previous message * (with the same CSeq and branch parameter), it would have been * absorbed earlier in processing. So we know the branch parameter * is different without having to remember the previous value. */ messageCSeq == lastRemoteCSeq ? LOOPED : IN_ORDER; } else { ordering = NO_DIALOG; } unlock(); return ordering; }
UtlBoolean SipDialogMgr::updateDialog(const SipMessage& message, const char* dialogHandle) { UtlString handle(dialogHandle ? dialogHandle : ""); // If the dialog handle was not set, get it from the message if(handle.isNull()) { message.getDialogHandle(handle); } lock(); SipDialog* dialog = findDialog(handle, TRUE, // if established handle, find early dialog FALSE); // do not want established dialogs for early handle if(dialog) { #ifdef TEST_PRINT UtlString dialogDump; dialog->toString(dialogDump); printf("SipDialogMgr::updateDialog dialog before:\n%s\n", dialogDump.data()); #endif dialog->updateDialogData(message); #ifdef TEST_PRINT dialog->toString(dialogDump); printf("SipDialogMgr::updateDialog dialog after:\n%s\n", dialogDump.data()); #endif } unlock(); return(dialog != NULL); }
// Send a NOTIFY to all subscribers to the given resourceId and eventTypeKey. UtlBoolean SipSubscribeServer::notifySubscribers(const char* resourceId, const char* eventTypeKey, const char* eventType, const char* reason) { OsSysLog::add(FAC_SIP, PRI_DEBUG, "SipSubscribeServer::notifySubscribers resourceId '%s', eventTypeKey '%s', eventType '%s', reason '%s'", resourceId, eventTypeKey, eventType, reason ? reason : "[null]"); UtlBoolean notifiedSubscribers = FALSE; UtlString eventName(eventType); lockForRead(); SubscribeServerEventData* eventData = dynamic_cast <SubscribeServerEventData*> (mEventDefinitions.find(&eventName)); // Get the event-specific info to find subscriptions interested in // this content. if (eventData) { int numSubscriptions = 0; SipMessage** notifyArray = NULL; UtlString** acceptHeaderValuesArray = NULL; // Select the subscriptionChange value to describe what we're doing // with the subscriptions. enum SipSubscriptionMgr::subscriptionChange change; { // Select the correct format for generating the Subscription-State value. UtlString* formatp = NULL; // We may need a temp UtlString. const char* format; if (reason == NULL) { format = "active;expires=%ld"; change = SipSubscriptionMgr::subscriptionContinues; } else if (strcmp(reason, terminationReasonSilent) == 0) { // Do not admit that the subscription is ending. format = "active;expires=%ld"; change = SipSubscriptionMgr::subscriptionTerminatedSilently; } else if (strcmp(reason, terminationReasonNone) == 0) { format = "terminated"; change = SipSubscriptionMgr::subscriptionTerminated; } else { // Allocate a UtlString and assemble the Subscription-State format in it. formatp = new UtlString(); formatp->append("terminated;reason="); formatp->append(reason); format = formatp->data(); change = SipSubscriptionMgr::subscriptionTerminated; } // Construct a NOTIFY (without body) for each subscription, containing // the dialog-related information about each subscription. eventData->mpEventSpecificSubscriptionMgr-> createNotifiesDialogInfo(resourceId, eventTypeKey, format, numSubscriptions, acceptHeaderValuesArray, notifyArray); OsSysLog::add(FAC_SIP, PRI_DEBUG, "SipSubscribeServer::notifySubscribers numSubscriptions for '%s' = %d", resourceId, numSubscriptions); // Free the temporary UtlString, if necessary. if (formatp) { delete formatp; } } // For each NOTIFY, add the subscription-related information and then // send it. for (int notifyIndex = 0; notifyIndex < numSubscriptions; notifyIndex++) { SipMessage* notify = notifyArray[notifyIndex]; // Check to see if the dialog information could be added. // (The subscription might have been destroyed between when // it was decided to respond to it, and when the dialog information // was retrieved.) UtlString callId; notify->getCallIdField(&callId); if (!callId.isNull()) { if (change != SipSubscriptionMgr::subscriptionTerminatedSilently) { // Fill in the NOTIFY request body/content eventData->mpEventSpecificHandler-> getNotifyContent(resourceId, eventTypeKey, eventType, *(eventData->mpEventSpecificContentMgr), *(acceptHeaderValuesArray[notifyIndex]), *notify, eventData->mEventSpecificFullState, NULL); // Call the application callback to edit the NOTIFY // content if that is required for this event type. // Also gets 'version' (if relevant) and 'savedEventTypeKey'. int version; UtlString savedEventTypeKey; eventData->mpEventSpecificSubscriptionMgr-> updateNotifyVersion(eventData->mpEventSpecificContentVersionCallback, *notify, version, savedEventTypeKey); // Update the saved record of the NOTIFY CSeq and the // XML version number for the specified savedEventTypeKey, // 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. eventData->mpEventSpecificSubscriptionMgr-> updateVersion(*notify, version, savedEventTypeKey); // Set the Contact header. setContact(notify); // Send the NOTIFY request. eventData->mpEventSpecificUserAgent->send(*notify); } if (change != SipSubscriptionMgr::subscriptionContinues) { // Remove the record of the subscription. UtlString dialogHandle; notify->getDialogHandle(dialogHandle); eventData->mpEventSpecificSubscriptionMgr-> endSubscription(dialogHandle, change); } } } // Free the NOTIFY requests and accept header field values. eventData->mpEventSpecificSubscriptionMgr-> freeNotifies(numSubscriptions, acceptHeaderValuesArray, notifyArray); } // event type not enabled else { OsSysLog::add(FAC_SIP, PRI_ERR, "SipSubscribeServer::notifySubscribers " "event type: %s not enabled - " "Why are we seeing a callback for this?", eventName.data()); } unlockForRead(); return notifiedSubscribers; }
/// Terminate all subscriptions and accept no new ones. void SipSubscribeServer::shutdown(const char* reason) { // If reason is NULL, use the default reason established by the constructor. if (!reason) { reason = mDefaultTermination; } OsSysLog::add(FAC_SIP, PRI_DEBUG, "SipSubscribeServer::shutdown reason '%s'", reason ? reason : "[null]"); lockForRead(); // Select the subscriptionChange value to describe what we're doing // with the subscriptions. enum SipSubscriptionMgr::subscriptionChange change; // Select the correct format for generating the Subscription-State value. UtlString* formatp = NULL; // We may need a temp UtlString. const char* format; if (reason == NULL) { format = "active;expires=%ld"; change = SipSubscriptionMgr::subscriptionContinues; } else if (strcmp(reason, terminationReasonSilent) == 0) { // Do not admit that the subscription is ending. format = "active;expires=%ld"; change = SipSubscriptionMgr::subscriptionTerminatedSilently; } else if (strcmp(reason, terminationReasonNone) == 0) { format = "terminated"; change = SipSubscriptionMgr::subscriptionTerminated; } else { // Allocate a UtlString and assemble the Subscription-State format in it. formatp = new UtlString(); formatp->append("terminated;reason="); formatp->append(reason); format = formatp->data(); change = SipSubscriptionMgr::subscriptionTerminated; } // For each event type that is registered, delete all the subscriptions. UtlHashBagIterator event_itor(mEventDefinitions); SubscribeServerEventData* eventData; while ((eventData = dynamic_cast <SubscribeServerEventData*> (event_itor()))) { // Unregister interest in SUBSCRIBE requests and NOTIFY // responses for this event type, so we do not service new subscriptions. eventData->mpEventSpecificUserAgent->removeMessageObserver(*(getMessageQueue())); int numSubscriptions = 0; SipMessage** notifyArray = NULL; UtlString** acceptHeaderValuesArray = NULL; UtlString** resourceIdArray = NULL; UtlString** eventTypeKeyArray = NULL; // :TODO: The four situations where NOTIFYs are generated should // be factored into a series of methods in // mpEventSpecificSubscriptionMgr that generate NOTIFYs // sequentially, and for each NOTIFY, call a common service // method that does the remaining operations and sends the // NOTIFY. // Construct a NOTIFY (without body) for every subscription, containing // the dialog-related information about each subscription. eventData->mpEventSpecificSubscriptionMgr-> createNotifiesDialogInfoEvent(static_cast <const UtlString&> (*eventData), format, numSubscriptions, acceptHeaderValuesArray, notifyArray, resourceIdArray, eventTypeKeyArray); OsSysLog::add(FAC_SIP, PRI_DEBUG, "SipSubscribeServer::shutdown eventType = '%s', numSubscriptions = %d", eventData->data(), numSubscriptions); // For each NOTIFY, add the subscription-related information and then // send it. for (int notifyIndex = 0; notifyIndex < numSubscriptions; notifyIndex++) { SipMessage* notify = notifyArray[notifyIndex]; // Check to see if the dialog information could be added. // (The subscription might have been destroyed between when // it was decided to respond to it, and when the dialog information // was retrieved.) UtlString callId; notify->getCallIdField(&callId); if (!callId.isNull()) { if (change != SipSubscriptionMgr::subscriptionTerminatedSilently) { // Fill in the NOTIFY request body/content eventData->mpEventSpecificHandler-> getNotifyContent(*(resourceIdArray[notifyIndex]), *(eventTypeKeyArray[notifyIndex]), *eventData, *(eventData->mpEventSpecificContentMgr), *(acceptHeaderValuesArray[notifyIndex]), *notify, eventData->mEventSpecificFullState, NULL); // Call the application callback to edit the NOTIFY // content if that is required for this event type. // Also gets 'version' (if relevant) and 'savedEventTypeKey'. int version; UtlString savedEventTypeKey; eventData->mpEventSpecificSubscriptionMgr-> updateNotifyVersion(eventData->mpEventSpecificContentVersionCallback, *notify, version, savedEventTypeKey); // Set the Contact header. setContact(notify); // Send the NOTIFY request. eventData->mpEventSpecificUserAgent->send(*notify); } // Remove the record of the subscription. UtlString dialogHandle; notify->getDialogHandle(dialogHandle); eventData->mpEventSpecificSubscriptionMgr-> endSubscription(dialogHandle, change); } } // Free the NOTIFY requests and accept header field values. SipSubscriptionMgr::freeNotifies(numSubscriptions, acceptHeaderValuesArray, notifyArray); // Free the resource and event type arrays. for (int index = 0; index < numSubscriptions; index++) { delete resourceIdArray[index]; delete eventTypeKeyArray[index]; } delete[] resourceIdArray; delete[] eventTypeKeyArray; // Remove eventData from mEventDefinitions. mEventDefinitions.removeReference(eventData); delete eventData; } unlockForRead(); // Free the temporary UtlString, if necessary. if (formatp) { delete formatp; } lockForWrite(); mEventDefinitions.destroyAll(); unlockForWrite(); }
UtlBoolean SipRefreshManager::initiateRefresh(SipMessage& subscribeOrRegisterRequest, void* applicationData, const RefreshStateCallback refreshStateCallback, UtlString& earlyDialogHandle) { UtlBoolean intitialRequestSent = FALSE; // Make sure we do not have an existing dialog or refresh session state // going for the given message UtlString messageDialogHandle; subscribeOrRegisterRequest.getDialogHandle(messageDialogHandle); UtlBoolean existingRefreshState = FALSE; UtlBoolean existingDialogState = FALSE; if(!SipDialog::isEarlyDialog(messageDialogHandle)) { existingDialogState = TRUE; OsSysLog::add(FAC_SIP, PRI_ERR, "SipRefreshManager::initiateRefresh called with established dialog handle: %s", messageDialogHandle.data()); } else { OsLock localLock(mRefreshMgrMutex); // See if there is an early or established dialog for this message if(getAnyDialog(messageDialogHandle)) { existingRefreshState = TRUE; intitialRequestSent = FALSE; OsSysLog::add(FAC_SIP, PRI_ERR, "SipRefreshManager::initiateRefresh called with pre-existing refresh state: %s", messageDialogHandle.data()); } // The dialog should not exist either else if(mpDialogMgr->dialogExists(messageDialogHandle) || mpDialogMgr->earlyDialogExistsFor(messageDialogHandle)) { existingDialogState = TRUE; OsSysLog::add(FAC_SIP, PRI_ERR, "SipRefreshManager::initiateRefresh called with pre-existing dialog: %s", messageDialogHandle.data()); } } // Should not be any existing refresh or dialog states // for this message if(!existingRefreshState && !existingDialogState) { // Make sure we are registered to receive responses // for the message we are about to send UtlString method; subscribeOrRegisterRequest.getRequestMethod(&method); if(method.compareTo(SIP_REGISTER_METHOD) == 0) { lock(); if(!mReceivingRegisterResponses) { mReceivingRegisterResponses = TRUE; // receive REGISTER responses mpUserAgent->addMessageObserver(*(getMessageQueue()), SIP_REGISTER_METHOD, FALSE, // yes requests TRUE, // no responses TRUE, // incoming, FALSE, // outgoing NULL); } unlock(); } else if(method.compareTo(SIP_SUBSCRIBE_METHOD) == 0) { UtlString eventType; subscribeOrRegisterRequest.getEventField(&eventType, NULL); // Check to see if we have already registered to // receive the event type lock(); if(mEventTypes.find(&eventType) == NULL) { mEventTypes.insert(new UtlString(eventType)); // receive SUBSCRIBE responses for this event type mpUserAgent->addMessageObserver(*(getMessageQueue()), SIP_SUBSCRIBE_METHOD, FALSE, // no requests TRUE, // yes responses TRUE, // incoming, FALSE, // outgoing eventType); } unlock(); } // Create a new refresh state int requestedExpiration = 0; // returned from following call RefreshDialogState* state = createNewRefreshState(subscribeOrRegisterRequest, messageDialogHandle, applicationData, refreshStateCallback, requestedExpiration); // create a new dialog mpDialogMgr->createDialog(subscribeOrRegisterRequest, TRUE, // message from this side messageDialogHandle); // Keep track of when we send this request to be refreshed long now = OsDateTime::getSecsSinceEpoch(); state->mPendingStartTime = now; state->mRequestState = REFRESH_REQUEST_PENDING; // Set a timer at which to resend the next refresh based upon the // assumption that the request will succeed. When we receive a // failed response, we will cancel the timer and reschedule // a new timer based upon a smaller fraction of the requested // expiration period setRefreshTimer(*state, TRUE); // Resend with successful timeout OsTimer* resendTimer = state->mpRefreshTimer; // Mark the refresh state as having an outstanding request // and make a copy of the request. The copy needs to be // attached to the state before the send incase the response // comes back before we return from the send. state->mRequestState = REFRESH_REQUEST_PENDING; state->mpLastRequest = new SipMessage(subscribeOrRegisterRequest); // Add the state to the container of refresh states // No need to lock this refreshMgr earlier as this is a new // state and no one can touch it until it is in the list. lock(); mRefreshes.insert(state); unlock(); // NOTE: at this point is is no longer safe to touch the state // without locking it again. Avoid locking this refresh mgr // when something can block (e.g. like calling SipUserAgent ::send) // Send the request // Is the correct? Should we send the request first and only set // a timer if the request succeeds?? intitialRequestSent = mpUserAgent->send(subscribeOrRegisterRequest); // We do not clean up the state even if the send fails. // The application must end the refresh as the refresh // manager should retry the send if it failed if(!intitialRequestSent) { // Need to lock this refresh mgr and make sure the state // still exists. It could have been deleted while this // was unlocked above. lock(); if(stateExists(state)) { // It is now safe to touch the state again // Mark the state of the last request as having // failed, so we know to resend when the timer // fires state->mRequestState = REFRESH_REQUEST_FAILED; // The expiration should still be set to zero // the initial send failed, cancel the timer and // fire the notification. The handleMessage method // will see that the subscription or registration // has never succeeded and reshedule the timer for // a failure timeout. We cannot reschedule the // timer here as there is a race condition between // deleting the timer here (in the applications context) // and in handleMessage in this refresh manager's // context. // If the timer has not changed assume it is still safe // to touch it if(state->mpRefreshTimer == resendTimer) { stopTimerForFailureReschedule(state->mpRefreshTimer); } // Do not notify the application that the request failed // when it occurs on the first invokation. The application // will know by the return. } unlock(); } } return(intitialRequestSent); }
UtlBoolean SipSubscriptionMgr::insertDialogInfo(const SipMessage& subscribeRequest, const UtlString& resourceId, const UtlString& eventTypeKey, UtlString& subscribeDialogHandle, UtlBoolean& isNew, UtlBoolean& isSubscriptionExpired, SipMessage& subscribeResponse) { isNew = FALSE; UtlBoolean subscriptionSucceeded = FALSE; UtlString dialogHandle; subscribeRequest.getDialogHandle(dialogHandle); SubscriptionServerState* state = NULL; int expiration = -1; isSubscriptionExpired = TRUE; // 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); // Get and validate the expires period // This potentially should be delegated to the event handler specifics if(!subscribeRequest.getExpiresField(&expiration)) { expiration = mDefaultExpiration; } else if(expiration > mMaxExpiration) { expiration = mMaxExpiration; } // 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) { // 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::insertDialogInfo 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 may already exist else { // Get and validate the expires period // This potentially should be delegated to the event handler specifics if(!subscribeRequest.getExpiresField(&expiration)) { expiration = mDefaultExpiration; } else if(expiration > mMaxExpiration) { expiration = mMaxExpiration; } // 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) { long now = OsDateTime::getSecsSinceEpoch(); state->mExpirationDate = now + expiration; if(state->mpLastSubscribeRequest) { delete state->mpLastSubscribeRequest; } state->mpLastSubscribeRequest = new SipMessage(subscribeRequest); subscribeRequest.getAcceptField(state->mAcceptHeaderValue); // Set the contact to the same request URI that came in UtlString contact; subscribeRequest.getRequestUri(&contact); // Add the angle brackets for contact 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; } // No state, but SUBSCRIBE had a to-tag. else { SipMessage* subscribeCopy = new SipMessage(subscribeRequest); // 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; 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::insertDialogInfo insert subscription for key '%s', contact '%s', mExpirationDate %ld", stateKey->data(), requestContact.data(), state->mExpirationDate); } // Not safe to touch these after we unlock stateKey = NULL; state = NULL; subscribeCopy = NULL; // Set the contact to the same request URI that came in UtlString contact; subscribeRequest.getRequestUri(&contact); // Add the angle brackets for contact Url url(contact); url.includeAngleBrackets(); contact = url.toString(); subscribeResponse.setResponseData(&subscribeRequest, SIP_ACCEPTED_CODE, SIP_ACCEPTED_TEXT, contact); subscribeResponse.setExpiresField(expiration); subscriptionSucceeded = TRUE; // Unsubscribe if(expiration == 0) { isSubscriptionExpired = TRUE; } else { isSubscriptionExpired = FALSE; } subscribeDialogHandle = dialogHandle; } 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); }
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); }