예제 #1
0
void SipClient::getClientNames(UtlString& clientNames) const
{
    char portString[40];

    // host DNS name
    sprintf(portString, "%d", mRemoteHostPort);
    clientNames = " remote host: ";
    clientNames.append(mRemoteHostName);
    clientNames.append(":");
    clientNames.append(portString);

    // host IP address
    clientNames.append(" remote IP: ");
    clientNames.append(mRemoteSocketAddress);
    clientNames.append(":");
    clientNames.append(portString);

    // via address
    clientNames.append(" remote Via address: ");
    clientNames.append(mRemoteViaAddress);
    clientNames.append(":");
    clientNames.appendNumber(mRemoteViaPort);

    // received address
    clientNames.append(" received address: ");
    clientNames.append(mReceivedAddress);
    clientNames.append(":");
    clientNames.appendNumber(mRemoteReceivedPort);
}
예제 #2
0
void
RegistrationDB::getUnexpiredContactsFieldsContaining (
   UtlString& substringToMatch,
   const int& timeNow,   
   UtlSList& matchingContactFields ) const
{
   // Clear the results
   matchingContactFields.destroyAll();
   if( m_pFastDB != NULL )
   {
       SMART_DB_ACCESS;
       dbCursor< RegistrationRow > cursor;
       UtlString queryString = "contact like '%" + substringToMatch + "%' and expires>";
       queryString.appendNumber( timeNow );
       dbQuery query;
       query = queryString;

       if ( cursor.select(query) > 0 )
       {
           // Copy all the unexpired contacts into the result list
           do
           {
               UtlString* contactValue = new UtlString(cursor->contact);
               matchingContactFields.append( contactValue );
           } while ( cursor.next() );
       }
   }
   else
   {
      OsSysLog::add(FAC_DB, PRI_CRIT, "RegistrationDB::getUnexpiredContactsFieldsContaining failed - no DB");
   }
}
예제 #3
0
void CallId::getNewCallId(UtlString& callId)
{
   // Lock to protect common variables.
   OsLock lock(sCallIdMutex);

   // Increment the call number, rolling over after 0xFFFF, since we only
   // use the low-order 16 bits.
   sCallNum = (sCallNum + 1) & 0xFFFF;

   // Compute the next value.
   nextValue();

   // Compose the new Call-Id.
   callId.remove(0);
   callId.append(sChainValue, 0, CALL_ID_CHAIN_VALUE_REVEALED);
   callId.appendNumber(sCallNum, "%04x");
}
예제 #4
0
void NatMaintainer::sendKeepAliveToEndpoint( const char* pIpAddress, uint16_t portNumber )
{
   bool bDoSendKeepAlive = true;
   KeepAliveEndpointDescriptor* pKeepAliveEndpointDescriptor;

   pKeepAliveEndpointDescriptor = &( mpEndpointsKeptAliveList[ portNumber ] );

   if( pKeepAliveEndpointDescriptor->mLastRefreshRoundNumber == mRefreshRoundNumber )
   {
      // We have already sent a keep-alive to an endpoint utilizing this port in this refresh
      // round.  Check to see if we also have an IP address match.  If we do, that endpoint
      // has already been refreshed in this round so do not send it yet another refresh.
      if( strcmp( pIpAddress, pKeepAliveEndpointDescriptor->mIpAddress ) == 0 )
      {
         bDoSendKeepAlive = false;
      }
   }
   else
   {
      // We have yet to send a keep-alive to an endpoint utilizing this port in this refresh round
      // Save the endpoint's IP address against the port in mpEndpointsKeptAliveList[portNumber]
      // to prevent the generation of any aditional keep alives to that endpoint during that round.
      pKeepAliveEndpointDescriptor->mLastRefreshRoundNumber = mRefreshRoundNumber;
      strncpy( pKeepAliveEndpointDescriptor->mIpAddress, pIpAddress, MAX_IP_ADDRESS_STRING_SIZE );
      pKeepAliveEndpointDescriptor->mIpAddress[ MAX_IP_ADDRESS_STRING_SIZE ] = 0;
   }
   if( bDoSendKeepAlive )
   {
      // Generate unique call-id and branches for each remote endpoint
      UtlString callId = pIpAddress;
      callId.append('-').appendNumber( portNumber, "%d" )
            .append('-').append( NAT_MAINTAINER_MARKER_STRING ).append('-').append( mBaseCallId );
      mpKeepAliveMessage->setCallIdField( callId );
      
      UtlString branchId = callId;
      branchId.appendNumber( mNextSeqValue, "%d" );
      mpKeepAliveMessage->setSendProtocol(OsSocket::UDP);
      mpKeepAliveMessage->setTopViaTag( branchId.data(), "branch" );   
      mpSipRouter->sendUdpKeepAlive( *mpKeepAliveMessage, pIpAddress, portNumber );
   }
}
예제 #5
0
파일: Notifier.cpp 프로젝트: ciuc/sipxecs
void
Notifier::sendNotifyForeachSubscription (
    const char* key,
    const char* event,
    SipMessage& notify,
    const SipMessage* subscribe)
{
    SubscribeDB::Subscriptions subscriptions;

    int timeNow = (int)OsDateTime::getSecsSinceEpoch();

    // Get all subscriptions associated with this identity
    SubscribeDB::defaultCollection().collection().getUnexpiredSubscriptions(
           SUBSCRIPTION_COMPONENT_STATUS, key, event, timeNow, subscriptions );



    // Add the static configured contacts.
    UtlString userUri(key);
    UtlString eventType(event);
    UtlString userContact;
    UtlString userFromUri;
    UtlString userToUri;
    UtlString userCallid;
    EntityRecord entity;
    if (EntityDB::defaultCollection().collection().findByIdentity(std::string(key), entity))
    {
        std::vector<EntityRecord::StaticUserLoc> staticUserLoc = entity.staticUserLoc();
        if (staticUserLoc.size() > 0)
        {
            //staticUserLoc[0].event.c_str();
            userContact = staticUserLoc[0].contact.c_str();
            userFromUri = staticUserLoc[0].fromUri.c_str();
            userToUri = staticUserLoc[0].toUri.c_str();
            userCallid = staticUserLoc[0].callId.c_str();

             Os::Logger::instance().log(FAC_SIP, PRI_DEBUG,
                           "Notifier::sendNotifyForeachSubscription configured contact %s", userUri.data());
             int notifycseq = 0;
             userCallid.append("-");
             userCallid.appendNumber((mpStaticSeq++ & 0xFFFF), "%04x");
             UtlString id("");
             UtlString subscriptionState("terminated");
             UtlString recordroute("");

             SendTheNotify(notify, mpSipUserAgent, userUri, userContact, userFromUri, userToUri, userCallid, notifycseq,
                           eventType, id, subscriptionState, recordroute);
        }
    }


    int numSubscriptions = subscriptions.size();

    if (  numSubscriptions > 0 )
    {
        Os::Logger::instance().log( FAC_SIP, PRI_INFO, "Notifier::sendNotifyForeachSubscription: "
                      " %d '%s' subscriptions for '%s'", numSubscriptions, event, key );

        UtlString subscribeCallid;
        if ( subscribe )
        {
           subscribe->getCallIdField(&subscribeCallid);
           Os::Logger::instance().log( FAC_SIP, PRI_INFO, "Notifier::sendNotifyForeachSubscription: "
                         " notify only '%s'", subscribeCallid.data() );
        }

        // There may be any number of subscriptions
        // for the same identity and event type!
        // send a notify to each
        for (SubscribeDB::Subscriptions::iterator iter = subscriptions.begin(); iter != subscriptions.end(); iter++ )
        {
            Subscription& record = *iter;

            UtlString uri        = record.uri().c_str();
            UtlString callid     = record.callId().c_str();
            UtlString contact    = record.contact().c_str();
            int expires = record.expires();
            UtlString eventtype  = record.eventType().c_str();
            UtlString id         =  record.id().c_str();
            UtlString to         = record.toUri().c_str();
            UtlString from       = record.fromUri().c_str();
            if ( subscribe && subscribeCallid != callid  )
            {
               Os::Logger::instance().log( FAC_SIP, PRI_DEBUG, "Notifier::sendNotifyForeachSubscription: "
                             " skipping '%s'; notify only '%s'", callid.data(), subscribeCallid.data() );
               continue;
            }
            UtlString key        = record.key().c_str();
            UtlString recordroute= record.recordRoute().c_str();
            int notifycseq      = record.notifyCseq();

            // the recordRoute column in the database is optional
            // and can be set to null, the IMDB does not support null columns
            // so look to see if the field is set to "%" a special single character
            // reserved value we use to null columns in the IMDB
            if ( record.recordRoute().empty() )
            {
                recordroute.remove(0);
            }

            UtlString subscriptionState;
            int requestedExpires = -1;
            if ( subscribe )
            {
               subscribe->getExpiresField(&requestedExpires);
            }
            if ( requestedExpires == 0 )
            {
               subscriptionState = SIP_SUBSCRIPTION_TERMINATED;
               // the subscription should be removed from the database after this NOTIFY is sent
            }
            else
            {
               subscriptionState = SIP_SUBSCRIPTION_ACTIVE ";expires=";
               char expStr[10];
               sprintf(expStr, "%d", expires);
               subscriptionState.append(expStr);
            }

            // increment the outbound cseq sent with the notify
            // this will guarantee that duplicate messages are rejected
            notifycseq += 1;

            SendTheNotify(notify, mpSipUserAgent, uri, contact, to, from, callid, notifycseq, 
                          eventtype, id, subscriptionState, recordroute);

            // Update the Notify sequence number (CSeq) in the IMDB
            // (We must supply a dummy XML version number.)
            SubscribeDB::defaultCollection().collection().updateNotifyUnexpiredSubscription (
               SUBSCRIPTION_COMPONENT_STATUS, to, from, callid,
               eventtype, id, timeNow, notifycseq, 0 );
        }
    }
    else
    {
       Os::Logger::instance().log( FAC_SIP, PRI_INFO, "Notifier::sendNotifyForeachSubscription: "
                     " no '%s' subscriptions for '%s'", event, key );
    }
}
예제 #6
0
void Dialog::getBytes(UtlString& b, ssize_t& l)
{
   b.remove(0);
   b.append(BEGIN_DIALOG);
   UtlString singleLine;
   singleLine = DOUBLE_QUOTE;
   XmlEscape(singleLine, mId);
   singleLine += DOUBLE_QUOTE;
   b += singleLine;
   if (!mCallId.isNull())
   {
      b.append(CALL_ID_EQUAL);
      singleLine = DOUBLE_QUOTE;
      XmlEscape(singleLine, mCallId);
      singleLine += DOUBLE_QUOTE;
      b += singleLine;
   }

   if (!mLocalTag.isNull())
   {
      b.append(LOCAL_TAG_EQUAL);
      // mLocalTag is a token
      singleLine = DOUBLE_QUOTE + mLocalTag + DOUBLE_QUOTE;
      b += singleLine;
   }

   if (!mRemoteTag.isNull())
   {
      b.append(REMOTE_TAG_EQUAL);
      // mRemoteTag is a token
      singleLine = DOUBLE_QUOTE + mRemoteTag + DOUBLE_QUOTE;
      b += singleLine;
   }

   if (!mDirection.isNull())
   {
      b.append(DIRECTION_EQUAL);
      // mDirection is a token
      singleLine = DOUBLE_QUOTE + mDirection + DOUBLE_QUOTE;
      b += singleLine;
   }
   b.append(END_LINE);

   // State element
   b.append(BEGIN_STATE);
   if (!mEvent.isNull())
   {
      b.append(EVENT_EQUAL);
      // mEvent is a token
      singleLine = DOUBLE_QUOTE + mEvent + DOUBLE_QUOTE;
      b += singleLine;
   }

   if (!mCode.isNull())
   {
      b.append(CODE_EQUAL);
      // mCode is a token
      singleLine = DOUBLE_QUOTE + mCode + DOUBLE_QUOTE;
      b += singleLine;
   }

   // End of state element
   singleLine = END_BRACKET + mState + END_STATE;
   b += singleLine;

   // Duration element
   if (mDuration !=0)
   {
      b += BEGIN_DURATION;
      b.appendNumber((Int64) OsDateTime::getSecsSinceEpoch() - mDuration);
      b += END_DURATION;
   }

   // Local element
   b.append(BEGIN_LOCAL);
   if (!mLocalIdentity.isNull())
   {
      b.append(BEGIN_IDENTITY);
      if (!mLocalDisplay.isNull())
      {
         UtlString displayName = mLocalDisplay;
         NameValueTokenizer::frontBackTrim(&displayName, "\"");
         b.append(DISPLAY_EQUAL);
         singleLine = DOUBLE_QUOTE;
         XmlEscape(singleLine, displayName);
         singleLine += DOUBLE_QUOTE;
         b += singleLine;
      }

      singleLine = END_BRACKET;
      XmlEscape(singleLine, mLocalIdentity);
      singleLine += END_IDENTITY;
      b += singleLine;
   }

   if (!mLocalTarget.isNull() && mLocalTarget.compareTo("sip:") != 0)
   {
      singleLine = BEGIN_TARGET;
      XmlEscape(singleLine, mLocalTarget);
      singleLine += DOUBLE_QUOTE END_LINE;
      b += singleLine;
      // add optional parameters
      UtlDListIterator* iterator = getLocalParameterIterator();
      NameValuePairInsensitive* nvp;
      while ((nvp = (NameValuePairInsensitive*) (*iterator)()))
      {
         singleLine = BEGIN_DIALOG_PARAM;
         singleLine += PNAME;
         XmlEscape(singleLine, nvp->data());
         singleLine += PVALUE;
         XmlEscape(singleLine, nvp->getValue());
         singleLine += END_DIALOG_PARAM;
         b += singleLine;
      }
      delete iterator;

      singleLine = END_TARGET;
      b += singleLine;
   }

   // End of local element
   b.append(END_LOCAL);

   // Remote element
   b.append(BEGIN_REMOTE);
   if (!mRemoteIdentity.isNull())
   {
      b.append(BEGIN_IDENTITY);
      if (!mRemoteDisplay.isNull())
      {
         UtlString displayName = mRemoteDisplay;
         NameValueTokenizer::frontBackTrim(&displayName, "\"");
         b.append(DISPLAY_EQUAL);
         singleLine = DOUBLE_QUOTE;
         XmlEscape(singleLine, displayName);
         singleLine += DOUBLE_QUOTE;
         b += singleLine;
      }

      singleLine = END_BRACKET;
      XmlEscape(singleLine, mRemoteIdentity);
      singleLine += END_IDENTITY;
      b += singleLine;
   }

   if (!mRemoteTarget.isNull() && mRemoteTarget.compareTo("sip:") != 0)
   {
      singleLine = BEGIN_TARGET;
      XmlEscape(singleLine, mRemoteTarget);
      singleLine += DOUBLE_QUOTE END_LINE;
      b += singleLine;
      // add optional parameters
      UtlDListIterator* iterator = getRemoteParameterIterator();
      NameValuePairInsensitive* nvp;
      while ((nvp = (NameValuePairInsensitive*) (*iterator)()))
      {
         singleLine = BEGIN_DIALOG_PARAM;
         singleLine += PNAME;
         XmlEscape(singleLine, nvp->data());
         singleLine += PVALUE;
         XmlEscape(singleLine, nvp->getValue());
         singleLine += END_DIALOG_PARAM;
         b += singleLine;
      }
      delete iterator;

      singleLine = END_TARGET;
      b += singleLine;
   }

   // End of remote element
   b.append(END_REMOTE);

   // End of dialog element
   b.append(END_DIALOG);

   l = b.length();
}
예제 #7
0
   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 );
   }
