void SipImpliedSubscriptions::buildSubscribeRequest( const SipMessage& registerMessage
                           ,int duration
                           ,SipMessage& subscribeRequest
                           ,UtlString&  callId
                           ,UtlString&  fromTag
                           ,UtlString&  fromUri
                           )
{
   UtlString registrationValue;
   UtlString tagNameValuePair;
   UtlString contactUri;
   int      sequenceNumber = 0;

   // Get the From URL, and change the tag
   Url fromUrl;
   registerMessage.getFromUrl( fromUrl );
   fromUrl.removeFieldParameter("tag"); // discard from tag from REGISTER
   registerMessage.getFromUri( &fromUri );
   (void) registerMessage.getContactUri(0, &contactUri);
   (void) registerMessage.getCSeqField(&sequenceNumber, &registrationValue);

   Url toUrl;
   registerMessage.getToUrl( toUrl );
   toUrl.removeFieldParameter("tag");
   
   UtlString toUri;
   registerMessage.getToUri( &toUri );

   registerMessage.getCallIdField( &callId );
   callId.prepend("implied-mwi-");

   // Build a from tag for the SUBSCRIBE
   //   - hash the call id so that it will be the same on each refresh
   UtlString callIdHash;
   NetMd5Codec::encode( callId.data(), callIdHash );
   fromUrl.setFieldParameter("tag", callIdHash.data() );
   fromTag = callIdHash; // for constructing the nonce

   subscribeRequest.setVoicemailData( fromUrl.toString() // From:
                                     ,toUrl.toString()   // To:
                                     ,toUri.data()       // request URI
                                     ,contactUri.data()  // taken from registration
                                     ,callId.data()
                                     ,++sequenceNumber
                                     ,duration
                                     );

   /*
    * Rewrite the event field to add our extension parameter to
    * ensure that the registration and subscription are synchronized.
    */
   const char* standardEventHeader = subscribeRequest.getHeaderValue(0, SIP_EVENT_FIELD);
   UtlString extendedEventHeader(standardEventHeader);
   extendedEventHeader.append(";" SIPX_IMPLIED_SUB "=");
   char durationString[12];
   sprintf(durationString, "%d", duration);
   extendedEventHeader.append(durationString);
   subscribeRequest.setHeaderValue(SIP_EVENT_FIELD, extendedEventHeader.data(), 0);
}
   /// wrapper function for all reg-info event tests
   bool ContactSetTest(UtlString regContactxml, UtlString requestUri, UtlString route)
   {
      bool ret = FALSE;
      instantiateAllTestFixtures( "resource-lists2.xml", 
                                  "subscription1", 
                                  "credential1", 
                                  "sip:127.0.0.1:45141",
                                  FALSE);

      // receive the reg-info subscribe 
      SipMessage request;
      while(getNextMessageFromRlsClientUnderTest( request, 5 ) )
      {
         UtlString method;
         request.getRequestMethod(&method);
         if(!request.isResponse() &&
            0 == method.compareTo(SIP_SUBSCRIBE_METHOD) )
         {
            // Accept the Subscription, regardless of whether it for a 'dialog' or 'reg' event
            // in order to stop retransmissions
            SipMessage regResponse;
            regResponse.setResponseData(&request, 202, "Accepted", "sip:127.0.0.1:45141");
            SipMessage * dispatchedMessage = new SipMessage(regResponse);            
            pResourceServerUnderTest->mClientUserAgent.dispatch(dispatchedMessage);

            // Deal with the two events separately
            UtlString eventField;
            request.getEventField(eventField);
            if(0 == eventField.compareTo("reg"))
            {
               UtlString contactInfo;
               request.getContactUri(0, &contactInfo);
               UtlString callid;
               request.getCallIdField(&callid);
               int cseq;
               request.getCSeqField(&cseq, NULL);

               Url toField;
               regResponse.getToUrl(toField);

               SipMessage regNotify;
               regNotify.setNotifyData(&request, 1, "", "", "reg");
               UtlString regInfo ("<?xml version=\"1.0\"?>\r\n"
                                 "<reginfo xmlns=\"urn:ietf:params:xml:ns:reginfo\" "
                                 "xmlns:gr=\"urn:ietf:params:xml:ns:gruuinfo\" version=\"911\" state=\"full\">\r\n"
                                 "   <registration aor=\"sip:[email protected]\" id=\"sip:[email protected]\" state=\"active\">\r\n "
                                 "      <contact id=\"sip:[email protected]@@&lt;");
               regInfo.append(contactInfo);
               regInfo.append("&gt;\" state=\"active\" event=\"registered\" q=\"1\" callid=\"");
               regInfo.append(callid);
               regInfo.append("\" cseq=\"");
               regInfo.appendNumber(cseq);
               regInfo.append("\">\r\n");
               regInfo.append(regContactxml);
               regInfo.append("      </contact>\r\n"
                              "   </registration>\r\n"
                              "</reginfo>");
               HttpBody * newBody = new HttpBody (regInfo, strlen(regInfo), "application/reginfo+xml");
               regNotify.setContentType("application/reginfo+xml");
               regNotify.setBody(newBody);

               // Set the From field the same as the to field from the 202 response, as it
               // contains the dialog identifying to tags
               regNotify.setRawFromField(toField.toString().data());
               sendToRlsServerUnderTest( regNotify );
            }
            else if(0 == eventField.compareTo("dialog"))
            {
               // If we find a dialog event subscription with the request uri and route
               // that we are looking for, mark the test as passed

               UtlString uri;
               UtlString myRoute;
               request.getRequestUri(&uri);
               request.getRouteField(&myRoute); 
               if(0 == uri.compareTo(requestUri) &&
                  0 == route.compareTo(myRoute))
               {
                  ret = true;
               }
            }
         }
      }
      return ret;
   }
