/// Remove identity info from a message. void SipXauthIdentity::remove(SipMessage & message, HeaderName headerName) { int idHeaderCount = message.getCountHeaderFields(headerName); if (idHeaderCount>0) { UtlString rUri; message.getRequestUri(&rUri); Os::Logger::instance().log(FAC_SIP, PRI_WARNING, "SipXauthIdentity::remove" ": '%d' occurrances of %s in request to '%s'", idHeaderCount, headerName, rUri.data()); for (int i = idHeaderCount - 1;i>=0;i--) { message.removeHeader(headerName, i); } } }
/// Normalize identity info in a message. void SipXauthIdentity::normalize(SipMessage & message, HeaderName headerName) { int idHeaderCount = message.getCountHeaderFields(headerName); if (idHeaderCount>1) { UtlString rUri; message.getRequestUri(&rUri); Os::Logger::instance().log(FAC_SIP, PRI_WARNING, "SipXauthIdentity::remove" ": '%d' occurrances of SipXauthIdentity in request to '%s'", idHeaderCount, rUri.data()); // Remove all BUT the last header for (int i = idHeaderCount - 2;i>=0;i--) { //message.removeHeader(SipXauthIdentity::AuthIdentityHeaderName, i); message.removeHeader(headerName, i); } } }
/// Check the signature and parse the identity contained in specified header name bool SipXauthIdentity::decode(const UtlString& headerName, const SipMessage& message, const UtlString& callId, const UtlString& fromTag, DialogRule bindRule ) { bool foundIdentityHeader = false; UtlString rUri; message.getRequestUri(&rUri); int idHeaderCount = message.getCountHeaderFields(headerName); if (1==idHeaderCount) { foundIdentityHeader = decode(message.getHeaderValue(0, headerName), callId, fromTag, bindRule); } else if (idHeaderCount>1) { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipXauthIdentity::decode:" " '%d' occurrences of %s in request to '%s'", idHeaderCount, headerName.data(), rUri.data()); foundIdentityHeader = decode(message.getHeaderValue(idHeaderCount-1, headerName), callId, fromTag, bindRule); } if (foundIdentityHeader) { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipXauthIdentity::decode:" " found %s '%s' in request to '%s'", headerName.data(), mIdentity.data(), rUri.data()); } return foundIdentityHeader; }
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); }