예제 #8
0
// Add to the HttpBody the current state of the resource.
void ResourceInstance::generateBody(UtlString& rlmi,
                                    HttpBody& body,
                                    UtlBoolean consolidated,
                                    const UtlString& displayName) const
{
   OsSysLog::add(FAC_RLS, PRI_DEBUG,
                 "ResourceInstance::generateBody mInstanceName = '%s', consolidated = %d, displayName = '%s', mContentPresent = %d",
                 mInstanceName.data(), consolidated, displayName.data(),
                 mContentPresent);

   if (consolidated)
   {
      if (mContentPresent)
      {
         // If this is a consolidated dialog event list, edit the
         // stored XML into the right form and append each dialog
         // to the resource list event notice.

         TiXmlUtlStringWriter writer(&rlmi);

         // Iterate through all the <dialog> elements.
         UtlHashMapIterator itor(mXmlDialogs);
         UtlContainable* id;
         while ((id = itor()))
         {
            UtlVoidPtr* p = dynamic_cast <UtlVoidPtr*> (itor.value());
            TiXmlElement* dialog_element =
               static_cast <TiXmlElement*> (p->getValue());

            // Now that we've got a <dialog> element, edit it to fit
            // into a consolidated event notice.

            // Get the display name right.
            // Find the <local> element, which we know exists due to
            // earlier processing.
            TiXmlNode* local = dialog_element->FirstChild("local");
            // Find the <local><identity> element, which we know
            // exists due to earlier processing.
            TiXmlNode* identity = local->FirstChild("identity");
            // Update the display attribute, as that is what will show
            // on the phone.
            identity->ToElement()->
               SetAttribute("display", displayName);

            // Un-parse the dialog into the string for storage.
            writer << *dialog_element;
            writer << "\r\n";
         }
      }
   }
   else
   {
      // Generate the XML for the instance.
      rlmi += "    <instance id=\"";
      XmlEscape(rlmi, mInstanceName);
      rlmi += "\" state=\"";
      // Subscription states don't require escaping.
      rlmi += mSubscriptionState;
      rlmi += "\"";

      // Generate the body part for the instance, if necessary.
      if (mContentPresent)
      {
         UtlString contentBodyPartCid;
         // Use the count of parts in 'body' to generate a unique identifier for
         // each part.
         contentBodyPartCid.appendNumber(body.getMultipartCount());
         contentBodyPartCid += "@";
         contentBodyPartCid += getResourceListServer()->getDomainName();

         rlmi += " cid=\"";
         rlmi += contentBodyPartCid;
         rlmi += "\"";

         // Now add the <...> and use it in the header.
         contentBodyPartCid.prepend("<");
         contentBodyPartCid.append(">");

         HttpBody content_body(mContent.data(),
                               mContent.length(),
                               getResourceListServer()->getContentType());
         UtlDList content_body_parameters;
         content_body_parameters.append(
            new NameValuePair(HTTP_CONTENT_ID_FIELD,
                              contentBodyPartCid));
         body.appendBodyPart(content_body, content_body_parameters);
         content_body_parameters.destroyAll();
      }

      rlmi += "/>\r\n";
   }
}
예제 #9
0
/// Provide access to files as allowed by process definitions.
void HttpFileAccess::processRequest(const HttpRequestContext& requestContext,
                                    const HttpMessage& request,
                                    HttpMessage*& response
                                    )
{
   UtlString message;
   response = new HttpMessage();

   UtlString peerName;
   if (mSipxRpc->isAllowedPeer(requestContext, peerName))
   {
      if (requestContext.methodIs(HTTP_GET_METHOD))
      {
         UtlString path;
         requestContext.getMappedPath(path);

         FileResource* resource = FileResourceManager::getInstance()->find(path);
         if (resource)
         {
            if (resource->isReadable())
            {
               sendFile(path, peerName, requestContext, request, response);
            }
            else
            {
               message.append("resource ");
               resource->appendDescription(message);
               message.append(" does not allow write access to '");
               message.append(path);
               message.append("'");
               OsSysLog::add(FAC_SUPERVISOR, PRI_ERR, "HttpFileAccess::processRequest from %s %s",
                             peerName.data(), message.data());

               response->setResponseFirstHeaderLine(HTTP_PROTOCOL_VERSION,
                                                    HTTP_FORBIDDEN_CODE,
                                                    "Access denied by process definition");
               response->setBody(new HttpBody(message.data(),message.length()));
               response->setContentType(CONTENT_TYPE_TEXT_PLAIN);
               response->setContentLength(message.length());
            }
         }
         else
         {
            message.append("File resource '");
            message.append(path);
            message.append("' not known to sipXsupervisor.");
            OsSysLog::add(FAC_SUPERVISOR, PRI_ERR, "HttpFileAccess::processRequest from %s %s",
                          peerName.data(), message.data());

            response->setResponseFirstHeaderLine(HTTP_PROTOCOL_VERSION,
                                                 HTTP_FILE_NOT_FOUND_CODE,
                                                 HTTP_FILE_NOT_FOUND_TEXT);
            response->setBody(new HttpBody(message.data(),message.length()));
            response->setContentType(CONTENT_TYPE_TEXT_PLAIN);
            response->setContentLength(message.length());
         }
      }
      else if (requestContext.methodIs(HTTP_DELETE_METHOD))
      {
         UtlString path;
         requestContext.getMappedPath(path);

         FileResource* resource = FileResourceManager::getInstance()->find(path);
         if (resource)
         {
            if (resource->isWriteable())
            {
               OsPath filePath(path);
               if (OsFileSystem::exists(filePath))
               {
                  if (OS_SUCCESS
                      == OsFileSystem::remove(filePath, TRUE /* recursive */, TRUE /* force */))
                  {
                     message.append("File '");
                     message.append(path);
                     message.append("' deleted");
                     OsSysLog::add(FAC_SUPERVISOR, PRI_INFO, "HttpFileAccess::processRequest from %s %s",
                                   peerName.data(), message.data());
                  
                     response->setResponseFirstHeaderLine(HTTP_PROTOCOL_VERSION_1_1,
                                                          HTTP_OK_CODE, "Deleted");
                     response->setContentLength(0);

                     // tell anyone who cares that this has changed
                     resource->modified();
                  }
                  else
                  {
                     int       httpStatusCode;
                     UtlString httpStatusText;

                     switch (errno)
                     {
                     case EACCES:
                        httpStatusCode = HTTP_FORBIDDEN_CODE;
                        httpStatusText = "File Access Denied";
                        break;

                     default:
                        httpStatusCode = HTTP_SERVER_ERROR_CODE;
                        httpStatusText.append("Unknown error ");
                        httpStatusText.appendNumber(errno);
                        break;
                     }

                     message.append("File '");
                     message.append(path);
                     message.append("' errno ");
                     message.appendNumber(errno);
                     message.append(" ");

                     char errnoMsg[1024];
                     strerror_r(errno, errnoMsg, sizeof(errnoMsg));
                     message.append(errnoMsg);

                     OsSysLog::add(FAC_SUPERVISOR, PRI_ERR, "HttpFileAccess::processRequest from %s %s",
                                   peerName.data(), message.data());

                     response->setResponseFirstHeaderLine(HTTP_PROTOCOL_VERSION,
                                                          httpStatusCode,
                                                          httpStatusText);
                     response->setBody(new HttpBody(message.data(),message.length()));
                     response->setContentType(CONTENT_TYPE_TEXT_PLAIN);
                     response->setContentLength(message.length());
                  }                  
               }
               else
               {
                  message.append("File to be deleted '");
                  message.append(path);
                  message.append("' does not exist");
                  OsSysLog::add(FAC_SUPERVISOR, PRI_INFO, "HttpFileAccess::processRequest from %s %s",
                                peerName.data(), message.data());
                  
                  response->setResponseFirstHeaderLine(HTTP_PROTOCOL_VERSION_1_1,
                                                       HTTP_OK_CODE, "File does not exist");
                  response->setContentLength(0);
               }
            }
            else
            {
               message.append("resource ");
               resource->appendDescription(message);
               message.append(" does not allow write access to '");
               message.append(path);
               message.append("'");
               OsSysLog::add(FAC_SUPERVISOR, PRI_ERR, "HttpFileAccess::processRequest from %s %s",
                             peerName.data(), message.data());

               response->setResponseFirstHeaderLine(HTTP_PROTOCOL_VERSION,
                                                    HTTP_FORBIDDEN_CODE,
                                                    "Access denied by process definition");
               response->setBody(new HttpBody(message.data(),message.length()));
               response->setContentType(CONTENT_TYPE_TEXT_PLAIN);
               response->setContentLength(message.length());
            }
         }
         else
         {
            message.append("File resource '");
            message.append(path);
            message.append("' not known to sipXsupervisor.");
            OsSysLog::add(FAC_SUPERVISOR, PRI_ERR, "HttpFileAccess::processRequest from %s %s",
                          peerName.data(), message.data());

            response->setResponseFirstHeaderLine(HTTP_PROTOCOL_VERSION,
                                                 HTTP_FILE_NOT_FOUND_CODE,
                                                 HTTP_FILE_NOT_FOUND_TEXT);
            response->setBody(new HttpBody(message.data(),message.length()));
            response->setContentType(CONTENT_TYPE_TEXT_PLAIN);
            response->setContentLength(message.length());
         }
      }
      else
      {
         OsSysLog::add(FAC_SUPERVISOR, PRI_ERR, "HttpFileAccess::processRequest %s from %s",
                       HTTP_UNSUPPORTED_METHOD_TEXT, peerName.data());

         response->setResponseFirstHeaderLine(HTTP_PROTOCOL_VERSION,
                                              HTTP_UNSUPPORTED_METHOD_CODE,
                                              HTTP_UNSUPPORTED_METHOD_TEXT);
      }
   }
   else
   {
      message.append("Request not supported from untrusted peer.");
      OsSysLog::add(FAC_SUPERVISOR, PRI_ERR, "HttpFileAccess::processRequest %s",
                    message.data());

      response->setResponseFirstHeaderLine(HTTP_PROTOCOL_VERSION,
                                           HTTP_FORBIDDEN_CODE, HTTP_FORBIDDEN_TEXT);

      response->setBody(new HttpBody(message.data(),message.length()));
      response->setContentType(CONTENT_TYPE_TEXT_PLAIN);
      response->setContentLength(message.length());
   }
}
예제 #10
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;
   }