Exemple #3
0
void SipDialog::updateDialogData(const SipMessage& message)
{
    UtlString messageCallId;
    message.getCallIdField(&messageCallId);
    Url messageFromUrl;
    message.getFromUrl(messageFromUrl);
    UtlString messageFromTag;
    messageFromUrl.getFieldParameter("tag", messageFromTag);
    Url messageToUrl;
    message.getToUrl(messageToUrl);
    UtlString messageToTag;
    messageToUrl.getFieldParameter("tag", messageToTag);

    int cSeq;
    UtlString method;
    message.getCSeqField(&cSeq, &method);
    int responseCode = message.getResponseStatusCode();

    // Figure out if the request is from the local or
    // the remote side
    if(isTransactionLocallyInitiated(messageCallId,
                                     messageFromTag,
                                     messageToTag))
    {
        // This message is part of a transaction initiated by
        // the local side of the dialog

        if(cSeq > mLastLocalCseq)
        {
            mLastLocalCseq = cSeq;
        }

        if(cSeq >= mLastLocalCseq)
        {
            // Always update the contact if it is set
            UtlString messageContact;
            // Get the Contact value, but as an addr-spec.
            if(message.getContactUri(0, &messageContact) &&
                !messageContact.isNull())
            {
                if(message.isResponse())
                {
                   mRemoteContact.fromString(messageContact, TRUE);
                }
                else
                {
                   mLocalContact.fromString(messageContact, TRUE);
                }
            }
        }

        // Cannot assume that we only establish a dialog with the
        // initial cseq.  For example if there is an authentication
        // challenge, the dialog will not be established until the
        // second transaction.
        if(cSeq == mLastLocalCseq)
        {
            // A successful response to an INVITE or SUBSCRIBE
            // make this early dialog a set up dialog
            if(mLocalInitiatedDialog &&
               message.isResponse() &&
               responseCode >= SIP_2XX_CLASS_CODE && // successful dialog setup
               responseCode < SIP_3XX_CLASS_CODE &&
               mRemoteTag.isNull() && // tag not set
               mRouteSet.isNull()) // have not yet set the route set
            {
                // Change this early dialog to a set up dialog.
                // The tag gets set in the 2xx response
                // so we need to update the URL
                message.getToUrl(mRemoteField);
                mRemoteField.getFieldParameter("tag", mRemoteTag);

                // Need to get the route set as well
                // Make sure the Request Method is allowed to set Record-Routes
                if(message.isRecordRouteAccepted())
                {
                    message.buildRouteField(&mRouteSet);
                }
            }
        }
    }
    else if(isTransactionRemotelyInitiated(messageCallId,
                                           messageFromTag,
                                           messageToTag))
    {
        int prevRemoteCseq = mLastRemoteCseq;

        // This message is part of a transaction initiated by
        // the callee/destination of the session
        if(cSeq > mLastRemoteCseq)
        {
            mLastRemoteCseq = cSeq;
        }

        if(cSeq >= mLastRemoteCseq)
        {
            // Always update the contact if it is set
            UtlString messageContact;
            // Get the Contact value, but as an addr-spec.
            if(message.getContactUri(0, &messageContact) &&
                !messageContact.isNull())
            {
                if(message.isResponse())
                {
                   mLocalContact.fromString(messageContact, TRUE);
                }
                else
                {
                   mRemoteContact.fromString(messageContact, TRUE);
                }
            }
        }

        // First transaction from the otherside
        if(cSeq == mLastRemoteCseq && prevRemoteCseq == -1)
        {
            // A response (e.g. NOTIFY) can come before we get the
            // successful response to the initial transaction
            if(!mLocalInitiatedDialog &&
               !message.isResponse() &&
               mRemoteTag.isNull()) // tag not set
            {
                // Change this early dialog to a set up dialog.
                // The tag gets set in the 2xx response
                // so we need to update the URL
                message.getFromUrl(mRemoteField);
                mRemoteField.getFieldParameter("tag", mRemoteTag);
            }
        }

        // First successful response from the local side
        if(cSeq == mLastRemoteCseq)
        {
            if(!mLocalInitiatedDialog &&
               message.isResponse() &&
               responseCode >= SIP_2XX_CLASS_CODE && // successful dialog setup
               responseCode < SIP_3XX_CLASS_CODE &&
               mLocalTag.isNull())
            {
                // Update the local tag
                message.getToUrl(mLocalField);
                mLocalField.getFieldParameter("tag", mLocalTag);
            }
        }
    }
}
   void AppearanceTest()
   {
      instantiateAllTestFixtures( "appearance-groups1.xml",
                                  "subscription1",
                                  "credential1",
                                  "177.0.0.1:54140");

      UtlString sharedUri = "sip:[email protected]:54140";
      UtlString app1uri = "sip:127.0.0.1:45141";
      UtlString dialogHandle;

      // receive the reg-info subscribe
      SipMessage request;
      UtlString b;
      ssize_t l;
      while(getNextMessageFromAppearanceAgentUnderTest( request, 5 ))
      {
         request.getBytes(&b, &l);
         OsSysLog::add(FAC_RLS, PRI_DEBUG, "got message %s", b.data());
         UtlString method;
         request.getRequestMethod(&method);
         if(!request.isResponse() &&
            0 == method.compareTo(SIP_SUBSCRIBE_METHOD) )
         {
            // Accept the Subscription, regardless of whether it for a 'dialog' or 'reg' event
            // in order to stop retransmissions
            SipMessage regResponse;
            regResponse.setResponseData(&request, 202, "Accepted", app1uri);
            SipMessage * dispatchedMessage = new SipMessage(regResponse);
            dispatchedMessage->getBytes(&b, &l);
            OsSysLog::add(FAC_RLS, PRI_DEBUG, "sent message %s", b.data());
            pAppearanceAgentUnderTest->mServerUserAgent.dispatch(dispatchedMessage);

            // Deal with the two events separately
            UtlString eventField;
            request.getEventField(eventField);
            if(0 == eventField.compareTo("reg"))
            {
               UtlString contactInfo;
               request.getContactUri(0, &contactInfo);
               UtlString callid;
               request.getCallIdField(&callid);
               int cseq;
               request.getCSeqField(&cseq, NULL);

               Url toField;
               regResponse.getToUrl(toField);

               SipMessage regNotify;
               regNotify.setNotifyData(&request, 1, "", "", "reg");
               UtlString regInfo ("<?xml version=\"1.0\"?>\r\n"
                                 "<reginfo xmlns=\"urn:ietf:params:xml:ns:reginfo\" "
                                 "xmlns:gr=\"urn:ietf:params:xml:ns:gruuinfo\" version=\"911\" state=\"full\">\r\n"
                                 "   <registration aor=\"sip:[email protected]:54140\" id=\"sip:[email protected]:54140\" state=\"active\">\r\n "
                                 "      <contact id=\"sip:[email protected]:54140@@&lt;");
               regInfo.append(contactInfo);
               regInfo.append("&gt;\" state=\"active\" event=\"registered\" q=\"1\" callid=\"");
               regInfo.append(callid);
               regInfo.append("\" cseq=\"");
               regInfo.appendNumber(cseq);
               regInfo.append("\">\r\n");
               regInfo.append("<uri>");
               regInfo.append(app1uri);
               regInfo.append("</uri>");
               regInfo.append("      </contact>\r\n"
                              "   </registration>\r\n"
                              "</reginfo>");
               HttpBody * newBody = new HttpBody (regInfo, strlen(regInfo), "application/reginfo+xml");
               regNotify.setContentType("application/reginfo+xml");
               regNotify.setBody(newBody);

               // Set the From field the same as the to field from the 202 response, as it
               // contains the dialog identifying to tags
               regNotify.setRawFromField(toField.toString().data());
               sendToAppearanceAgentUnderTest( regNotify );
               regNotify.getBytes(&b, &l);
               OsSysLog::add(FAC_RLS, PRI_DEBUG, "sent reg NOTIFY to AppAgent");
               OsSysLog::add(FAC_RLS, PRI_DEBUG, "sent message %s", b.data());
            }
            else if (0 == eventField.compareTo(SLA_EVENT_TYPE))
             {
                // should send empty NOTIFY, but no one will care
                // save dialogHandle for this subscription/Appearance (ignore retransmissions)
                if (dialogHandle.isNull())
                {
                   SipMessage fake(b);
                   fake.getDialogHandle(dialogHandle);
                   OsSysLog::add(FAC_RLS, PRI_DEBUG, "got SUBSCRIBE(sla) request: dialogHandle %s", dialogHandle.data());
                }
             }
         }
      }

      CPPUNIT_ASSERT( !dialogHandle.isNull() );
      OsSysLog::add(FAC_RLS, PRI_DEBUG, "we now have an Appearance - test it");
      AppearanceGroup* pAppGroup = pAppearanceAgentUnderTest->getAppearanceGroupSet().
         findAppearanceGroup(sharedUri);
      CPPUNIT_ASSERT( pAppGroup );

      Appearance* pApp = pAppGroup->findAppearance(dialogHandle);
      CPPUNIT_ASSERT( pApp );
      ASSERT_STR_EQUAL( app1uri.data(), pApp->getUri()->data() );

      // test adding a new dialog
      const char* dialogEventString =
            "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
            "<dialog-info xmlns=\"urn:ietf:params:xml:ns:dialog-info\" version=\"0\" state=\"partial\" entity=\"sip:[email protected]:54140\">\n"
            "<dialog id=\"1\" call-id=\"[email protected]\" local-tag=\"264460498\" remote-tag=\"1c10982\" direction=\"recipient\">\n"
            "<state>confirmed</state>\n"
            "<local>\n"
            "<identity>[email protected]:5120</identity>\n"
            "<target uri=\"sip:[email protected]:5120\">\n"
            "<param pname=\"x-line-id\" pval=\"0\"/>\n"
            "<param pname=\"+sip.rendering\" pval=\"yes\"/>\n"
            "</target>\n"
            "</local>\n"
            "<remote>\n"
            "<identity>[email protected]</identity>\n"
            "</remote>\n"
            "</dialog>\n"
            "</dialog-info>\n"
            ;
      SipDialogEvent dialogEvent(dialogEventString);
      bool bFullContentChanged = false;
      bool bPartialContentChanged = pApp->updateState(&dialogEvent, bFullContentChanged);
      CPPUNIT_ASSERT(bPartialContentChanged);
      CPPUNIT_ASSERT(bFullContentChanged);
      pApp->dumpState();
      CPPUNIT_ASSERT(pApp->appearanceIsBusy());
      CPPUNIT_ASSERT(pApp->appearanceIdIsSeized("0"));
      CPPUNIT_ASSERT(!pApp->appearanceIdIsSeized("1"));

      // simulate user putting the call on hold
      const char* dialogEventString2 =
            "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
            "<dialog-info xmlns=\"urn:ietf:params:xml:ns:dialog-info\" version=\"0\" state=\"partial\" entity=\"sip:[email protected]:54140\">\n"
            "<dialog id=\"1\" call-id=\"[email protected]\" local-tag=\"264460498\" remote-tag=\"1c10982\" direction=\"recipient\">\n"
            "<state>confirmed</state>\n"
            "<local>\n"
            "<identity>[email protected]:5120</identity>\n"
            "<target uri=\"sip:[email protected]:5120\">\n"
            "<param pname=\"x-line-id\" pval=\"0\"/>\n"
            "<param pname=\"+sip.rendering\" pval=\"no\"/>\n"
            "</target>\n"
            "</local>\n"
            "<remote>\n"
            "<identity>[email protected]</identity>\n"
            "</remote>\n"
            "</dialog>\n"
            "</dialog-info>\n"
            ;
      SipDialogEvent dialogEvent2(dialogEventString2);
      bPartialContentChanged = pApp->updateState(&dialogEvent2, bFullContentChanged);
      CPPUNIT_ASSERT(bPartialContentChanged);
      CPPUNIT_ASSERT(bFullContentChanged);
      pApp->dumpState();
      CPPUNIT_ASSERT(!pApp->appearanceIsBusy());
      CPPUNIT_ASSERT(pApp->appearanceIdIsSeized("0"));
      CPPUNIT_ASSERT(!pApp->appearanceIdIsSeized("1"));

      // test MESSAGE debug handling
      const char* message =
         "MESSAGE sip:[email protected]:54140 SIP/2.0\r\n"
         "From: <sip:[email protected]>;tag=17211757-9E4FBD78\r\n"
         "To: <sip:[email protected]:54140>\r\n"
         "CSeq: 1 MESSAGE\r\n"
         "Call-ID: 51405734-b9be4835-dcd9d196\r\n"
         "Contact: <sip:[email protected]>\r\n"
         "Allow: INVITE, ACK, BYE, CANCEL, OPTIONS, INFO, MESSAGE, SUBSCRIBE, NOTIFY, PRACK, UPDATE, REFER\r\n"
         "Event: dialog\r\n"
         "User-Agent: UnitTest\r\n"
         "Accept-Language: en\r\n"
         "Accept: application/dialog-info+xml\r\n"
         "Max-Forwards: 70\r\n"
         "Expires: 3600\r\n"
         "Content-Length: 0\r\n"
         "\r\n";

      // send the MESSAGE
      SipMessage messageRequest( message, strlen( message ) );
      CPPUNIT_ASSERT( sendToAppearanceAgentUnderTest( messageRequest ) );

      // receive the 200 OK response
      SipMessage response;
      CPPUNIT_ASSERT( getNextMessageFromAppearanceAgentUnderTest( response, 5 ) );
      CPPUNIT_ASSERT( response.isResponse() );
      CPPUNIT_ASSERT( response.getResponseStatusCode() == SIP_OK_CODE );
   }