UtlBoolean SipDialog::isSameLocalCseq(const SipMessage& message) const { int cseq; message.getCSeqField(&cseq, NULL); return(cseq == mLastLocalCseq); }
void WaitingFor200OkforInvite::SuccessfulResponse( DialogTracker& impl, SipMessage& response, const char* address, int port ) const { int seqNum; UtlString seqMethod; if( response.getCSeqField( &seqNum, &seqMethod ) ) { if( seqMethod.compareTo( SIP_INVITE_METHOD ) == 0 ) { // normally we would not be expecting this 200 OK response to carry // an SDP however we have encountered in the field some endpoints // that repeat the SDP answer they already sent in a previous // reliable provisional response (see XECS-2079 for the details). // Given that, if an SDP answer is found, we will reprocess it // to make sure it gets the same transformations that the initial // one got as per XECS-2089. if( response.hasSdpBody() ) { impl.ProcessMediaAnswer( response, INITIAL_OFFER_ANSWER ); } ChangeState( impl, impl.pWaitingForAckForInvite ); } else { OsSysLog::add(FAC_NAT,PRI_DEBUG,"'%s:%s' - Received unexpected successful response for %s request", impl.name(), impl.GetCurrentState()->name(), seqMethod.data() ); } } }
UtlBoolean SipDialog::isNextRemoteCseq(const SipMessage& message) const { int cseq; message.getCSeqField(&cseq, NULL); return(cseq > mLastRemoteCseq); }
void Negotiating::FailureResponse( DialogTracker& impl, SipMessage& response, const char* address, int port ) const { int seqNum; UtlString seqMethod; if( response.getCSeqField( &seqNum, &seqMethod ) ) { if( seqMethod.compareTo( SIP_INVITE_METHOD ) == 0 ) { // session negotiation failed. Deallocate all the tentative // media relays tentatively allocated to handle the media // sessions that just failed. impl.deallocateAndClearAllMediaRelaySessions( true, true, false ); if( !impl.getDialogEstablishedFlag() ) { // this is a final failure response to a dialog-forming INVITE. That // event marks the end of the dialog hence, we do not need to continue // to track it. ChangeState( impl, impl.pMoribund ); } else { // the renegotiation failed but the dialog is still active. Go back to state where // we wait for an incoming INVITE. ChangeState( impl, impl.pWaitingForInvite ); } } else { OsSysLog::add(FAC_NAT,PRI_DEBUG,"'%s:%s' - Received unexpected successful response for %s request", impl.name(), impl.GetCurrentState()->name(), seqMethod.data() ); } } }
void ProcessingPrack::SuccessfulResponse( DialogTracker& impl, SipMessage& response, const char* address, int port ) const { int seqNum; UtlString seqMethod; if( response.getCSeqField( &seqNum, &seqMethod ) ) { if( seqMethod.compareTo( SIP_INVITE_METHOD ) == 0 ) { // normally we would not be expecting this 200 OK response to carry // an SDP in this state however we have encountered in the field some endpoints // that repeat the SDP answer they already sent in a previous // Successful response (see XECS-2079 for the details). // Given that, if an SDP answer is found, we will reprocess it // to make sure it gets the same transformations that the initial // one got as per XECS-2089. if( response.hasSdpBody() ) { impl.ProcessMediaAnswer( response, INITIAL_OFFER_ANSWER ); } } else { OsSysLog::add(FAC_NAT,PRI_DEBUG,"'%s:%s' - Received unexpected Successful Response for %s request", impl.name(), impl.GetCurrentState()->name(), seqMethod.data() ); } // We have received a response - although that does // not cause a state machine state change, we need to reset the tick counter // to show that there is still activity in this dialog. impl.resetTimerTickCounter(); } }
void SipXProxyCseObserver::handleOutputMessage( SipMessage& message, const char* address, int port ) { UtlString method; UtlString viaValue; UtlString branchId; UtlString callId; int rspStatus = 0; if (message.isResponse()) { int seq; if (message.getCSeqField(&seq, &method)) { if (0==method.compareTo(SIP_INVITE_METHOD, UtlString::ignoreCase)) { rspStatus = message.getResponseStatusCode(); if (rspStatus >= SIP_2XX_CLASS_CODE) { SipMessageEvent* finalTransResponse = new SipMessageEvent(new SipMessage(message), SipMessageEvent::APPLICATION); this->postMessage(*finalTransResponse); delete finalTransResponse; } } } } }
void SipImpliedSubscriptions::buildSubscribeRequest( const SipMessage& registerMessage ,int duration ,SipMessage& subscribeRequest ,UtlString& callId ,UtlString& fromTag ,UtlString& fromUri ) { UtlString registrationValue; UtlString tagNameValuePair; UtlString contactUri; int sequenceNumber = 0; // Get the From URL, and change the tag Url fromUrl; registerMessage.getFromUrl( fromUrl ); fromUrl.removeFieldParameter("tag"); // discard from tag from REGISTER registerMessage.getFromUri( &fromUri ); (void) registerMessage.getContactUri(0, &contactUri); (void) registerMessage.getCSeqField(&sequenceNumber, ®istrationValue); Url toUrl; registerMessage.getToUrl( toUrl ); toUrl.removeFieldParameter("tag"); UtlString toUri; registerMessage.getToUri( &toUri ); registerMessage.getCallIdField( &callId ); callId.prepend("implied-mwi-"); // Build a from tag for the SUBSCRIBE // - hash the call id so that it will be the same on each refresh UtlString callIdHash; NetMd5Codec::encode( callId.data(), callIdHash ); fromUrl.setFieldParameter("tag", callIdHash.data() ); fromTag = callIdHash; // for constructing the nonce subscribeRequest.setVoicemailData( fromUrl.toString() // From: ,toUrl.toString() // To: ,toUri.data() // request URI ,contactUri.data() // taken from registration ,callId.data() ,++sequenceNumber ,duration ); /* * Rewrite the event field to add our extension parameter to * ensure that the registration and subscription are synchronized. */ const char* standardEventHeader = subscribeRequest.getHeaderValue(0, SIP_EVENT_FIELD); UtlString extendedEventHeader(standardEventHeader); extendedEventHeader.append(";" SIPX_IMPLIED_SUB "="); char durationString[12]; sprintf(durationString, "%d", duration); extendedEventHeader.append(durationString); subscribeRequest.setHeaderValue(SIP_EVENT_FIELD, extendedEventHeader.data(), 0); }
UtlBoolean SipRefreshManager::getAcceptedExpiration(RefreshDialogState* state, const SipMessage& sipResponse, int& expirationPeriod) { UtlString method; UtlBoolean foundExpiration = FALSE; int cseq; sipResponse.getCSeqField(&cseq, &method); if(method.compareTo(SIP_REGISTER_METHOD) == 0) { // Get the presumably first contact in the REGISTER request // so that we can find the same contact in the response and // find out what the expiration is UtlString requestContact; Url requestContactUri; if(state && state->mpLastRequest && state->mpLastRequest->getContactEntry(0, &requestContact)) { requestContactUri = requestContact; } // Register could have it in the Contact header UtlString responseContactValue; int contactIndex = 0; while(sipResponse.getContactEntry(contactIndex , &responseContactValue)) { // Get the expires parameter for the contact if it exists Url contactUri(responseContactValue); if(requestContactUri.isUserHostPortEqual(contactUri)) { UtlString contactExpiresParameter; if(contactUri.getFieldParameter(SIP_EXPIRES_FIELD, contactExpiresParameter) && !contactExpiresParameter.isNull()) { foundExpiration = TRUE; // Convert to int expirationPeriod = atoi(contactExpiresParameter); } } contactIndex++; } } if(!foundExpiration) { // Not sure if we care if this is a request or response foundExpiration = sipResponse.getExpiresField(&expirationPeriod); } return(foundExpiration); }
void WaitingForMediaAnswer::SuccessfulResponse( DialogTracker& impl, SipMessage& response, const char* address, int port ) const { int seqNum; UtlString seqMethod; if( response.getCSeqField( &seqNum, &seqMethod ) ) { if( seqMethod.compareTo( SIP_INVITE_METHOD ) == 0 ) { impl.ProcessMediaAnswer( response, INITIAL_OFFER_ANSWER ); ChangeState( impl, impl.pWaitingForAckForInvite ); } } }
bool CallTracker::getSessionHandleFromVia( const SipMessage& message, int viaIndex, UtlString& sessionHandle ) const { bool bSessionHandleFound = false; UtlString viaValue; sessionHandle.remove( 0 ); if( message.getViaFieldSubField( &viaValue, viaIndex ) ) { // in most cases the Via will contain a proprietary tag called 'id' which carries // the handle of the SessionContext - this is the fastest way retrieve the SessionContext // handle so try it first. if( message.getViaTag( viaValue, VIA_SESSION_HANDLE_TAG, sessionHandle ) ) { bSessionHandleFound = true; } else { // The Via didn't contain the information we required to find the SessionContext object. // This can happen in cases where the sipXproxy waits for the 'best' failure response to INVITEs. // In such cases, for reasons that remain unknown, the Via headers contained in the // 'best' response selected by sipXproxy will be stripped of our special 'id' tag // which is normally used to find the correct SessionContext. // If we are dealing with a non-200 final response for INVITES then try to find the // SessionContext corresponding to the Via's topmost branchId using the information // contained in mBranchIdToSessionHandleMap. int seqNum; UtlString seqMethod; message.getCSeqField( &seqNum, &seqMethod ); if( message.isResponse() && message.getResponseStatusCode() >= SIP_3XX_CLASS_CODE && seqMethod.compareTo( SIP_INVITE_METHOD ) == 0 ) { UtlString branchId; if( message.getViaTag( viaValue, "branch", branchId ) ) { UtlString* pSessionHandle; pSessionHandle = dynamic_cast<UtlString*>(mBranchIdToSessionHandleMap.findValue( &branchId ) ); if( pSessionHandle ) { sessionHandle = *pSessionHandle; bSessionHandleFound = true; } } } } } return bSessionHandleFound; }
void WaitingFor200OkForPrack::FailureResponse( DialogTracker& impl, SipMessage& response, const char* address, int port ) const { int seqNum; UtlString seqMethod; if( response.getCSeqField( &seqNum, &seqMethod ) ) { if( seqMethod.compareTo( SIP_PRACK_METHOD ) == 0 ) { ChangeState( impl, impl.pWaitingForPrack ); } else { OsSysLog::add(FAC_NAT,PRI_DEBUG,"'%s:%s' - Received unexpected successful response for %s request", impl.name(), impl.GetCurrentState()->name(), seqMethod.data() ); } } }
enum SipDialogMgr::transactionSequence SipDialogMgr::isNewRemoteTransaction(const SipMessage& message) { enum transactionSequence ordering; UtlString handle; message.getDialogHandle(handle); UtlString callId; UtlString fromTag; UtlString toTag; SipDialog::parseHandle(handle, callId, fromTag, toTag); lock(); // Looking for any dialog that matches this handle SipDialog* dialog = findDialog(handle, TRUE, // if established, match early dialog TRUE); // if early, match established dialog if (dialog && dialog->isTransactionRemotelyInitiated(callId, fromTag, toTag)) { int messageCSeq; message.getCSeqField(&messageCSeq, NULL); int lastRemoteCSeq = dialog->getLastRemoteCseq(); ordering = messageCSeq < lastRemoteCSeq ? OUT_OF_ORDER : /** If this message was an exact duplicate of a previous message * (with the same CSeq and branch parameter), it would have been * absorbed earlier in processing. So we know the branch parameter * is different without having to remember the previous value. */ messageCSeq == lastRemoteCSeq ? LOOPED : IN_ORDER; } else { ordering = NO_DIALOG; } unlock(); return ordering; }
void WaitingFor200OkForPrack::SuccessfulResponse( DialogTracker& impl, SipMessage& response, const char* address, int port ) const { int seqNum; UtlString seqMethod; if( response.getCSeqField( &seqNum, &seqMethod ) ) { if( seqMethod.compareTo( SIP_PRACK_METHOD ) == 0 ) { ChangeState( impl, impl.pProcessingPrackWaitingForAckforInvite ); } else { // Not interesting for us but our parent class provides some handling for // other successful responses. ProcessingPrack::SuccessfulResponse( impl, response, address, port ); } } }
void WaitingFor200OkWithAnswerForPrack::SuccessfulResponse( DialogTracker& impl, SipMessage& response, const char* address, int port ) const { int seqNum; UtlString seqMethod; if( response.getCSeqField( &seqNum, &seqMethod ) ) { if( seqMethod.compareTo( SIP_PRACK_METHOD ) == 0 ) { impl.ProcessMediaAnswer( response, NON_INITIAL_OFFER_ANSWER ); impl.modifyNonIntialOfferAnswerExchangeDoneFlag( true ); ChangeState( impl, impl.pWaitingFor200OkforInvite ); } else { OsSysLog::add(FAC_NAT,PRI_DEBUG,"'%s:%s' - Received unexpected successful response for %s request", impl.name(), impl.GetCurrentState()->name(), seqMethod.data() ); } } }
void WaitingFor200OkForSlowStartPrack::FailureResponse( DialogTracker& impl, SipMessage& response, const char* address, int port ) const { int seqNum; UtlString seqMethod; if( response.getCSeqField( &seqNum, &seqMethod ) ) { if( seqMethod.compareTo( SIP_PRACK_METHOD ) == 0 ) { // PRACK was rejected. Keep tentative INVITE media relays in case an // acceptable PRACK gets generated by the UAC. ChangeState( impl, impl.pWaitingForPrackWithMediaAnswer ); } else { OsSysLog::add(FAC_NAT,PRI_DEBUG,"'%s:%s' - Received unexpected failure response for %s request", impl.name(), impl.GetCurrentState()->name(), seqMethod.data() ); } } }
void WaitingFor200OkWithAnswerForPrack::SuccessfulResponse( DialogTracker& impl, SipMessage& response, const char* address, int port ) const { int seqNum; UtlString seqMethod; if( response.getCSeqField( &seqNum, &seqMethod ) ) { if( seqMethod.compareTo( SIP_PRACK_METHOD ) == 0 ) { impl.ProcessMediaAnswer( response, NON_INITIAL_OFFER_ANSWER ); impl.modifyNonIntialOfferAnswerExchangeDoneFlag( true ); ChangeState( impl, impl.pProcessingPrackWaitingForAckforInvite ); } else { // Not interesting for us but our parent class provides some handling for // other successful responses. ProcessingPrack::SuccessfulResponse( impl, response, address, port ); } } }
void WaitingFor200OkWithAnswerForPrack::FailureResponse( DialogTracker& impl, SipMessage& response, const char* address, int port ) const { int seqNum; UtlString seqMethod; if( response.getCSeqField( &seqNum, &seqMethod ) ) { if( seqMethod.compareTo( SIP_PRACK_METHOD ) == 0 ) { // PRACK offer/answer failed. Deallocate any tentative media relays allocation // in preparation for that failed PRACK offer/answer negotiation. impl.deallocateAndClearAllMediaRelaySessions( false, true, false ); ChangeState( impl, impl.pWaitingForPrack ); } else { OsSysLog::add(FAC_NAT,PRI_DEBUG,"'%s:%s' - Received unexpected failure response for %s request", impl.name(), impl.GetCurrentState()->name(), seqMethod.data() ); } } }
// Update the IMDB with the NOTIFY CSeq now in notifyRequest and the // specified 'version' for the given eventTypeKey. void SipPersistentSubscriptionMgr::updateVersion(SipMessage& notifyRequest, int version, const UtlString& eventTypeKey) { // Call the superclass's updateVersion. SipSubscriptionMgr::updateVersion(notifyRequest, version, eventTypeKey); // Extract from the NOTIFY the information we need to find the right // IMDB row. int cseq; UtlString method; notifyRequest.getCSeqField(&cseq, &method); UtlString to; UtlString from; UtlString callId; UtlString eventType, eventId; int now; // Note that the "to" and "from" fields of the subscription table // are as those URIs appear in the SUBSCRIBE message, which is // reversed in the NOTIFY message. notifyRequest.getToField(&from); notifyRequest.getFromField(&to); notifyRequest.getCallIdField(&callId); notifyRequest.getEventField(&eventType, &eventId); now = (int) OsDateTime::getSecsSinceEpoch(); OsSysLog::add(FAC_SIP, PRI_DEBUG, "SipPersistentSubscriptionMgr::updateVersion " "callId = '%s', to = '%s', from = '%s', eventType = '%s', eventTypeKey = '%s', eventId = '%s', cseq = %d, version = %d", callId.data(), to.data(), from.data(), eventType.data(), eventTypeKey.data(), eventId.data(), cseq, version); mSubscriptionDBInstance->updateNotifyUnexpiredSubscription( mComponent, to, from, callId, eventTypeKey, eventId, now, cseq, version); // Start the save timer. mPersistenceTimer.oneshotAfter(sPersistInterval); }
/// 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]@@<"); regInfo.append(contactInfo); regInfo.append(">\" 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; }
UtlBoolean TestRegistrar::handleRegisterRequest(SipMessage message) { UtlBoolean messageProcessed = false; SipMessage finalResponse; UtlString responseToAddress; UtlString protocol; int responseToPort; int seqNum; UtlString method; UtlString contactField; int expires; static int retrySeqNum = 0; UtlString callId; message.getCallIdField(&callId); message.getContactField(0, contactField); message.getExpiresField(&expires); message.getCSeqField(&seqNum, &method); message.getFromAddress(&responseToAddress, &responseToPort, &protocol); finalResponse.setContactField(contactField); finalResponse.setExpiresField(expires); finalResponse.setFromField("sip:127.0.0.1:5070", 5070); finalResponse.setSendAddress(contactField, responseToPort); finalResponse.setToField(contactField, responseToPort, protocol); finalResponse.setUserAgentField("TestRegistrar"); // finalResponse.setRegisterData(responseToAddress, contactField, "127.0.0.1", contactField, callId, seqNum); if (contactField.contains("anon")) // anonymous user doesnt need authentication, just say ok { finalResponse.setResponseData(&message, 200, "OK"); } else if (contactField.contains("mike")) // mike requires registration, say 401 { UtlString realm; UtlString requestUser; UtlString requestRealm; UtlString requestNonce; UtlString uriParam; // TBD - 25-jan-2010 work might be needed if these tests are re-enabled message.getDigestAuthorizationData( &requestUser, &requestRealm, &requestNonce, NULL, NULL, &uriParam, NULL, // TBD cnonce NULL, // TBD nonceCount NULL, // TBD qop HttpMessage::SERVER, 0); if (seqNum == retrySeqNum) // if this is a retry response { // if they've sent any auth field, just accept it. // TODO: figure out if a username and password has been encrypted and sent. finalResponse.setCSeqField(++seqNum, SIP_REGISTER_METHOD); finalResponse.setResponseData(&message, 200, "OK"); } else { message.getCSeqField(&seqNum, &method); finalResponse.setCSeqField(++seqNum, method); retrySeqNum = seqNum; #ifdef _WIN32 finalResponse.setAuthenticateData("md5", "TestRegistrar", NULL, NULL, NULL, HttpMessage::HttpEndpointEnum::SERVER ); #else finalResponse.setAuthenticateData("md5", "TestRegistrar", NULL, NULL, NULL, HttpMessage::SERVER ); #endif finalResponse.setResponseData(&message, 401, "Not authorized"); } } else if (contactField.contains("xyzzy")) { // this is our special username that will cause a response // to be echoed back with the 3 digit value after xyzzy. // for instance, the contact xyzzy401 will cause a 401 response int pos = contactField.first("xyzzy"); char szCode[4]; szCode[0] = contactField[pos + 5]; szCode[1] = contactField[pos + 6]; szCode[2] = contactField[pos + 7]; szCode[3] = '\0'; finalResponse.setResponseData(&message, atoi(szCode), "OK"); } mpUserAgent->send(finalResponse); return messageProcessed; }
UtlBoolean SipXProxyCseObserver::handleMessage(OsMsg& eventMessage) { int msgType = eventMessage.getMsgType(); switch (msgType) { case OsMsg::OS_EVENT: switch (eventMessage.getMsgSubType()) { case OsEventMsg::NOTIFY: if (mpWriter) { mpWriter->flush(); } break; } break ; case OsMsg::PHONE_APP: { SipMessage* sipMsg; if(SipMessageEvent::TRANSPORT_ERROR == ((SipMessageEvent&)eventMessage).getMessageStatus()) { Os::Logger::instance().log(FAC_SIP, PRI_ERR, "SipXProxyCseObserver::handleMessage transport error"); } else if((sipMsg = (SipMessage*)((SipMessageEvent&)eventMessage).getMessage())) { UtlString method; int rspStatus = 0; UtlString rspText; UtlString contact; UtlString toTag; enum { UnInteresting, aCallRequest, aCallSetup, aCallFailure, aCallEnd, aCallTransfer } thisMsgIs = UnInteresting; Url toUrl; sipMsg->getToUrl(toUrl); // explicitly, an INVITE Request toUrl.getFieldParameter("tag", toTag); if (!sipMsg->isResponse()) { // sipMsg is a Request sipMsg->getRequestMethod(&method); if (0==method.compareTo(SIP_INVITE_METHOD, UtlString::ignoreCase)) { if (toTag.isNull()) { sipMsg->getContactEntry(0, &contact); thisMsgIs = aCallRequest; } } else if (0==method.compareTo(SIP_REFER_METHOD, UtlString::ignoreCase)) { thisMsgIs = aCallTransfer; sipMsg->getContactEntry(0, &contact); } else if (0==method.compareTo(SIP_BYE_METHOD, UtlString::ignoreCase)) { thisMsgIs = aCallEnd; // no additional information needed } else { // other request methods are not interesting } } else // this is a response { int seq; if (sipMsg->getCSeqField(&seq, &method)) // get the method out of cseq field { if (0==method.compareTo(SIP_INVITE_METHOD, UtlString::ignoreCase)) { // Responses to INVITES are handled differently based on whether // or not the INVITE is dialog-forming. If dialog-forming, // any final response above 400 is considered a failure for CDR // purposes. If not dialog-forming, then any final response above 400 // except 401 Unauthorized, 407 Proxy Authentication Required and // 408 Request Timeout will terminate. If we're in a dialog then // only 408 (Request Timeout) and 481 (Call/Transaction does not exist) // will terminate the dialog. rspStatus = sipMsg->getResponseStatusCode(); if (rspStatus >= SIP_4XX_CLASS_CODE) // any failure { // a failure code - this is a potential CallFailure - Call Resolver will determine. thisMsgIs = aCallFailure; sipMsg->getResponseStatusText(&rspText); } else if ( ( rspStatus >= SIP_2XX_CLASS_CODE ) && ( rspStatus < SIP_3XX_CLASS_CODE ) ) { thisMsgIs = aCallSetup; sipMsg->getContactEntry(0, &contact); } } else { // responses to non-INVITES are not interesting } } else { Os::Logger::instance().log(FAC_SIP, PRI_ERR, "SipXProxyCseObserver - no Cseq in response"); } } # ifdef LOG_DEBUG Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipXProxyCseObserver message is %s", ( thisMsgIs == UnInteresting ? "UnInteresting" : thisMsgIs == aCallEnd ? "a Call End" : thisMsgIs == aCallFailure ? "a Call Failure" : thisMsgIs == aCallRequest ? "a call Request" : thisMsgIs == aCallSetup ? "a Call Setup" : thisMsgIs == aCallTransfer ? "a Call Transfer" : "BROKEN" )); # endif if (thisMsgIs != UnInteresting) { // collect the sequence data mSequenceNumber++; OsTime timeNow; OsDateTime::getCurTime(timeNow); // collect the dialog information UtlString callId; sipMsg->getCallIdField(&callId); Url toUrl; sipMsg->getToUrl(toUrl); UtlString toTag; toUrl.getFieldParameter("tag", toTag); Url fromUrl; sipMsg->getFromUrl(fromUrl); UtlString fromTag; fromUrl.getFieldParameter("tag", fromTag); // collect the To and From UtlString toField; sipMsg->getToField(&toField); UtlString fromField; sipMsg->getFromField(&fromField); // collect the branch Id (i.e. transaction id) and via count. UtlString viaValue; int viaCount; UtlString branchId; viaCount = sipMsg->getCountHeaderFields(SIP_VIA_FIELD); viaCount = viaCount + sipMsg->getCountHeaderFields(SIP_SHORT_VIA_FIELD); if ( sipMsg->getViaFieldSubField( &viaValue, 0 ) ) { sipMsg->getViaTag( viaValue, "branch", branchId ); } UtlString referTo; UtlString referredBy; UtlString requestUri; UtlString references; UtlString replaces_callId; UtlString replaces_toTag; UtlString replaces_fromTag; UtlString matchingIdentityHeader; SipXauthIdentity sipxIdentity(*sipMsg, matchingIdentityHeader, true,SipXauthIdentity::allowUnbound); sipMsg->getReferToField(referTo); sipMsg->getReferredByField(referredBy); sipMsg->getRequestUri(&requestUri); sipMsg->getReferencesField(&references); if (sipMsg->getReplacesData(replaces_callId, replaces_toTag, replaces_fromTag)) { if (references.length() != 0) { references.append(","); } references.append(replaces_callId); references.append(";rel=xfer"); } UtlString responseMethod; UtlString calleeRoute; int cseqNumber; sipMsg->getCSeqField(&cseqNumber, &responseMethod); BranchTimePair* callIdBranchIdTime; // generate the call state event record if (mpBuilder) { UtlString identity; UtlString recordRoute; bool routeFound = false; bool paiPresent = false; switch (thisMsgIs) { case aCallRequest: if (sipxIdentity.getIdentity(identity)) { paiPresent = true; } if ( branchId && branchId.data() ) { mCallTransMutex.acquire(); unsigned long currentTime = OsDateTime::getSecsSinceEpoch(); UtlString* keyCallId = new UtlString(callId); BranchTimePair* valBranchTimePair = new BranchTimePair(branchId.data(), ¤tTime, &paiPresent); if (NULL == mCallTransMap.insertKeyAndValue(keyCallId, valBranchTimePair) ) { // Unable to add callId to map so it must already be present. delete keyCallId; delete valBranchTimePair; // Check if the paiPresent value is set to true or not. // If not set and we now have a PAI for this call, set it and generate another call request state event // with this info. Otherwise skip over. if ( paiPresent ) { callIdBranchIdTime = (BranchTimePair*) mCallTransMap.findValue(&callId); if ( callIdBranchIdTime && (*callIdBranchIdTime->getPaiPresent() == false) ) { // need to generate another call request event in order to state originator is internal. callIdBranchIdTime->setPaiPresent(&paiPresent); } else { mCallTransMutex.release(); return(TRUE); } } else { mCallTransMutex.release(); return(TRUE); } } mCallTransMutex.release(); } mpBuilder->callRequestEvent(mSequenceNumber, timeNow, contact, references, branchId, viaCount, paiPresent); break; case aCallSetup: // Clear out from the map only if rspStatus is higher than 200 as its possible to receive multiple 200 messages. // If the response is 200, the call in the map will be cleared out when the call ends. mCallTransMutex.acquire(); callIdBranchIdTime = (BranchTimePair*) mCallTransMap.findValue(&callId); if ( callIdBranchIdTime && (0 == branchId.compareTo(callIdBranchIdTime)) ) { if ( rspStatus > SIP_2XX_CLASS_CODE ) { mCallTransMap.destroy(&callId); } mCallTransMutex.release(); } else { // CallId/BranchId are either not found or doesn't match. Not a final response. mCallTransMutex.release(); return(TRUE); } for (int rrNum = 0; (!routeFound && sipMsg->getRecordRouteUri(rrNum, &recordRoute)); rrNum++ ) { Url recordRouteUrl(recordRoute); if (mpSipUserAgent->isMyHostAlias(recordRouteUrl)) { // This is a record route for our proxy, extract Call tags if they exist. recordRouteUrl.getUrlParameter(SIP_SIPX_CALL_DEST_FIELD, calleeRoute, 0); routeFound = true; } } mpBuilder->callSetupEvent(mSequenceNumber, timeNow, contact, calleeRoute, branchId, viaCount); break; case aCallFailure: // Failure case means that the response code is > 400. If the call is found // in the map, then this is a final response. Delete from the map and build an event. mCallTransMutex.acquire(); callIdBranchIdTime = (BranchTimePair*) mCallTransMap.findValue(&callId); if ( callIdBranchIdTime && (0 == branchId.compareTo(callIdBranchIdTime)) ) { mCallTransMap.destroy(&callId); mCallTransMutex.release(); if ( rspStatus != SIP_PROXY_AUTH_REQ_CODE ) { mpBuilder->callFailureEvent(mSequenceNumber, timeNow, branchId, viaCount, rspStatus, rspText); } else { // response was an authentication required. Don't build a CSE for these as a new Invite will // occur. return(TRUE); } } else { // Call was not found in the map so this is not a final response. Ignore it. mCallTransMutex.release(); return(TRUE); } break; case aCallEnd: mCallTransMutex.acquire(); mCallTransMap.destroy(&callId); mCallTransMutex.release(); mpBuilder->callEndEvent(mSequenceNumber, timeNow); break; case aCallTransfer: mpBuilder->callTransferEvent(mSequenceNumber, timeNow, contact, referTo, referredBy, requestUri); break; default: // shouldn't be possible to get here Os::Logger::instance().log(FAC_SIP, PRI_ERR, "SipXProxyCseObserver invalid thisMsgIs"); break; } mpBuilder->addCallData(cseqNumber, callId, fromTag, toTag, fromField, toField); UtlString via; for (int i=0; sipMsg->getViaField(&via, i); i++) { mpBuilder->addEventVia(via); } mpBuilder->completeCallEvent(); // get the completed record UtlString event; mpBuilder->finishElement(event); if (mpWriter) { mpWriter->writeLog(event.data()); } } else { Os::Logger::instance().log(FAC_SIP, PRI_ERR, "SipXProxyCseObserver - no CallStateEventBuilder!"); } } } else { Os::Logger::instance().log(FAC_SIP, PRI_ERR, "SipXProxyCseObserver getMessage returned NULL"); } } break; default: { Os::Logger::instance().log(FAC_SIP, PRI_ERR, "SipXProxyCseObserver invalid message type %d", msgType ); } } // end switch (msgType) return(TRUE); }
UtlBoolean SipXProxyCseObserver::handleMessage(OsMsg& eventMessage) { int msgType = eventMessage.getMsgType(); switch (msgType) { case OsMsg::OS_EVENT: switch (eventMessage.getMsgSubType()) { case OsEventMsg::NOTIFY: if (mpWriter) { mpWriter->flush(); } break; } break ; case OsMsg::PHONE_APP: { SipMessage* sipMsg; if(SipMessageEvent::TRANSPORT_ERROR == ((SipMessageEvent&)eventMessage).getMessageStatus()) { OsSysLog::add(FAC_SIP, PRI_ERR, "SipXProxyCseObserver::handleMessage transport error"); } else if((sipMsg = (SipMessage*)((SipMessageEvent&)eventMessage).getMessage())) { UtlString method; int rspStatus = 0; UtlString rspText; UtlString contact; UtlString toTag; enum { UnInteresting, aCallRequest, aCallSetup, aCallFailure, aCallEnd, aCallTransfer } thisMsgIs = UnInteresting; Url toUrl; sipMsg->getToUrl(toUrl); // explicitly, an INVITE Request toUrl.getFieldParameter("tag", toTag); if (!sipMsg->isResponse()) { // sipMsg is a Request sipMsg->getRequestMethod(&method); if (0==method.compareTo(SIP_INVITE_METHOD, UtlString::ignoreCase)) { if (toTag.isNull()) { thisMsgIs = aCallRequest; } } else if (0==method.compareTo(SIP_REFER_METHOD, UtlString::ignoreCase)) { thisMsgIs = aCallTransfer; sipMsg->getContactEntry(0, &contact); } else if (0==method.compareTo(SIP_BYE_METHOD, UtlString::ignoreCase)) { thisMsgIs = aCallEnd; // no additional information needed } else { // other request methods are not interesting } } else // this is a response { int seq; if (sipMsg->getCSeqField(&seq, &method)) // get the method out of cseq field { if (0==method.compareTo(SIP_INVITE_METHOD, UtlString::ignoreCase)) { // Responses to INVITES are handled differently based on whether // or not the INVITE is dialog-forming. If dialog-forming, // any final response above 400 is considered a failure for CDR // purposes. If not dialog-forming, then any final response above 400 // except 401 Unauthorized, 407 Proxy Authentication Required and // 408 Request Timeout will terminate. If we're in a dialog then // only 408 (Request Timeout) and 481 (Call/Transaction does not exist) // will terminate the dialog. rspStatus = sipMsg->getResponseStatusCode(); if (rspStatus >= SIP_4XX_CLASS_CODE) // any failure { // a failure code - this is a potential CallFailure - Call Resolver will determine. thisMsgIs = aCallFailure; sipMsg->getResponseStatusText(&rspText); } else if ( ( rspStatus >= SIP_2XX_CLASS_CODE ) && ( rspStatus < SIP_3XX_CLASS_CODE ) ) { thisMsgIs = aCallSetup; sipMsg->getContactEntry(0, &contact); } } else { // responses to non-INVITES are not interesting } } else { OsSysLog::add(FAC_SIP, PRI_ERR, "SipXProxyCseObserver - no Cseq in response"); } } # ifdef LOG_DEBUG OsSysLog::add(FAC_SIP, PRI_DEBUG, "SipXProxyCseObserver message is %s", ( thisMsgIs == UnInteresting ? "UnInteresting" : thisMsgIs == aCallEnd ? "a Call End" : thisMsgIs == aCallFailure ? "a Call Failure" : thisMsgIs == aCallRequest ? "a call Request" : thisMsgIs == aCallSetup ? "a Call Setup" : thisMsgIs == aCallTransfer ? "a Call Transfer" : "BROKEN" )); # endif if (thisMsgIs != UnInteresting) { // collect the sequence data mSequenceNumber++; OsTime timeNow; OsDateTime::getCurTime(timeNow); // collect the dialog information UtlString callId; sipMsg->getCallIdField(&callId); Url toUrl; sipMsg->getToUrl(toUrl); UtlString toTag; toUrl.getFieldParameter("tag", toTag); Url fromUrl; sipMsg->getFromUrl(fromUrl); UtlString fromTag; fromUrl.getFieldParameter("tag", fromTag); // collect the To and From UtlString toField; sipMsg->getToField(&toField); UtlString fromField; sipMsg->getFromField(&fromField); UtlString referTo; UtlString referredBy; UtlString requestUri; sipMsg->getReferToField(referTo); sipMsg->getReferredByField(referredBy); sipMsg->getRequestUri(&requestUri); UtlString responseMethod; int cseqNumber; sipMsg->getCSeqField(&cseqNumber, &responseMethod); // generate the call state event record if (mpBuilder) { switch (thisMsgIs) { case aCallRequest: mpBuilder->callRequestEvent(mSequenceNumber, timeNow, contact); break; case aCallSetup: mpBuilder->callSetupEvent(mSequenceNumber, timeNow, contact); break; case aCallFailure: mpBuilder->callFailureEvent(mSequenceNumber, timeNow, rspStatus, rspText); break; case aCallEnd: mpBuilder->callEndEvent(mSequenceNumber, timeNow); break; case aCallTransfer: mpBuilder->callTransferEvent(mSequenceNumber, timeNow, contact, referTo, referredBy, requestUri); break; default: // shouldn't be possible to get here OsSysLog::add(FAC_SIP, PRI_ERR, "SipXProxyCseObserver invalid thisMsgIs"); break; } mpBuilder->addCallData(cseqNumber, callId, fromTag, toTag, fromField, toField); UtlString via; for (int i=0; sipMsg->getViaField(&via, i); i++) { mpBuilder->addEventVia(via); } mpBuilder->completeCallEvent(); // get the completed record UtlString event; mpBuilder->finishElement(event); if (mpWriter) { mpWriter->writeLog(event.data()); } } else { OsSysLog::add(FAC_SIP, PRI_ERR, "SipXProxyCseObserver - no CallStateEventBuilder!"); } } } else { OsSysLog::add(FAC_SIP, PRI_ERR, "SipXProxyCseObserver getMessage returned NULL"); } } break; default: { OsSysLog::add(FAC_SIP, PRI_ERR, "SipXProxyCseObserver invalid message type %d", msgType ); } } // end switch (msgType) return(TRUE); }
void SipDialog::updateDialogData(const SipMessage& message) { UtlString messageCallId; message.getCallIdField(&messageCallId); Url messageFromUrl; message.getFromUrl(messageFromUrl); UtlString messageFromTag; messageFromUrl.getFieldParameter("tag", messageFromTag); Url messageToUrl; message.getToUrl(messageToUrl); UtlString messageToTag; messageToUrl.getFieldParameter("tag", messageToTag); int cSeq; UtlString method; message.getCSeqField(&cSeq, &method); int responseCode = message.getResponseStatusCode(); // Figure out if the request is from the local or // the remote side if(isTransactionLocallyInitiated(messageCallId, messageFromTag, messageToTag)) { // This message is part of a transaction initiated by // the local side of the dialog if(cSeq > mLastLocalCseq) { mLastLocalCseq = cSeq; } if(cSeq >= mLastLocalCseq) { // Always update the contact if it is set UtlString messageContact; // Get the Contact value, but as an addr-spec. if(message.getContactUri(0, &messageContact) && !messageContact.isNull()) { if(message.isResponse()) { mRemoteContact.fromString(messageContact, TRUE); } else { mLocalContact.fromString(messageContact, TRUE); } } } // Cannot assume that we only establish a dialog with the // initial cseq. For example if there is an authentication // challenge, the dialog will not be established until the // second transaction. if(cSeq == mLastLocalCseq) { // A successful response to an INVITE or SUBSCRIBE // make this early dialog a set up dialog if(mLocalInitiatedDialog && message.isResponse() && responseCode >= SIP_2XX_CLASS_CODE && // successful dialog setup responseCode < SIP_3XX_CLASS_CODE && mRemoteTag.isNull() && // tag not set mRouteSet.isNull()) // have not yet set the route set { // Change this early dialog to a set up dialog. // The tag gets set in the 2xx response // so we need to update the URL message.getToUrl(mRemoteField); mRemoteField.getFieldParameter("tag", mRemoteTag); // Need to get the route set as well // Make sure the Request Method is allowed to set Record-Routes if(message.isRecordRouteAccepted()) { message.buildRouteField(&mRouteSet); } } } } else if(isTransactionRemotelyInitiated(messageCallId, messageFromTag, messageToTag)) { int prevRemoteCseq = mLastRemoteCseq; // This message is part of a transaction initiated by // the callee/destination of the session if(cSeq > mLastRemoteCseq) { mLastRemoteCseq = cSeq; } if(cSeq >= mLastRemoteCseq) { // Always update the contact if it is set UtlString messageContact; // Get the Contact value, but as an addr-spec. if(message.getContactUri(0, &messageContact) && !messageContact.isNull()) { if(message.isResponse()) { mLocalContact.fromString(messageContact, TRUE); } else { mRemoteContact.fromString(messageContact, TRUE); } } } // First transaction from the otherside if(cSeq == mLastRemoteCseq && prevRemoteCseq == -1) { // A response (e.g. NOTIFY) can come before we get the // successful response to the initial transaction if(!mLocalInitiatedDialog && !message.isResponse() && mRemoteTag.isNull()) // tag not set { // Change this early dialog to a set up dialog. // The tag gets set in the 2xx response // so we need to update the URL message.getFromUrl(mRemoteField); mRemoteField.getFieldParameter("tag", mRemoteTag); } } // First successful response from the local side if(cSeq == mLastRemoteCseq) { if(!mLocalInitiatedDialog && message.isResponse() && responseCode >= SIP_2XX_CLASS_CODE && // successful dialog setup responseCode < SIP_3XX_CLASS_CODE && mLocalTag.isNull()) { // Update the local tag message.getToUrl(mLocalField); mLocalField.getFieldParameter("tag", mLocalTag); } } } }
void AppearanceTest() { instantiateAllTestFixtures( "appearance-groups1.xml", "subscription1", "credential1", "177.0.0.1:54140"); UtlString sharedUri = "sip:[email protected]:54140"; UtlString app1uri = "sip:127.0.0.1:45141"; UtlString dialogHandle; // receive the reg-info subscribe SipMessage request; UtlString b; ssize_t l; while(getNextMessageFromAppearanceAgentUnderTest( request, 5 )) { request.getBytes(&b, &l); OsSysLog::add(FAC_RLS, PRI_DEBUG, "got message %s", b.data()); UtlString method; request.getRequestMethod(&method); if(!request.isResponse() && 0 == method.compareTo(SIP_SUBSCRIBE_METHOD) ) { // Accept the Subscription, regardless of whether it for a 'dialog' or 'reg' event // in order to stop retransmissions SipMessage regResponse; regResponse.setResponseData(&request, 202, "Accepted", app1uri); SipMessage * dispatchedMessage = new SipMessage(regResponse); dispatchedMessage->getBytes(&b, &l); OsSysLog::add(FAC_RLS, PRI_DEBUG, "sent message %s", b.data()); pAppearanceAgentUnderTest->mServerUserAgent.dispatch(dispatchedMessage); // Deal with the two events separately UtlString eventField; request.getEventField(eventField); if(0 == eventField.compareTo("reg")) { UtlString contactInfo; request.getContactUri(0, &contactInfo); UtlString callid; request.getCallIdField(&callid); int cseq; request.getCSeqField(&cseq, NULL); Url toField; regResponse.getToUrl(toField); SipMessage regNotify; regNotify.setNotifyData(&request, 1, "", "", "reg"); UtlString regInfo ("<?xml version=\"1.0\"?>\r\n" "<reginfo xmlns=\"urn:ietf:params:xml:ns:reginfo\" " "xmlns:gr=\"urn:ietf:params:xml:ns:gruuinfo\" version=\"911\" state=\"full\">\r\n" " <registration aor=\"sip:[email protected]:54140\" id=\"sip:[email protected]:54140\" state=\"active\">\r\n " " <contact id=\"sip:[email protected]:54140@@<"); regInfo.append(contactInfo); regInfo.append(">\" 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 ); }
void subscribeMwiClientTest() { smClientExpiration = -1; smNumClientNotifiesReceived = 0; smLastClientNotifyReceived = NULL; smNumClientSubResponsesReceived = 0; smLastClientSubResponseReceived = NULL; UtlString resourceId("[email protected]:"); UtlString eventTypeKey("message-summary"); UtlString eventType(eventTypeKey); UtlString from("Frida<sip:111@localhost:"); UtlString to("Tia<sip:222@localhost:"); UtlString contact("sip:[email protected]:"); char portString[20]; sprintf(portString, "%d", UNIT_TEST_SIP_PORT); resourceId.append(portString); from.append(portString); from.append('>'); to.append(portString); to.append('>'); contact.append(portString); SipUserAgent* userAgent = new SipUserAgent(UNIT_TEST_SIP_PORT, UNIT_TEST_SIP_PORT); userAgent->start(); // Set up the subscribe client SipDialogMgr* clientDialogMgr = new SipDialogMgr(); SipRefreshManager* refreshMgr = new SipRefreshManager(*userAgent, *clientDialogMgr); refreshMgr->start(); SipSubscribeClient* subClient = new SipSubscribeClient(*userAgent, *clientDialogMgr, *refreshMgr); subClient->start(); // Set up the subscribe server SipSubscribeServer* subServer = SipSubscribeServer::buildBasicServer(*userAgent, eventType); SipSubscriptionMgr* subMgr = subServer->getSubscriptionMgr(eventType); SipDialogMgr* serverDialogMgr = subMgr->getDialogMgr(); SipPublishContentMgr* contentMgr = subServer->getPublishMgr(eventType); HttpBody* preexistingBodyPtr = NULL; UtlBoolean isDefaultContent; subServer->start(); // Enable the handler for the MWI server subServer->enableEventType(eventType, userAgent); //CPPUNIT_ASSERT(TRUE); //ASSERT_STR_EQUAL("a", "a"); // Create a crude Subscription server/observer OsMsgQ incomingServerMsgQueue; // Register an interest in SUBSCRIBE requests // for this event type userAgent->addMessageObserver(incomingServerMsgQueue, SIP_SUBSCRIBE_METHOD, TRUE, // requests FALSE, // no reponses TRUE, // incoming FALSE, // no outgoing eventType, NULL, NULL); OsMsgQ incomingClientMsgQueue; userAgent->addMessageObserver(incomingClientMsgQueue, SIP_SUBSCRIBE_METHOD, FALSE, // no requests TRUE, // reponses TRUE, // incoming FALSE, // no outgoing eventType, NULL, NULL); // Should not be any pre-existing content CPPUNIT_ASSERT(!contentMgr->getContent(resourceId, eventTypeKey, NULL, preexistingBodyPtr, isDefaultContent)); int numDefaultContent = -1; int numResourceSpecificContent = -1; int numCallbacksRegistered = -1; contentMgr->getStats(numDefaultContent, numResourceSpecificContent, numCallbacksRegistered); CPPUNIT_ASSERT(numDefaultContent == 0); CPPUNIT_ASSERT(numResourceSpecificContent == 0); CPPUNIT_ASSERT(numCallbacksRegistered == 1); // Create a subscribe request, send it and keep it refreshed UtlString earlyDialogHandle; CPPUNIT_ASSERT(subClient->addSubscription(resourceId, eventType, from, to, contact, 60, // seconds expiration this, subStateCallback, notifyCallback, earlyDialogHandle)); contentMgr->getStats(numDefaultContent, numResourceSpecificContent, numCallbacksRegistered); CPPUNIT_ASSERT(numDefaultContent == 0); CPPUNIT_ASSERT(numResourceSpecificContent == 0); CPPUNIT_ASSERT(numCallbacksRegistered == 1); // See if a subscribe was sent and received /*OsTime messageTimeout(5, 0); // 5 seconds OsMsg* osMessage = NULL; const SipMessage* subscribeResponse = NULL; const SipMessage* notifyRequest = NULL; incomingServerMsgQueue.receive(osMessage, messageTimeout); CPPUNIT_ASSERT(osMessage); int msgType = osMessage->getMsgType(); int msgSubType = osMessage->getMsgSubType(); CPPUNIT_ASSERT(msgType == OsMsg::PHONE_APP); CPPUNIT_ASSERT(msgSubType == SipMessage::NET_SIP_MESSAGE); const SipMessage* sipMessage = ((SipMessageEvent*)osMessage)->getMessage(); int messageType = ((SipMessageEvent*)osMessage)->getMessageStatus(); CPPUNIT_ASSERT(sipMessage); CPPUNIT_ASSERT(messageType == SipMessageEvent::APPLICATION);*/ const SipMessage* serverSideSubRequest = NULL; CPPUNIT_ASSERT(removeMessage(incomingServerMsgQueue, 5000, // milliseconds serverSideSubRequest)); CPPUNIT_ASSERT(serverSideSubRequest); // Sub request got to server const SipMessage* clientSideSubResponse = NULL; CPPUNIT_ASSERT(removeMessage(incomingClientMsgQueue, 5000, // milliseconds clientSideSubResponse)); CPPUNIT_ASSERT(clientSideSubResponse); //UtlString clientStateString; //subClient->dumpStates(clientStateString); //printf("client states:\n%s\n", clientStateString.data()); int waitIterations = 0; while(smLastClientNotifyReceived == NULL || smLastClientSubResponseReceived == NULL) { OsTask::delay(100); waitIterations++; if(waitIterations >= 100) { break; } } CPPUNIT_ASSERT(smLastClientSubResponseReceived); CPPUNIT_ASSERT(smLastClientNotifyReceived); SipMessage* firstSubResponse = smLastClientSubResponseReceived; smLastClientSubResponseReceived = NULL; int firstSubCseq; firstSubResponse->getCSeqField(&firstSubCseq, NULL); SipMessage* firstNotifyRequest = smLastClientNotifyReceived; smLastClientNotifyReceived = NULL; int firstNotifyCseq; firstNotifyRequest->getCSeqField(&firstNotifyCseq, NULL); CPPUNIT_ASSERT(firstSubCseq == 1); CPPUNIT_ASSERT(firstNotifyCseq == 0); //subClient->dumpStates(clientStateString); //printf("client states:\n%s\n", clientStateString.data()); //UtlString dialogMgrDumpString; //clientDialogMgr.toString(dialogMgrDumpString); //printf("Client Dialog manager dump 1:\n%s\n", // dialogMgrDumpString.data()); // The refresh manager should re-SUBSCRIBE // Wait for the next notify request and subscribe response int secondMessageWait = 60; int resendTimeout = 0.55 * secondMessageWait; if(resendTimeout < 40) { resendTimeout = 40; } for(int i = 0; i < secondMessageWait - 1; i++) { if(i == resendTimeout - 1) { printf("v"); } else { printf("="); } } printf("v\n"); SipMessage* secondSubResponse = NULL; SipMessage* secondNotifyRequest = NULL; while(secondNotifyRequest == NULL || secondSubResponse == NULL) { OsTask::delay(1000); if(smLastClientSubResponseReceived) { secondSubResponse = smLastClientSubResponseReceived; smLastClientSubResponseReceived = NULL; } if(smLastClientNotifyReceived) { secondNotifyRequest = smLastClientNotifyReceived; smLastClientNotifyReceived = NULL; } printf("."); waitIterations++; if(waitIterations >= secondMessageWait) { printf("\n"); break; } } //subClient->dumpStates(clientStateString); //printf("client states:\n%s\n", clientStateString.data()); //clientDialogMgr.toString(dialogMgrDumpString); //printf("Client Dialog manager dump 2:\n%s\n", // dialogMgrDumpString.data()); CPPUNIT_ASSERT(removeMessage(incomingServerMsgQueue, 5000, // milliseconds serverSideSubRequest)); CPPUNIT_ASSERT(serverSideSubRequest); // Sub request got to server //UtlString subRequestDump; //int len; //serverSideSubRequest->getBytes(&subRequestDump, &len); //printf("server side sub request:\n%s\n", // subRequestDump.data()); CPPUNIT_ASSERT(removeMessage(incomingClientMsgQueue, 5000, // milliseconds clientSideSubResponse)); CPPUNIT_ASSERT(clientSideSubResponse); // Sub respon got to client //UtlString subResponseDump; //clientSideSubResponse->getBytes(&subResponseDump, &len); //printf("client side sub response:\n%s\n", // subResponseDump.data()); CPPUNIT_ASSERT(secondNotifyRequest); CPPUNIT_ASSERT(secondSubResponse); int secondSubCseq = -1; int secondNotifyCseq = -1; smLastClientSubResponseReceived = NULL; secondSubResponse->getCSeqField(&secondSubCseq, NULL); smLastClientNotifyReceived = NULL; secondNotifyRequest->getCSeqField(&secondNotifyCseq, NULL); CPPUNIT_ASSERT(firstSubCseq < secondSubCseq); CPPUNIT_ASSERT(firstNotifyCseq < secondNotifyCseq); // Unregister the queues so we stop receiving messages on them userAgent->removeMessageObserver(incomingServerMsgQueue); userAgent->removeMessageObserver(incomingClientMsgQueue); refreshMgr->requestShutdown(); subClient->requestShutdown(); }
UtlBoolean SipPersistentSubscriptionMgr::updateDialogInfo( const SipMessage& subscribeRequest, UtlString& resourceId, UtlString& eventTypeKey, UtlString& eventType, UtlString& subscribeDialogHandle, UtlBoolean& isNew, UtlBoolean& isSubscriptionExpired, SipMessage& subscribeResponse, SipSubscribeServerEventHandler& handler) { OsSysLog::add(FAC_SIP, PRI_DEBUG, "SipPersistentSubscriptionMgr::updateDialogInfo " "resourceId = '%s', eventTypeKey = '%s'", resourceId.data(), eventTypeKey.data()); UtlBoolean ret; // Call SipSubscriptionMgr to update the in-memory data. ret = SipSubscriptionMgr::updateDialogInfo(subscribeRequest, resourceId, eventTypeKey, eventType, subscribeDialogHandle, isNew, isSubscriptionExpired, subscribeResponse, handler); // If that succeeded, update the IMDB. if (ret) { UtlString requestUri; UtlString callId; UtlString contactEntry; UtlString to; UtlString from; UtlString route; UtlString accept; subscribeRequest.getRequestUri(&requestUri); subscribeRequest.getCallIdField(&callId); subscribeRequest.getContactEntry(0, &contactEntry); subscribeRequest.getToField(&to); subscribeRequest.getFromField(&from); subscribeRequest.buildRouteField(&route); accept.append(subscribeRequest.getHeaderValue(0, SIP_ACCEPT_FIELD)); int expires = 0; subscribeResponse.getExpiresField(&expires); expires += OsDateTime::getSecsSinceEpoch(); int subscribeCseq; UtlString subscribeCseqMethod; subscribeRequest.getCSeqField(&subscribeCseq, &subscribeCseqMethod); OsSysLog::add(FAC_SIP, PRI_DEBUG, "SipPersistentSubscriptionMgr::updateDialogInfo " "mComponent = '%s', requestUri = '%s', callId = '%s', contactEntry = '%s', expires = %d, to = '%s', from = '%s', key = '%s', route = '%s', accept = '%s'", mComponent.data(), requestUri.data(), callId.data(), contactEntry.data(), expires, to.data(), from.data(), resourceId.data(), route.data(), accept.data()); // Attempt to update an existing row. int now = (int)OsDateTime::getSecsSinceEpoch(); ret = mSubscriptionDBInstance->updateSubscribeUnexpiredSubscription( mComponent, to, from, callId, eventTypeKey, "", now, expires, subscribeCseq); if (!ret) { // Add a new row. // This call assumes that eventTypeKey is OK for use as the <eventtype>, // and that the NOTIFY CSeq's will start at 1. 0 is used as // the initial XML version. ret = mSubscriptionDBInstance->insertRow( mComponent, requestUri, callId, contactEntry, expires, subscribeCseq, eventTypeKey, "", to, from, resourceId, route, 1, accept, 0); if (!ret) { OsSysLog::add(FAC_SIP, PRI_ERR, "SipPersistantSubscriptionMgr::addSubscription " "Could not update or insert record in database"); } } // Start the save timer. mPersistenceTimer.oneshotAfter(sPersistInterval); } return ret; }
void SessionContext::CseqData::setValue( const SipMessage& message ) { message.getCSeqField( &mSeqNum, &mMethod ); }