예제 #11
0
    void testOneShotAfter()
    {
       struct TestOneShotStruct
       {
          const char* testDescription;
          long seconds;
          long milliseconds;
          int tolerance;
       };

       OsCallback* pNotifier;
       string Message;
       int testCount;

       TestOneShotStruct testData[] = {
          { "Test one shot after when time is specified as 0", 0, 0, OSTIMETOLERANCE },
          { "Test one shot after when time is equal to one second", 1, 0,
            OSTIMETOLERANCE },
          /* The next case was added to check if the inaccuracy applies ONLY to decimal
             values or even to integers
          */
          { "Test one shot after when time is greater than one second", 2, 285,
            OSTIMETOLERANCE },
          { "Test one shot after when time is an integer > 1", 3, 0, OSTIMETOLERANCE },
          { "Test one shot after when time is greater than 0 but less than 1", 0, 252,
            OSTIMETOLERANCE },
       };

       testCount = sizeof(testData)/ sizeof(testData[0]);

       for (int i = 0; i < testCount; i++)
       {
          long expectedWaitUSecs;
          OsTimer* pTimer;
          UtlBoolean returnValue;

          OsTime timeToWait(testData[i].seconds,
                            testData[i].milliseconds*OsTime::USECS_PER_MSEC);

          pNotifier = new OsCallback((void*)this, TVCallback);
          pTimer = new OsTimer(*pNotifier);

          expectedWaitUSecs = SecsToUsecs(testData[i].seconds) +
             MsecsToUsecs(testData[i].milliseconds);

          // Give a small delay so we synchronize with the timer.
          OsTask::delay(20);
          gettimeofday(&startTV, NULL);
          gCallBackCount = 0;
          returnValue = pTimer->oneshotAfter(timeToWait);

          OsTask::delay(expectedWaitUSecs / OsTime::USECS_PER_MSEC +
                        testData[i].tolerance);

          // gCallBackCount is reinitialized to 0 each iteration, so
          // its value should be 1 now.

          UtlString failureMessage;

          failureMessage.remove(0);
          failureMessage.append("Timer did not fire for iteration ");
          failureMessage.appendNumber(i);
          failureMessage.append("\n");
          failureMessage.append(testData[i].testDescription);
          failureMessage.append("\n  seconds:      ");
          failureMessage.appendNumber(testData[i].seconds);
          failureMessage.append("\n  milliseconds: ");
          failureMessage.appendNumber(testData[i].milliseconds);
          failureMessage.append("\n  tolerance:    ");
          failureMessage.appendNumber(testData[i].tolerance);

          KNOWN_BUG("Fails on ecs-fc8. Timing issue.", "XECS-1975");
          CPPUNIT_ASSERT_MESSAGE(failureMessage.data(), gCallBackCount == 1);

          failureMessage.remove(0);
          failureMessage.append("oneshotAfter returned failure on iteration ");
          failureMessage.appendNumber(i);
          failureMessage.append("\n");
          failureMessage.append(testData[i].testDescription);
          failureMessage.append("\n  seconds:      ");
          failureMessage.appendNumber(testData[i].seconds);
          failureMessage.append("\n  milliseconds: ");
          failureMessage.appendNumber(testData[i].milliseconds);
          failureMessage.append("\n  tolerance:    ");
          failureMessage.appendNumber(testData[i].tolerance);
          CPPUNIT_ASSERT_MESSAGE(failureMessage.data(), returnValue);

          REPORT_SKEW(("      Timing inaccuracy for iter %3d = %8ld us; Time = %ld.%03ld;\n",
                       i,
                       getTimeDeltaInUsecs() - expectedWaitUSecs,
                       testData[i].seconds,
                       testData[i].milliseconds
                         ));
          delete pTimer;
          delete pNotifier;
       }
    }
