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);
}
Example #2
0
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);
}