예제 #12
0
bool FileRpcReplaceFile::execute(const HttpRequestContext& requestContext,
                                 UtlSList&                 params,
                                 void*                     userData,
                                 XmlRpcResponse&           response,
                                 ExecutionStatus&          status)
{

   const int  minPermissions = 0100;
   const int  maxPermissions = 0777;

   bool result = false;
   status = XmlRpcMethod::FAILED;

   if (4 != params.entries())
   {
      handleExtraExecuteParam(name(), response, status);
   }
   else
   {
      if (!params.at(0) || !params.at(0)->isInstanceOf(UtlString::TYPE))
      {
         handleMissingExecuteParam(name(), PARAM_NAME_CALLING_HOST, response, status);
      }
      else
      {
         UtlString* pCallingHostname = dynamic_cast<UtlString*>(params.at(0));

         if (!params.at(1) || !params.at(1)->isInstanceOf(UtlString::TYPE))
         {
            handleMissingExecuteParam(name(), PARAM_NAME_FILE_NAME, response, status);
         }
         else
         {
            UtlString* pfileName = dynamic_cast<UtlString*>(params.at(1));

            if (!params.at(2) || !params.at(2)->isInstanceOf(UtlInt::TYPE))
            {
               handleMissingExecuteParam(name(), PARAM_NAME_FILE_PERMISSIONS, response, status);
            }
            else
            {
               UtlInt* pfilePermissions = dynamic_cast<UtlInt*>(params.at(2));
           
               if (!params.at(3) || !params.at(3)->isInstanceOf(UtlString::TYPE))
               {
                  handleMissingExecuteParam(name(), PARAM_NAME_FILE_DATA, response, status);
               }
               else
               {
                  UtlBool method_result(true);
                  SipxRpc* pSipxRpcImpl = ((SipxRpc *)userData);

                  if(validCaller(requestContext, *pCallingHostname, response, *pSipxRpcImpl, name()))
                  {
                     // Check the resource permissions.  To be added when available.
                     FileResource* fileResource =
                        FileResourceManager::getInstance()->find(pfileName->data());
                     if (!fileResource)
                     {
                        UtlString faultMsg;
                        faultMsg.append("File '");
                        faultMsg.append(*pfileName);
                        faultMsg.append("' not declared as a resource by any sipXecs process");
                        OsSysLog::add(FAC_SUPERVISOR, PRI_ERR, "FileRpc::replaceFile %s",
                                      faultMsg.data());
                        result=false;
                        response.setFault(FileRpcMethod::InvalidParameter, faultMsg);
                     }
                     else if (!fileResource->isWriteable())
                     {
                        UtlString faultMsg;
                        faultMsg.append("File '");
                        faultMsg.append(*pfileName);
                        faultMsg.append("' is not writeable (configAccess='read-only')");
                        OsSysLog::add(FAC_SUPERVISOR, PRI_ERR, "FileRpc::replaceFile %s",
                                      faultMsg.data());
                        result=false;
                        response.setFault(FileRpcMethod::InvalidParameter, faultMsg);
                     }
                     else if (   ( pfilePermissions->getValue() <= minPermissions )
                              || ( pfilePermissions->getValue() > maxPermissions ))
                     {
                        UtlString faultMsg;
                        faultMsg.appendNumber(pfilePermissions->getValue(),"File permissions %04o");
                        faultMsg.appendNumber(minPermissions,"not within valid range (%04o - ");
                        faultMsg.appendNumber(maxPermissions,"%04o)");
                        OsSysLog::add(FAC_SUPERVISOR, PRI_ERR, "FileRpc::replaceFile %s",
                                      faultMsg.data());
                        result = false;
                        response.setFault(FileRpcMethod::InvalidParameter, faultMsg);
                     }
                     else
                     {
                        // Write out the file.
                        UtlString* pfileData = dynamic_cast<UtlString*>(params.at(3));
                        UtlString faultMsg;
                        if((result=replicateFile(*pfileName,*pfilePermissions,*pfileData,faultMsg )))
                        {
                           // Construct and set the response.
                           response.setResponse(&method_result);
                           status = XmlRpcMethod::OK;

                           // Tell anyone who cares that this changed
                           fileResource->modified();
                        }
                        else
                        {
                           // Replication failed.
                           response.setFault(FileRpcMethod::InvalidParameter, faultMsg);
                        }
                     }
                  }
               }
            }
         }
      }
   }

   return result;
}
// Constructor
SipPersistentSubscriptionMgr::SipPersistentSubscriptionMgr(
   const UtlString& component,
   const UtlString& domain,
   const UtlString& fileName) :
   mComponent(component),
   mDomain(domain),
   mSubscriptionDBInstance(SubscriptionDB::getInstance(fileName)),
   mPersistenceTimer(mPersistTask.getMessageQueue(), 0),
   mPersistTask(mSubscriptionDBInstance)
{
   OsSysLog::add(FAC_SIP, PRI_DEBUG,
                 "SipPersistentSubscriptionMgr:: "
                 "mComponent = '%s', mDomain = '%s', fileName = '%s'",
                 mComponent.data(), mDomain.data(), fileName.data());

   // Start the persist task.
   mPersistTask.start();

   // Read the subscription table and initialize the SipSubscriptionMgr.

   unsigned long now = OsDateTime::getSecsSinceEpoch();
   ResultSet rs;
   mSubscriptionDBInstance->getAllRows(rs);
   UtlSListIterator itor(rs);
   UtlHashMap* rowp;
   while ((rowp = dynamic_cast <UtlHashMap*> (itor())))
   {
      if (OsSysLog::willLog(FAC_SIP, PRI_DEBUG))
      {
         UtlString out;
         UtlHashMapIterator itor(*rowp);
         UtlString* key;
         UtlContainable* value;
         while ((key = dynamic_cast <UtlString*> (itor())))
         {
            value = itor.value();
            if (!out.isNull())
            {
               out.append(", ");
            }
            out.append(*key);
            out.append(" = ");
            if (value->getContainableType() == UtlString::TYPE)
            {
               out.append("'");
               out.append(*(dynamic_cast <UtlString*> (value)));
               out.append("'");
            }
            else if (value->getContainableType() == UtlInt::TYPE)
            {
               out.appendNumber((int) (*(dynamic_cast <UtlInt*> (value))));
            }
            else
            {
               out.append(value->getContainableType());
            }
         }
         OsSysLog::add(FAC_SIP, PRI_DEBUG,
                       "SipPersistentSubscriptionMgr:: "
                       "table row: %s",
                       out.data());
      }

      // First, filter for rows that have the right component and have
      // not yet expired.
      UtlString* componentp =
         dynamic_cast <UtlString*> (rowp->findValue(&SubscriptionDB::gComponentKey));
      assert(componentp);
      int expires =
         *(dynamic_cast <UtlInt*> (rowp->findValue(&SubscriptionDB::gExpiresKey)));
      if (componentp->compareTo(mComponent) == 0 &&
          expires - now >= 0)
      {
         OsSysLog::add(FAC_SIP, PRI_DEBUG,
                       "SipPersistentSubscriptionMgr:: "
                       "loading row");

         // Extract the values from the row.
         const UtlString* top =
            dynamic_cast <UtlString*> (rowp->findValue(&SubscriptionDB::gToKey));
         const UtlString* fromp =
            dynamic_cast <UtlString*> (rowp->findValue(&SubscriptionDB::gFromKey));
         const UtlString* callidp =
            dynamic_cast <UtlString*> (rowp->findValue(&SubscriptionDB::gCallidKey));
         const UtlString* eventtypep =
            dynamic_cast <UtlString*> (rowp->findValue(&SubscriptionDB::gEventtypeKey));
         const UtlString* eventidp =
            dynamic_cast <UtlString*> (rowp->findValue(&SubscriptionDB::gIdKey));
         // Correct the null string if it is returned as
         // SPECIAL_IMDB_NULL_VALUE.
         if (eventidp->compareTo(special_imdb_null_value) == 0)
         {
            eventidp = &null_string;
         }
         int subcseq =
            *(dynamic_cast <UtlInt*> (rowp->findValue(&SubscriptionDB::gSubscribecseqKey)));
         const UtlString* urip =
            dynamic_cast <UtlString*> (rowp->findValue(&SubscriptionDB::gUriKey));
         const UtlString* contactp =
            dynamic_cast <UtlString*> (rowp->findValue(&SubscriptionDB::gContactKey));
         const UtlString* routep =
            dynamic_cast <UtlString*> (rowp->findValue(&SubscriptionDB::gRecordrouteKey));
         // Correct the null string if it is returned as
         // SPECIAL_IMDB_NULL_VALUE.
         if (routep->compareTo(special_imdb_null_value) == 0)
         {
            routep = &null_string;
         }
         int notifycseq =
            *(dynamic_cast <UtlInt*> (rowp->findValue(&SubscriptionDB::gNotifycseqKey)));
         const UtlString* acceptp =
            dynamic_cast <UtlString*> (rowp->findValue(&SubscriptionDB::gAcceptKey));
         int version =
            *(dynamic_cast <UtlInt*> (rowp->findValue(&SubscriptionDB::gVersionKey)));

         // Use SipSubscriptionMgr to update the in-memory data.

         // Construct a fake SUBSCRIBE request to carry most of the data
         // that updateDialogInfo needs.
         SipMessage subscribeRequest;
         subscribeRequest.setSubscribeData(urip->data(),
                                           fromp->data(),
                                           top->data(),
                                           callidp->data(),
                                           subcseq,
                                           eventtypep->data(),
                                           acceptp->data(),
                                           eventidp->data(),
                                           contactp->data(),
                                           routep->data(),
                                           expires - now);

         // Variables to hold the output of insertDialogInfo.
         UtlString subscribeDialogHandle;
         UtlBoolean isNew, isSubscriptionExpired;
         SipMessage subscribeResponse;
         UtlBoolean ret =
            SipSubscriptionMgr::insertDialogInfo(subscribeRequest,
                                                 // *urip is the request-URI of
                                                 // of the SUBSCRIBE.
                                                 *urip,
                                                 *eventtypep,
                                                 subscribeDialogHandle,
                                                 isNew,
                                                 isSubscriptionExpired,
                                                 subscribeResponse);
         if (!ret)
         {
            OsSysLog::add(FAC_SIP, PRI_ERR,
                          "SipPersistentSubscriptionMgr:: "
                          "insertDialogInfo failed urip = '%s', subscribeDialogHandle = '%s'",
                          urip->data(), subscribeDialogHandle.data());
         }
         else
         {
            // Set the next NOTIFY CSeq value.
            // (The data in IMDB has already been set.)
            SipSubscriptionMgr::setNextNotifyCSeq(subscribeDialogHandle,
                                                  notifycseq,
                                                  version);
         }
      }
   }
}
예제 #14
0
// Do preliminary processing of message to log it,
// clean up its data, and extract any needed source address.
void SipClient::preprocessMessage(SipMessage& msg,
                                  const UtlString& msgText,
                                  int msgLength)
{
   // Canonicalize short field names.
   msg.replaceShortFieldNames();

   // Get the send address.
   UtlString fromIpAddress;
   int fromPort;
   msg.getSendAddress(&fromIpAddress, &fromPort);

   // Log the message.
   // Only bother processing if the logs are enabled
   if (   mpSipUserAgent->isMessageLoggingEnabled()
          || Os::Logger::instance().willLog(FAC_SIP_INCOMING, PRI_INFO)
      )
   {
      UtlString logMessage;
      logMessage.append("Read SIP message:\n");
      logMessage.append("----Local Host:");
      logMessage.append(mLocalHostAddress);
      logMessage.append("---- Port: ");
      logMessage.appendNumber(
                 portIsValid(mLocalHostPort) ? mLocalHostPort : defaultPort());
      logMessage.append("----\n");
      logMessage.append("----Remote Host:");
      logMessage.append(fromIpAddress);
      logMessage.append("---- Port: ");
      logMessage.appendNumber(
                 portIsValid(fromPort) ? fromPort : defaultPort());
      logMessage.append("----\n");

      logMessage.append(msgText.data(), msgLength);
      UtlString messageString;
      logMessage.append(messageString);
      logMessage.append("====================END====================\n");

      // Send the message to the SipUserAgent for its internal log.
      mpSipUserAgent->logMessage(logMessage.data(), logMessage.length());
      // Write the message to the syslog.
      Os::Logger::instance().log(FAC_SIP_INCOMING, PRI_INFO, "%s", logMessage.data());
   }

   // Set the date field if not present
   long epochDate;
   if (!msg.getDateField(&epochDate))
   {
      msg.setDateField();
   }

   // Set the protocol and time.
   msg.setSendProtocol(mSocketType);
   msg.setTransportTime(touchedTime);

   // Keep track of where this message came from
   msg.setSendAddress(fromIpAddress.data(), fromPort);

   // Keep track of the interface on which this message was
   // received.
   msg.setInterfaceIpPort(mClientSocket->getLocalIp(), mClientSocket->getLocalHostPort());

   if (mReceivedAddress.isNull())
   {
      mReceivedAddress = fromIpAddress;
      mRemoteReceivedPort = fromPort;
   }

   // If this is a request...
   if (!msg.isResponse())
   {
      UtlString lastAddress;
      int lastPort;
      UtlString lastProtocol;
      int receivedPort;
      UtlBoolean receivedSet;
      UtlBoolean maddrSet;
      UtlBoolean receivedPortSet;

      // Fill in 'received' and 'rport' in top Via if needed.
      msg.setReceivedViaParams(fromIpAddress, fromPort);

      // Derive information about the other end of the connection
      // from the Via that the other end provided.

      // Get the addresses from the topmost Via.
      msg.getTopVia(&lastAddress, &lastPort, &lastProtocol,
                    &receivedPort, &receivedSet, &maddrSet,
                    &receivedPortSet);

      // :QUERY: Should this test be mClientSocket->isConnected()?
      if (   (   mSocketType == OsSocket::TCP
                 || mSocketType == OsSocket::SSL_SOCKET
                )
             && !receivedPortSet
         )
      {
         // we can use this socket as if it were
         // connected to the port specified in the
         // via field
         mRemoteReceivedPort = lastPort;
      }

      // Keep track of the address the other
      // side said they sent from.  Note, this cannot
      // be trusted unless this transaction is
      // authenticated
      if (mRemoteViaAddress.isNull())
      {
         mRemoteViaAddress = lastAddress;
         mRemoteViaPort =
            portIsValid(lastPort) ? lastPort : defaultPort();
      }
   }

   //
   // Call all sip input processors
   //
   system_tap_sip_rx(fromIpAddress.data(), portIsValid(fromPort) ? fromPort : defaultPort(),
           mLocalHostAddress.data(), portIsValid(mLocalHostPort) ? mLocalHostPort : defaultPort(),
           msgText.data(), msgLength);

   mpSipUserAgent->executeAllSipInputProcessors(msg, fromIpAddress.data(), portIsValid(fromPort) ? fromPort : defaultPort());
}
예제 #15
0
// Thread execution code.
int SipClient::run(void* runArg)
{
   OsMsg*    pMsg = NULL;
   OsStatus  res;
   // Buffer to hold data read from the socket but not yet parsed
   // into incoming SIP messages.
   UtlString readBuffer;
   bool      waitingToReportErr  = FALSE;    // controls whether to read-select on socket
   bool      tcpOnErrWaitForSend = TRUE;
   int       repeatedEOFs = 0;

   Os::Logger::instance().log(FAC_SIP, PRI_DEBUG,
                 "SipClient[%s]::run start  "
                 "tcpOnErrWaitForSend-%d waitingToReportErr-%d mbTcpOnErrWaitForSend-%d repeatedEOFs-%d",
                 mName.data(), tcpOnErrWaitForSend, waitingToReportErr,
                 mbTcpOnErrWaitForSend, repeatedEOFs);

   // Wait structure:
   struct pollfd fds[2];
   // Incoming message on the message queue (to be sent on the socket).
   fds[0].fd = mPipeReadingFd;
   // Socket ready to write (to continue sending message).
   // Socket ready to read (message to be received).

   do
   {
      assert(repeatedEOFs < 20);
      // The file descriptor for the socket may changemsg->getSendAddress(&fromIpAddress, &fromPort);, as OsSocket's
      // can be re-opened.
      fds[1].fd = mClientSocket->getSocketDescriptor();

      // Initialize the revents members.
      // This may not be necessary (the man page is not clear), but
      // Valgrind flags 'fds[*].revents' as undefined if they aren't
      // initialized.
      fds[0].revents = 0;
      fds[1].revents = 0;

      fds[0].events = POLLIN;   // only read-select on pipe

      // For non-blocking connect failures, don't read-select on socket if
      // the initial read showed an error but we have to wait to report it.
      if (!waitingToReportErr)
      {
          // This is the normal path.
          // Read the socket only if the socket is not shared.
          // If it is shared, the ancestral SipClient will read it.
          // If multiple threads attempt to read the socket, poll() may
          // succeed but another may read the data, leaving us to block on
          // read.
          fds[1].events = mbSharedSocket ? 0 : POLLIN;

          // Set wait for writing the socket if there is queued messages to
          // send.
          if (mWriteQueued)
          {
             // Wait for output on the socket to not block.
             fds[1].events |= POLLOUT;
          }

      }
      else
      {
          // just waiting to report error, ignore the socket
          fds[1].fd =-1;
          fds[1].events = 0;
      }

      // If there is residual data in the read buffer,
      // pretend the socket is ready to read.
      if (!readBuffer.isNull())
      {
         fds[1].revents = POLLIN;
      }
      else
      {
         // Otherwise, call poll() to wait.
         int resPoll = poll(&fds[0], sizeof (fds) / sizeof (fds[0]),
                        POLL_TIMEOUT);
         assert(resPoll >= 0 || (resPoll == -1 && errno == EINTR));
         if (resPoll != 0)
         {
             Os::Logger::instance().log(FAC_SIP, PRI_DEBUG,
                           "SipClient[%s]::run "
                           "resPoll= %d revents: fd[0]= %x fd[1]= %x",
                           mName.data(),
                           resPoll, fds[0].revents, fds[1].revents );
         }
      }

      if ((fds[1].revents & (POLLERR | POLLHUP)) != 0)
      {
          Os::Logger::instance().log(FAC_SIP, PRI_DEBUG,
                        "SipClient[%s]::run "
                        "SipMessage::poll error(%d) ",
                        mName.data(), errno);

          if (OsSocket::isFramed(mClientSocket->getIpProtocol()))
          {
              Os::Logger::instance().log(FAC_SIP, PRI_ERR,
                            "SipClient[%s]::run "
                            "SipMessage::poll error(%d) got POLLERR | POLLHUP on UDP socket",
                            mName.data(), errno);

          }
          else	// eg. tcp socket
          // This client's socket is a connection-oriented protocol and the
          // connection has been terminated (probably by the remote end).
          // We must terminate the SipClient.
          // We drop the queued messages, but we do not report them to
          // SipUserAgent as failed sends.  This will cause SipUserAgent to
          // retry the send using the same transport (rather than continuing
          // to the next transport), which should cause a new connection to
          // be made to the remote end.
          {
              // On non-blocking connect failures, we need to get the first send message
              // in order to successfully trigger the protocol fallback mechanism
              if (!tcpOnErrWaitForSend)
              {
                 // Return all buffered messages with a transport error indication.
                 emptyBuffer(TRUE);
                 clientStopSelf();
              }
              else
              {
                 fds[1].revents &= ~(POLLERR | POLLHUP);  // clear error bit if waiting
                 waitingToReportErr = TRUE;
              }
          }
      }

      // Check for message queue messages (fds[0]) before checking the socket(fds[1]),
      // to make sure that we process shutdown messages promptly, even
      // if we would be spinning trying to service the socket.
      else if ((fds[0].revents & POLLIN) != 0)
      {
         // Poll finished because the pipe is ready to read.
         // (One byte in pipe means message available in queue.)
         // Only a SipClient with a derived SipClientWriteBuffer
         // uses the pipe in the Sip message send process

         // Check to see how many messages are in the queue.
         int numberMsgs = (getMessageQueue())->numMsgs();
         Os::Logger::instance().log(FAC_SIP, PRI_DEBUG,
                       "SipClient[%s]::run got pipe-select  "
                       "Number of Messages waiting: %d",
                       mName.data(),
                       numberMsgs);
         int i;
         char buffer[1];
         for (i = 0; i < numberMsgs; i++)
         {
            // Receive the messages.
            res = receiveMessage((OsMsg*&) pMsg, OsTime::NO_WAIT);
            assert(res == OS_SUCCESS);

            // Normally, this is a SIP message for the write buffer.  Once we have gotten
            // here, we are able to report any initial non-blocking connect error.
            mbTcpOnErrWaitForSend = FALSE;
            tcpOnErrWaitForSend = FALSE;
            Os::Logger::instance().log(FAC_SIP, PRI_DEBUG,
                          "SipClient[%s]::run got pipe-select  "
                          "mbTcpOnErrWaitForSend-%d waitingToReportErr-%d mbTcpOnErrWaitForSend-%d repeatedEOFs-%d",
                          mName.data(), mbTcpOnErrWaitForSend, waitingToReportErr,
                          mbTcpOnErrWaitForSend, repeatedEOFs);

            // Read 1 byte from the pipe to clear it for this message.  One byte is
            // inserted into the pipe for each message.
            assert(read(mPipeReadingFd, &buffer, 1) == 1);

            if (!handleMessage(*pMsg))            // process the message (from queue)
            {
               OsServerTask::handleMessage(*pMsg);
            }

            if (!pMsg->getSentFromISR())
            {
               pMsg->releaseMsg();                         // free the message
            }

            // In order to report an unframed(eg TCP) socket error to SipUserAgent dispatcher,
            // the error must be carried in a sip message from the client's message queue.
            // The message holds all the identifying information.
            if (waitingToReportErr)
            {
                // Return all buffered messages with a transport error indication.
                emptyBuffer(TRUE);
                clientStopSelf();
            }
         }
      } // end reading msg-available-for-output-queue pipe

      else if ((fds[1].revents & POLLOUT) != 0)
      {
         // Poll finished because socket is ready to write.

         // Call method to continue writing data.
         writeMore();
      }
      else if ((fds[1].revents & POLLIN) != 0)
      {
         // Poll finished because socket is ready to read.

         // Read message.
         // Must allocate a new message because SipUserAgent::dispatch will
         // take ownership of it.

         SipMessage* msg = new SipMessage;
         int res = msg->read(mClientSocket,
                             HTTP_DEFAULT_SOCKET_BUFFER_SIZE,
                             &readBuffer);

         if (res >= 65536)
         {
           //
           // This is more than the allowable size of a SIP message.  Discard!
           //
            UtlString remoteHostAddress;
            int remoteHostPort;
            msg->getSendAddress(&remoteHostAddress, &remoteHostPort);
            OS_LOG_WARNING(FAC_SIP, "Received a SIP Message ("
             << res << " bytes) beyond the maximum allowable size from host "
             << remoteHostAddress.data() << ":" << remoteHostPort);
            delete msg;
            readBuffer.remove(0);
            continue;
         }

         // Use readBuffer to hold any unparsed data after the message
         // we read.
         // Note that if a message was successfully parsed, readBuffer
         // still contains as its prefix the characters of that message.
         // We save them for logging purposes below and will delete them later.

         UtlString remoteHostAddress;
         int remoteHostPort;
         msg->getSendAddress(&remoteHostAddress, &remoteHostPort);
         if (!mClientSocket->isSameHost(remoteHostAddress.data(), mLocalHostAddress.data()))
         {
           try
           {
             if (!remoteHostAddress.isNull())
             {
               boost::asio::ip::address remoteIp = boost::asio::ip::address::from_string(remoteHostAddress.data());

               if (rateLimit().isBannedAddress(remoteIp))
               {
                  delete msg;
                  readBuffer.remove(0);
                  continue;
               }

               rateLimit().logPacket(remoteIp, 0);
             }
           }
           catch(const std::exception& e)
           {
             Os::Logger::instance().log(FAC_SIP_INCOMING, PRI_CRIT, 
               "SipClient[%s]::run rate limit exception: %s",  mName.data(), e.what());
           }
         }


         // Note that input was processed at this time.
         touch();

         //
         // Count the CR/LF to see if this is a keep-alive
         //
         int crlfCount = 0;
         for (int i = 0; i < res; i++)
         {
           if (readBuffer(i) == '\r' || readBuffer(i) == '\n')
           {
             crlfCount++;
           } else
           {
             break;
           }
         }

         if (res == crlfCount)
         {
             repeatedEOFs = 0;
             // The 'message' was a keepalive (CR-LF or CR-LF-CR-LF).
             UtlString fromIpAddress;
             int fromPort;
             UtlString buffer;
             int bufferLen;

             // send one CRLF set in the reply
             buffer.append("\r\n");
             bufferLen = buffer.length();

             // Get the send address for response.
             msg->getSendAddress(&fromIpAddress, &fromPort);
             if ( !portIsValid(fromPort))
             {
                 fromPort = defaultPort();
             }

            // Log the message at DEBUG level.
            // Only bother processing if the logs are enabled
            if (   mpSipUserAgent->isMessageLoggingEnabled()
                   || Os::Logger::instance().willLog(FAC_SIP_INCOMING, PRI_DEBUG)
               )
            {
               UtlString logMessage;
               logMessage.append("Read keepalive message:\n");
               logMessage.append("----Local Host:");
               logMessage.append(mLocalHostAddress);
               logMessage.append("---- Port: ");
               logMessage.appendNumber(
                  portIsValid(mLocalHostPort) ? mLocalHostPort : defaultPort());
               logMessage.append("----\n");
               logMessage.append("----Remote Host:");
               logMessage.append(fromIpAddress);
               logMessage.append("---- Port: ");
               logMessage.appendNumber(
                  portIsValid(fromPort) ? fromPort : defaultPort());
               logMessage.append("----\n");

               logMessage.append(readBuffer.data(), res);
               UtlString messageString;
               logMessage.append(messageString);
               logMessage.append("====================END====================\n");

               // Don't bother to send the message to the SipUserAgent for its internal log.

               // Write the message to the syslog.
               Os::Logger::instance().log(FAC_SIP_INCOMING, PRI_DEBUG, "%s", logMessage.data());
            }

            // send the CR-LF response message
            switch (mSocketType)
            {
            case OsSocket::TCP:
            {
               Os::Logger::instance().log(FAC_SIP, PRI_DEBUG,
                             "SipClient[%s]::run send TCP keep-alive CR-LF response, ",
                             mName.data());
               SipClientSendMsg sendMsg(OsMsg::OS_EVENT,
                                        SipClientSendMsg::SIP_CLIENT_SEND_KEEP_ALIVE,
                                        fromIpAddress,
                                        fromPort);
                handleMessage(sendMsg);     // add newly created keep-alive to write buffer
            }
               break;
            case OsSocket::UDP:
            {
                Os::Logger::instance().log(FAC_SIP, PRI_DEBUG,
                              "SipClient[%s]::run send UDP keep-alive CR-LF response, ",
                              mName.data());
               (dynamic_cast <OsDatagramSocket*> (mClientSocket))->write(buffer.data(),
                                                                         bufferLen,
                                                                         fromIpAddress,
                                                                         fromPort);
            }
               break;
            default:
               break;
            }

            // Delete the SipMessage allocated above, which is no longer needed.
            delete msg;

            // Now that logging is done, remove the parsed bytes and
            // remember any unparsed input for later use.
            readBuffer.remove(0, res);
         }  // end keep-alive msg

         else if (res > 0)      // got message, but not keep-alive
         {
            // Message successfully read.
            repeatedEOFs = 0;

            // Do preliminary processing of message to log it,
            // clean up its data, and extract any needed source address.
            preprocessMessage(*msg, readBuffer, res);

            // Dispatch the message.
            // dispatch() takes ownership of *msg.
            mpSipUserAgent->dispatch(msg);

            // Now that logging is done, remove the parsed bytes and
            // remember any unparsed input for later use.
            readBuffer.remove(0, res);
         }  // end process read of >0 bytes
         else
         {
            // Something went wrong while reading the message.
            // (Possibly EOF on a connection-oriented socket.)
            repeatedEOFs++;

            // Delete the SipMessage allocated above, which is no longer needed.
            delete msg;
            Os::Logger::instance().log(FAC_SIP, PRI_DEBUG,
                          "SipClient[%s]::run SipMessage::read returns %d (error(%d) or EOF), "
                          "readBuffer = '%.1000s'",
                          mName.data(), res, errno, readBuffer.data());

            Os::Logger::instance().log(FAC_SIP, PRI_DEBUG,
                          "SipClient[%s]::run error wait status  "
                          "tcpOnErrWaitForSend-%d waitingToReportErr-%d "
                          "mbTcpOnErrWaitForSend-%d repeatedEOFs-%d "
                          "protocol %d framed %d",
                          mName.data(),
                          tcpOnErrWaitForSend, waitingToReportErr,
                          mbTcpOnErrWaitForSend, repeatedEOFs,
                          mClientSocket->getIpProtocol(),
                          OsSocket::isFramed(mClientSocket->getIpProtocol()));

            // If the socket is not framed (is connection-oriented),
            // we need to abort the connection and post a message
            // :TODO: This doesn't work right for framed connection-oriented
            // protocols (like SCTP), but OsSocket doesn't have an EOF-query
            // method -- we need to close all connection-oriented
            // sockets as well in case it was an EOF.
            // Define a virtual function that returns the correct bit.
            if (!OsSocket::isFramed(mClientSocket->getIpProtocol()))
            {
                // On non-blocking connect failures, we need to get the first send message
                // in order to successfully trigger the protocol fallback mechanism
                if (!tcpOnErrWaitForSend)
                {
                   // Return all buffered messages with a transport error indication.
                   emptyBuffer(TRUE);
                   clientStopSelf();
                }
                else
                {
                   fds[1].revents &= ~(POLLERR | POLLHUP);  // clear error bit if waiting
                   waitingToReportErr = TRUE;
                }
            }
            // Delete the data read so far, which will not have been
            // deleted by HttpMessage::read.
            readBuffer.remove(0);
         }
      } // end POLLIN reading socket
   }
   while (isStarted());

   return 0;        // and then exit
}
예제 #16
0
// Add to the HttpBody the current state of the resource.
void ResourceCached::generateBody(UtlString& rlmi,
                                  HttpBody& body,
                                  UtlBoolean consolidated,
                                  const UtlString& nameXml,
                                  const UtlString& displayName) const
{
   // Generate the preamble for the resource.
   rlmi += "  <resource uri=\"";
   XmlEscape(rlmi, *(static_cast <const UtlString*> (this)));
   rlmi += "\">\r\n";
   if (!nameXml.isNull())
   {
      rlmi += "    ";
      rlmi += nameXml;
   }

   if (consolidated)
   {
      // If consolidating resource instances, generate the XML for the
      // unified resource instance.
      rlmi += "    <instance id=\"consolidated\" state=\"active\"";

      UtlString contentBodyPartCid;
      // Use the count of parts in 'body' to generate a unique identifier for
      // each part.
      contentBodyPartCid.appendNumber(body.getMultipartCount());
      contentBodyPartCid += "@";
      contentBodyPartCid += getResourceListServer()->getDomainName();

      rlmi += " cid=\"";
      rlmi += contentBodyPartCid;
      rlmi += "\"";

      // Now add the <...> and use it in the header.
      contentBodyPartCid.prepend("<");
      contentBodyPartCid.append(">");

      // Create a single HttpBody to contain the unified dialog event.
      UtlString dialog_event;

      // XML declaration is optional, but Broadworks uses it.
      dialog_event += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
      dialog_event += BEGIN_DIALOG_INFO;
      dialog_event += VERSION_EQUAL;
      dialog_event += "\"";
      // The consolidated dialog events need to have persistent
      // version numbers, as they all have the same instance id.
      // So we use a global version number in the ResourceListSet.
      dialog_event.appendNumber(getResourceListSet()->getVersion());
      dialog_event += "\"";
      dialog_event += STATE_EQUAL;
      dialog_event += "\"full\"";
      dialog_event += ENTITY_EQUAL;
      dialog_event += "\"";
      dialog_event += *(static_cast <const UtlString*> (this));
      dialog_event += "\">\r\n";
      // Save the length of dialog_event, so we can tell later if
      // any <dialog>s have been added to it.
      unsigned int preamble_length = dialog_event.length();

      // Call the ContactSet to generate the consolidated dialog event body.
      if (mContactSetP)
      {
         mContactSetP->generateBody(dialog_event, body, consolidated,
                                    displayName);
      }

      // If no <dialog>s have been added, we have to add a dummy
      // <dialog> to carry the display name.
      if (dialog_event.length() == preamble_length)
      {
         dialog_event +=
            "<dialog id=\";\"><state>terminated</state><local><identity display=\"";
         XmlEscape(dialog_event, displayName);
         dialog_event += "\">";
         XmlEscape(dialog_event, *(static_cast <const UtlString*> (this)));
         dialog_event += "</identity></local></dialog>\r\n";
      }

      dialog_event += END_DIALOG_INFO;

      // Insert the consolidated dialog event body into the multiplart body.
      HttpBody content_body(dialog_event.data(),
                            dialog_event.length(),
                            DIALOG_EVENT_CONTENT_TYPE);
      UtlDList content_body_parameters;
      content_body_parameters.append(
         new NameValuePair(HTTP_CONTENT_ID_FIELD,
                           contentBodyPartCid));
      body.appendBodyPart(content_body, content_body_parameters);
      content_body_parameters.destroyAll();

      // Finish the <instance> element.
      rlmi += "/>\r\n";
   }
   else
   {
      // Call the ContactSet to do the work.
      if (mContactSetP)
      {
         mContactSetP->generateBody(rlmi, body, consolidated, displayName);
      }
   }   
   
// Generate the postamble for the resource.
   rlmi += "  </resource>\r\n";
}
예제 #17
0
void HttpFileAccess::sendFile(const UtlString& path,
                              const UtlString& peerName,
                              const HttpRequestContext& requestContext,
                              const HttpMessage& request,
                              HttpMessage*& response
                              )
{
   UtlString message;
   
   int file = open(path.data(), O_RDONLY);
   if (0 <= file)
   {
      UtlString fileBuffer;
      if (fileBuffer.capacity(MAX_FILE_CHUNK_SIZE + 1) >= MAX_FILE_CHUNK_SIZE + 1)
      {
         response->setResponseFirstHeaderLine(HTTP_PROTOCOL_VERSION_1_1,
                                              HTTP_OK_CODE, HTTP_OK_TEXT);
         response->useChunkedBody(true);

         // @TODO Annotate file resources with a mime type?

         // add a Content-Disposition header to suggest saving as the basename of the file
         response->setContentType("application/octet-stream");
         UtlString dispositionValue;
         UtlString basename;
         ssize_t finalSlash = path.last('/');
         if (UTL_NOT_FOUND != finalSlash)
         {
            basename.append(path, finalSlash+1, path.length()-finalSlash);
         }
         else
         {
            basename.append(path); // don't think this should ever be true....
         }
         dispositionValue.append("attachment; filename=\"");
         dispositionValue.append(basename);
         dispositionValue.append("\"");
         response->setHeaderValue(HTTP_CONTENT_DISPOSITION_FIELD,dispositionValue.data());

         bool    writtenOk = response->writeHeaders(requestContext.socket());
         ssize_t bytes;
         Int64   totalBytes = 0;
         Int64   chunks = 0;
         while (   writtenOk
                && (bytes = read(file, (void*)fileBuffer.data(),
                                 MAX_FILE_CHUNK_SIZE)))
         {
            fileBuffer.setLength(bytes);
            writtenOk = response->writeChunk(requestContext.socket(), fileBuffer);
            if (writtenOk)
            {
               totalBytes += bytes;
               chunks++;
            }
            OsSysLog::add(FAC_SUPERVISOR, PRI_DEBUG,
                          "file block %"FORMAT_INTLL"d %zd %s", chunks, bytes,
                          writtenOk ? "ok" : "failed");
         }
         if (writtenOk)
         {
            response->writeEndOfChunks(requestContext.socket());

            message.append(" sent file '");
            message.append(path);
            message.append("' (");
            message.appendNumber(totalBytes,"%"FORMAT_INTLL"d");
            message.append(" bytes in ");
            message.appendNumber(chunks,"%"FORMAT_INTLL"d");
            message.append(" chunks) to peer ");
            message.append(peerName);
            OsSysLog::add(FAC_SUPERVISOR, PRI_INFO, "HttpFileAccess::processRequest %s",
                          message.data());
         }
         else
         {
            message.append("error writing response after ");
            message.appendNumber(totalBytes,"%"FORMAT_INTLL"d");
            message.append(" body bytes in ");
            message.appendNumber(chunks,"%"FORMAT_INTLL"d");
            message.append(" chunks) to peer ");
            message.append(peerName);
            OsSysLog::add(FAC_SUPERVISOR, PRI_ERR, "HttpFileAccess::processRequest %s",
                          message.data());
         }

         /*
          * We've already written the response, so prevent HttpServer from sending it by
          * not passing it back.
          */
         delete response;
         response = NULL;
      }
      else
      {
         // Send out-of-resources message
         message.append("Supervisor Buffer Exhausted");
         response->setResponseFirstHeaderLine(HTTP_PROTOCOL_VERSION,
                                              HTTP_OUT_OF_RESOURCES_CODE,
                                              message.data());
         response->setContentType(CONTENT_TYPE_TEXT_PLAIN);
         response->setBody(new HttpBody(message.data(),message.length()));
         response->setContentLength(message.length());

         OsSysLog::add(FAC_SUPERVISOR, PRI_ERR,
                       "HttpFileAccess::processRequest from %s %s",
                       peerName.data(), message.data());
      }
   }
   else
   {
      int       httpStatusCode;
      UtlString httpStatusText;

      switch (errno)
      {
      case EACCES:
         httpStatusCode = HTTP_FORBIDDEN_CODE;
         httpStatusText = "File Access Denied";
         break;

      case ENOENT:
         httpStatusCode = HTTP_FILE_NOT_FOUND_CODE;
         httpStatusText = HTTP_FILE_NOT_FOUND_TEXT;
         break;

      default:
         httpStatusCode = HTTP_SERVER_ERROR_CODE;
         httpStatusText = HTTP_SERVER_ERROR_TEXT;
         break;
      }

      message.append("File '");
      message.append(path);
      message.append("' errno ");
      message.appendNumber(errno);
      message.append(" ");

      char errnoMsg[1024];
      strerror_r(errno, errnoMsg, sizeof(errnoMsg));
      message.append(errnoMsg);

      OsSysLog::add(FAC_SUPERVISOR, PRI_ERR, "HttpFileAccess::processRequest from %s %s",
                    peerName.data(), message.data());

      response->setResponseFirstHeaderLine(HTTP_PROTOCOL_VERSION,
                                           httpStatusCode,
                                           httpStatusText);
      response->setBody(new HttpBody(message.data(),message.length()));
      response->setContentType(CONTENT_TYPE_TEXT_PLAIN);
      response->setContentLength(message.length());
   }
}
예제 #18
0
   void instantiateAllTestFixtures( UtlString resourceListFile,
                                    //< Name of the resource list to use for the test.  The filename
                                    //< specified mus texist in .../sipXrls/src/test/rlsdata/
                                    UtlString subscriptionDbName, 
                                    //< Specifies the subscription DB to use for the test.  The name 
                                    //< is used to select which of the files in .../sipXrls/src/test/rlsdata/
                                    //< will get used to preload the subscription IMDB.  The name provided here is the 
                                    //< xml file in .../sipXrls/src/test/rlsdata/ without the ".xml" extension.
                                    UtlString credentialDbName,
                                    //< Specifies the credential DB to use for the test.  The name 
                                    //< is used to select which of the files in .../sipXrls/src/test/rlsdata/
                                    //< will get used to preload the credential IMDB.  The name provided here is the 
                                    //< xml file in .../sipXrls/src/test/rlsdata/ without the ".xml" extension.
                                    UtlString domianName,
                                    //< Specify the domain name to use for the test
                                    UtlBoolean routeAllRequestsToRlsServer
                                    //< Send all requests to either the RLS Server UA or the RLS Client UA
                                    //< The Client UA typically deals with the outgoing subscriptions and
                                    //< the Server UA deals witht the incoming subscriptions
                                    )
   {
      mCredentialDbName = credentialDbName;
      // force copy of input files into the 'work' directory
      sipDbContext.inputFile( subscriptionDbName + ".xml" );
      sipDbContext.inputFile( credentialDbName + ".xml" );
      UtlString tempResourceListFile = UtlString(TEST_DATA_DIR) + "/" + resourceListFile;

      pSipUserAgent = new SipUserAgent( 45141, 45141, 45142
                                       ,"127.0.0.1"  // default publicAddress
                                       ,NULL         // default defaultUser
                                       ,"127.0.0.1" );  // default defaultSipAddress

      // Stop SipUserAgent from rejecting all SUBSCRIBEs
      pSipUserAgent->allowMethod(SIP_SUBSCRIBE_METHOD, true);

      SipXecsService* rlsTestService = new SipXecsService("sipxrlsTest", "SIPX_RLS", "TestCase");

      pResourceServerUnderTest = new ResourceListServer(rlsTestService,
                                       domianName, // domain 
                                       "rlstest.test", // realm
                                       NULL, 
                                       DIALOG_EVENT_TYPE, 
                                      DIALOG_EVENT_CONTENT_TYPE,
                                       45140, // TCP port
                                       45140, // UDP port
                                       45140, // TLS port
                                       "127.0.0.1", // Bind IP address
                                       &tempResourceListFile,
                                       (60 * 60), // Default subscription resubscribe interval.
                                       (40 * 60), // Default minimum subscription resubscribe interval.
                                       250,  // publishing delay? 
                                       20,   // The maximum number of reg subscriptions per resource.
                                       20,   // The maximum number of contacts per reg subscription.
                                       20,   // The maximum number of resource instances per contact
                                       20,    // The maximum number of dialogs per resource instance
                                       32, 3600, 86400, // Subscribe server expiration parameters
                                       subscriptionDbName,
                                       credentialDbName );

      pUacOutputProcessor = new OutputProcessorFixture();
      pUasOutputProcessor = new OutputProcessorFixture();
      
      pResourceServerUnderTest->mClientUserAgent.addSipOutputProcessor( pUacOutputProcessor );
      pResourceServerUnderTest->mServerUserAgent.addSipOutputProcessor( pUasOutputProcessor );

      UtlString proxyAddress;
      if(routeAllRequestsToRlsServer)
      {
         proxyAddress = "127.0.0.1:45140";
      }
      else
      {
         int pPort;
         pResourceServerUnderTest->mClientUserAgent.getLocalAddress(&proxyAddress, &pPort);
         proxyAddress.append(':');
         proxyAddress.appendNumber(pPort);
      }

      pSipUserAgent->setProxyServers(proxyAddress.data());

      pResourceServerUnderTest->start();
   }
예제 #19
0
AuthPlugin::AuthResult
CallerAlias::authorizeAndModify(const UtlString& id,    /**< The authenticated identity of the
                                                         *   request originator, if any (the null
                                                         *   string if not).
                                                         *   This is in the form of a SIP uri
                                                         *   identity value as used in the
                                                         *   credentials database (user@domain)
                                                         *   without the scheme or any parameters.
                                                         */
                                const Url&  requestUri, ///< parsed target Uri
                                RouteState& routeState, ///< the state for this request.  
                                const UtlString& method,///< the request method
                                AuthResult  priorResult,///< results from earlier plugins.
                                SipMessage& request,    ///< see AuthPlugin regarding modifying
                                bool bSpiralingRequest, ///< spiraling indication 
                                UtlString&  reason      ///< rejection reason
                                )
{
   // get the call-id to use in logging
   UtlString callId;
   request.getCallIdField(&callId);

   if (   (priorResult != DENY) // no point in modifying a request that won't be sent
       )   
   {
      UtlString callerFrom;
      UtlString callerFromTagOffsetStr;
      UtlString aliasFrom;
      UtlString aliasFromTagOffsetStr;
      UtlString originalFromTag;

      if (   !routeState.getParameter(mInstanceName.data(), CALLER_FROM_PARAM, callerFrom)
          || !routeState.getParameter(mInstanceName.data(), CALLER_TAG_OFFSET_PARAM, callerFromTagOffsetStr)
          || !routeState.getParameter(mInstanceName.data(), ALIAS_FROM_PARAM, aliasFrom)
          || !routeState.getParameter(mInstanceName.data(), ALIAS_TAG_OFFSET_PARAM, aliasFromTagOffsetStr)
          || !routeState.originalCallerFromTagValue(mInstanceName.data(), originalFromTag)
          )
      {
         if (   routeState.isMutable()
             && routeState.directionIsCallerToCalled(mInstanceName.data())
             ) // a new dialog?
         {
            /*
             * Get the callers identity by getting the caller URI and:
             *    remove all parameters
             *    remove the scheme name
             */
            UtlString callerIdentity;

            UtlString originalFromField;
            request.getFromField(&originalFromField);
            Url originalFromUrl(originalFromField);
            
            /*
             * Extract the from identity as a key for the caller alias table
             * Start with the From header field (someday we should use the Identity if present)
             */
            Url fromUrl(originalFromUrl);
            fromUrl.removeParameters(); // parameters are not relevant for this 
         
            Url::Scheme fromUrlScheme = fromUrl.getScheme();
            switch (fromUrlScheme)
            {
            case Url::SipsUrlScheme:
               // sips and sip are equivalent for identity purposes,
               //   so just set to sip 
               fromUrl.setScheme(Url::SipUrlScheme);
               //   and fall through to extract the identity...

            case Url::SipUrlScheme:
               // case Url::TelUrlScheme: will go here, since 'tel' and 'sip' are the same length
               fromUrl.getUri(callerIdentity);
               callerIdentity.remove(0,4 /* strlen("sip:") */); // strip off the scheme name
               break;

            default:
               // for all other schemes, treat identity as null
               Os::Logger::instance().log(FAC_SIP, PRI_WARNING,
                             "CallerAlias[%s]::check4andApplyAlias From uses unsupported scheme '%s'"
                             " - using null identity",
                             mInstanceName.data(),
                             Url::schemeName(fromUrlScheme)
                             );
               break;
            }

            /*
             * Determine whether the identity is one for which this proxy
             * is authoritative; if not, we will not use wildcard matches.
             */
            bool identityIsLocal = mpSipRouter->isLocalDomain(fromUrl);
            
            // now we have callerIdentity set; use for looking up each contact.
            Os::Logger::instance().log(FAC_SIP, PRI_DEBUG,
                          "CallerAlias[%s]::check4andApplyAlias "
                          "\n  caller '%s' %s",
                          mInstanceName.data(),
                          callerIdentity.data(),
                          identityIsLocal ? "is local" : "is not local"
                          );

            /*
             * Examine the request URI,
             * checking for a caller alias set for its domain(including asssociated gateway sipxecsLineid)  with callerIdentity
             */

            UtlString sipxecsLineIdField;
            requestUri.getUrlParameter(SIPX_SIPXECS_LINEID_URI_PARAM, sipxecsLineIdField);

            Os::Logger::instance().log(FAC_SIP, PRI_DEBUG,
                             "getUrlParameter: sipxecsLineid[%s]"
                             " in CallerAlias",
                             sipxecsLineIdField.data()
                             );

            UtlString targetDomain;
            requestUri.getHostWithPort(targetDomain);

            if (!(sipxecsLineIdField.isNull()))
            {
                targetDomain.append(";").append(SIPX_SIPXECS_LINEID_URI_PARAM).append("=").append(sipxecsLineIdField.data());
            }

            Os::Logger::instance().log(FAC_SIP, PRI_DEBUG,
                          "CallerAlias::targetDomain [%s]",
                          targetDomain.data()
                          );

            // look up any caller alias for this identity and contact domain
            UtlString callerAlias;
            if (identityIsLocal && getCallerAlias(callerIdentity, targetDomain, callerAlias) )
            {
               // found a caller alias, so rewrite the From information
               /*
                * The From header requires special handling
                * - we need to preserve the tag, if any, from the original header
                */
               originalFromUrl.getFieldParameter("tag", originalFromTag);

               Url newFromUrl(callerAlias.data());
               newFromUrl.removeFieldParameter("tag"); // specifying a tag is a no-no
               if ( !originalFromTag.isNull() )
               {
                  newFromUrl.setFieldParameter("tag", originalFromTag.data());
               }
               UtlString newFromFieldValue;
               newFromUrl.toString(newFromFieldValue);
                   
               // log the change we are making before stripping the tag from the field values
               Os::Logger::instance().log( FAC_SIP, PRI_INFO,
                             "CallerAlias[%s]::check4andApplyAlias call %s set caller alias\n"
                             "  Original-From: %s\n"
                             "  Aliased-From:  %s",
                             mInstanceName.data(), callId.data(),
                             originalFromField.data(),
                             newFromFieldValue.data()
                             );

               // rewrite the caller identity with the aliased value
               request.setRawFromField(newFromFieldValue.data());

               // Factor the tag values out of the field values stored in the RouteState
               //  We do this because otherwise we'll end up encoding and sending two copies
               //  of the tag; since some phones send really long tag values (no one knows why),
               //  this can cause such large Record-Route headers that they cause interop problems.
               if ( ! originalFromTag.isNull() )
               {
                  // find the offset of the tag value in the callers from field
                  ssize_t callerFromTagOffset;
                  callerFromTagOffset = originalFromField.index(originalFromTag);
                  callerFromTagOffsetStr.appendNumber(callerFromTagOffset);
                  // strip the tag value from the original From value to be stored in the RouteState
                  originalFromField.replace(callerFromTagOffset, originalFromTag.length(), "");
                  
                  // find the offset of the tag value in the aliased from field
                  ssize_t aliasFromTagOffset;
                  aliasFromTagOffset = newFromFieldValue.index(originalFromTag);
                  aliasFromTagOffsetStr.appendNumber(aliasFromTagOffset);
                  // strip the tag value from the aliased From value to be stored in the RouteState
                  newFromFieldValue.replace(aliasFromTagOffset, originalFromTag.length(), "");
               }

               // save the original and new values so that we can fix them later
               routeState.setParameter(mInstanceName.data(),
                                       CALLER_FROM_PARAM,originalFromField);
               routeState.setParameter(mInstanceName.data(),
                                       CALLER_TAG_OFFSET_PARAM,callerFromTagOffsetStr);
               routeState.setParameter(mInstanceName.data(),
                                       ALIAS_FROM_PARAM,newFromFieldValue);
               routeState.setParameter(mInstanceName.data(),
                                       ALIAS_TAG_OFFSET_PARAM,aliasFromTagOffsetStr);
            }
            else
            {
               Os::Logger::instance().log( FAC_SIP, PRI_DEBUG,
                             "CallerAlias[%s]::check4andApplyAlias call %s found no alias",
                             mInstanceName.data(), callId.data()
                             );
            }
         }
         else
         {
            Os::Logger::instance().log(FAC_SIP, PRI_DEBUG,
                          "CallerAlias[%s]::authorizeAndModify "
                          "not mutable - no rewrite",
                          mInstanceName.data()                          
                          );
         }
      }
      else // the callerFrom and aliasFrom parameters were found
      {
         /*
          * This request has had its From rewritten, so fix either the From
          * or the To depending on which direction this request is going.
          */
         if (!request.isResponse()) // can't modify responses, so don't bother
         {
            size_t tagOffset;
            
            if (routeState.directionIsCallerToCalled(mInstanceName.data()))
            {
               // replace the from tag value in the stored aliased header
               tagOffset = strtol(aliasFromTagOffsetStr.data(), NULL, 10);
               aliasFrom.insert(tagOffset, originalFromTag);

               // put the aliased header into the message
               request.setRawFromField(aliasFrom);
               Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, "CallerAlias[%s]::authorizeAndModify "
                             "call %s reset From",
                             mInstanceName.data(), callId.data()
                             );
            }
            else // direction is Called to Caller
            {
               // replace the from tag value in the stored original header
               tagOffset = strtol(callerFromTagOffsetStr.data(), NULL, 10);
               callerFrom.insert(tagOffset, originalFromTag);

               request.setRawToField(callerFrom.data());
               Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, "CallerAlias[%s]::authorizeAndModify "
                             "call %s reset To",
                             mInstanceName.data(), callId.data()
                             );
            }
         }
      }
   }
   return AuthPlugin::CONTINUE;
}