// // // Copyright (C) 2004 SIPfoundry Inc. // Licensed by SIPfoundry under the LGPL license. // // Copyright (C) 2004 Pingtel Corp. // Licensed to SIPfoundry under a Contributor Agreement. // // $$ ////////////////////////////////////////////////////////////////////////////// // SYSTEM INCLUDES #include #include // APPLICATION INCLUDES #include "os/OsSysLog.h" #include "os/OsDefs.h" #include "utl/UtlRegex.h" #include "net/Url.h" #include "net/SipMessage.h" #include "ForwardRules.h" // EXTERNAL FUNCTIONS // EXTERNAL VARIABLES // CONSTANTS /* //////////////////////////// PUBLIC //////////////////////////////////// */ // Constructor ForwardRules::ForwardRules() { mDoc = NULL; } // Destructor ForwardRules::~ForwardRules() { } /* ============================ MANIPULATORS ============================== */ OsStatus ForwardRules::loadMappings(const UtlString configFileName, const UtlString mediaserver, const UtlString& voicemail, const UtlString& localhost) { OsStatus currentStatus = OS_SUCCESS; if(mDoc) delete mDoc; mDoc = new TiXmlDocument( configFileName.data() ); if( !mDoc->LoadFile() ) { UtlString parseError = mDoc->ErrorDesc(); OsSysLog::add( FAC_SIP, PRI_ERR, "ERROR parsing forwardingrules '%s': %s" ,configFileName.data(), parseError.data()); return OS_NOT_FOUND; } if(!voicemail.isNull()) mVoicemail.append(voicemail); if(!localhost.isNull()) mLocalhost.append(localhost); if(!mediaserver.isNull()) mMediaServer.append(mediaserver); return currentStatus; } void ForwardRules::buildDefaultRules(const char* domain, const char* hostname, const char* ipAddress, const char* fqhn, int localPort) { if(mDoc) delete mDoc; mDoc = new TiXmlDocument(); buildDefaultRules(domain, hostname, ipAddress, fqhn, localPort, *mDoc); mDoc->Print(); } void ForwardRules::buildDefaultRules(const char* domain, const char* hostname, const char* ipAddress, const char* fqhn, int localPort, TiXmlDocument& xmlDoc) { // Note: fqhn == fully qualified host name UtlString hostnamePort(hostname ? hostname : "localhost"); UtlString domainPort(domain ? domain : ""); UtlString ipAddressPort(ipAddress ? ipAddress : "127.0.0.1"); UtlString fqhnPort(fqhn ? fqhn : "localhost"); if(localPort == 5060) localPort = PORT_NONE; if(portIsValid(localPort)) { char portString[40]; sprintf(portString,":%d", localPort); hostnamePort.append(portString); domainPort.append(portString); ipAddressPort.append(portString); fqhnPort.append(portString); } UtlString sdsAddress(fqhn); sdsAddress.append(":5090"); UtlString statusAddress(fqhn); statusAddress.append(":5110"); UtlString regAddress(fqhn); regAddress.append(":5070"); UtlString configAddress("sipuaconfig"); UtlString configFqhnAddress(configAddress); configFqhnAddress.append("."); configFqhnAddress.append(domain); TiXmlElement routes("routes"); TiXmlElement route("route"); route.SetAttribute("mappingType", "local"); TiXmlElement routeFromDomain("routeFrom"); TiXmlText domainText(domainPort.data()); TiXmlElement routeFromFqhn("routeFrom"); TiXmlText fqhnText(fqhnPort.data()); TiXmlElement routeFromHost("routeFrom"); TiXmlText hostText(hostnamePort.data()); TiXmlElement routeFromIp("routeFrom"); TiXmlText ipText(ipAddressPort.data()); TiXmlElement methodMatch("methodMatch"); TiXmlElement methodPattern("methodPattern"); TiXmlText subscribe("SUBSCRIBE"); TiXmlElement fieldMatchConfig("fieldMatch"); fieldMatchConfig.SetAttribute("fieldName", "Event"); TiXmlElement fieldPatternConfig("fieldPattern"); TiXmlText configEvent("sip-config"); TiXmlElement routeToSds("routeTo"); TiXmlText sdsText(sdsAddress.data()); TiXmlElement fieldMatchStatus("fieldMatch"); fieldMatchStatus.SetAttribute("fieldName", "Event"); TiXmlElement fieldPatternStatus("fieldPattern"); TiXmlText mwiEvent("message-summary*"); TiXmlElement routeToStatus("routeTo"); TiXmlText statusText(statusAddress.data()); TiXmlElement routeToReg("routeTo"); TiXmlText regText(regAddress.data()); TiXmlElement routeConfig("route"); TiXmlElement routeFromFqhnConfig("routeFrom"); TiXmlText fqhnConfigText(configFqhnAddress.data()); TiXmlElement routeFromConfig("routeFrom"); TiXmlText configText(configAddress.data()); // Link everyting up in reverse order as it TinyXml // makes copies routeFromDomain.InsertEndChild(domainText); route.InsertEndChild(routeFromDomain); routeFromHost.InsertEndChild(hostText); route.InsertEndChild(routeFromHost); routeFromFqhn.InsertEndChild(fqhnText); route.InsertEndChild(routeFromFqhn); routeFromIp.InsertEndChild(ipText); route.InsertEndChild(routeFromIp); methodPattern.InsertEndChild(subscribe); methodMatch.InsertEndChild(methodPattern); fieldPatternStatus.InsertEndChild(mwiEvent); fieldMatchStatus.InsertEndChild(fieldPatternStatus); routeToStatus.InsertEndChild(statusText); fieldMatchStatus.InsertEndChild(routeToStatus); methodMatch.InsertEndChild(fieldMatchStatus); fieldPatternConfig.InsertEndChild(configEvent); fieldMatchConfig.InsertEndChild(fieldPatternConfig); routeToSds.InsertEndChild(sdsText); fieldMatchConfig.InsertEndChild(routeToSds); methodMatch.InsertEndChild(fieldMatchConfig); routeToReg.InsertEndChild(regText); methodMatch.InsertEndChild(routeToReg); route.InsertEndChild(methodMatch); route.InsertEndChild(routeToReg); routeFromFqhnConfig.InsertEndChild(fqhnConfigText); routeConfig.InsertEndChild(routeFromFqhnConfig); routeFromConfig.InsertEndChild(configText); routeConfig.InsertEndChild(routeFromConfig); routeConfig.InsertEndChild(routeToReg); routes.InsertEndChild(route); routes.InsertEndChild(routeConfig); xmlDoc.InsertEndChild(routes); } /* ============================ CREATORS ================================== */ OsStatus ForwardRules::getRoute(const Url& requestUri, const SipMessage& request, UtlString& routeToString, UtlString& mappingType) { OsStatus currentStatus = OS_FAILED; // Get the "routes" element. TiXmlNode* prevRouteNode = mDoc->FirstChild( XML_TAG_ROUTES); if (!prevRouteNode) { OsSysLog::add(FAC_SIP, PRI_ERR, "UrlMapping::loadMappings - No child Node for Mappings"); return OS_FILE_READ_FAILED; } currentStatus = parseRouteMatchContainer(requestUri, request, routeToString, mappingType, prevRouteNode); return currentStatus; } OsStatus ForwardRules::parseRouteMatchContainer(const Url& requestUri, const SipMessage& request, UtlString& routeToString, UtlString& mappingType, TiXmlNode* routesNode, TiXmlNode* previousRouteMatchNode) { UtlString testHost; requestUri.getHostAddress(testHost); int testPort = requestUri.getHostPort(); if(testPort == SIP_PORT) { testPort = PORT_NONE; } UtlBoolean routeMatchFound = false; OsStatus methodMatchFound = OS_FAILED; TiXmlElement* routesElement = routesNode->ToElement(); TiXmlNode* routeMatchNode = previousRouteMatchNode; // Iterate through routes container children looking for // route tags while ( (routeMatchNode = routesElement->IterateChildren(routeMatchNode)) && methodMatchFound != OS_SUCCESS) { mappingType.remove(0); // Skip non-elements if(routeMatchNode && routeMatchNode->Type() != TiXmlNode::ELEMENT) { continue; } // Skip non-route elements TiXmlElement* routeMatchElement = routeMatchNode->ToElement(); UtlString tagValue = routeMatchElement->Value(); if(tagValue.compareTo(XML_TAG_ROUTEMATCH) != 0 ) { continue; } const char* mappingTypePtr = routeMatchElement->Attribute(XML_ATT_MAPPINGTYPE); //get the mapping Type attribute mappingType.append(mappingTypePtr ? mappingTypePtr : ""); // Iterate through the route container's children looking // for routeFrom elements TiXmlNode* routeFromPatternNode = NULL; for( routeFromPatternNode = routeMatchElement->FirstChild( XML_TAG_ROUTEFROM); routeFromPatternNode; routeFromPatternNode = routeFromPatternNode->NextSibling( XML_TAG_ROUTEFROM ) ) { // Skip non-elements if(routeFromPatternNode && routeFromPatternNode->Type() != TiXmlNode::ELEMENT) { continue; } //found routeFrom pattern tag TiXmlElement* routeFromPatternElement = routeFromPatternNode->ToElement(); //get the host text value from it TiXmlNode* routeFromPatternText = routeFromPatternElement->FirstChild(); if(routeFromPatternText && routeFromPatternText->Type() == TiXmlNode::TEXT) { TiXmlText* Xmlhost = routeFromPatternText->ToText(); if (Xmlhost) { UtlString host = Xmlhost->Value(); Url xmlUrl(host.data()); UtlString xmlHost; xmlUrl.getHostAddress(xmlHost); int xmlPort = xmlUrl.getHostPort(); // See if the host and port of the routeFrom elelment // match that of the URI if( (xmlHost.compareTo(testHost, UtlString::ignoreCase) == 0) && ((xmlPort == SIP_PORT && testPort == PORT_NONE) || xmlPort == testPort) ) { routeMatchFound = true; previousRouteMatchNode = routeMatchNode; // Find a match to the request method and recurse // to find child element field(s) matches and // get the routeTo value methodMatchFound = parseMethodMatchContainer(request, routeToString, routeMatchNode); if( methodMatchFound == OS_SUCCESS) break; } } } } } return methodMatchFound; } OsStatus ForwardRules::parseMethodMatchContainer(const SipMessage& request, UtlString& routeToString, TiXmlNode* routeMatchNode, TiXmlNode* previousMethodMatchNode) { OsStatus fieldMatchFound = OS_FAILED; UtlBoolean methodMatchFound = false; UtlString method; request.getRequestMethod(&method); TiXmlNode* methodMatchNode = previousMethodMatchNode; TiXmlElement* routeMatchElement = routeMatchNode->ToElement(); // Iterate through the children of the routeFrom container // looking for methodMatch elements while ( (methodMatchNode = routeMatchElement->IterateChildren( methodMatchNode)) && (fieldMatchFound != OS_SUCCESS) ) { // Skip non-elements if(methodMatchNode && methodMatchNode->Type() != TiXmlNode::ELEMENT) { continue; } // Skip non-methodMatch elements UtlString tagValue = methodMatchNode->Value(); if(tagValue.compareTo(XML_TAG_METHODMATCH) != 0 ) { continue; } //found methodPattern tag TiXmlElement* methodMatchElement = methodMatchNode->ToElement(); TiXmlNode* methodPatternNode = NULL; // Iteratore through the children of the methodMatch element // looking for the first methodPattern element that matches for( methodPatternNode = methodMatchElement->FirstChild( XML_TAG_METHODPATTERN); methodPatternNode; methodPatternNode = methodPatternNode->NextSibling(XML_TAG_METHODPATTERN ) ) { // Skip non-elements if(methodPatternNode && methodPatternNode->Type() != TiXmlNode::ELEMENT) { continue; } TiXmlElement* methodPatternElement = methodPatternNode->ToElement(); // Get the value contained in the methodPattern element TiXmlNode* methodPatternText = methodPatternElement->FirstChild(); if(methodPatternText && methodPatternText->Type() == TiXmlNode::TEXT) { TiXmlText* XmlMethod = methodPatternText->ToText(); if (XmlMethod) { // If the method of the request matches the method in // the methodMatch element UtlString methodString = XmlMethod->Value(); if (methodString.compareTo(method, UtlString::ignoreCase) == 0 ) { // Found a matching method, see if there is a fieldMatch // with a fieldName attribute that matches the fields // in the message methodMatchFound = true; fieldMatchFound = parseFieldMatchContainer(request, routeToString, methodMatchNode); if(fieldMatchFound == OS_SUCCESS) { break; } // None of the fields matched, see if the methodMatch // element has an immediate child routeTo element. // This is the "default" if none of the fieldMatches // matched. else { fieldMatchFound = getRouteTo(routeToString, methodMatchElement); if(fieldMatchFound == OS_SUCCESS) { break; } } } } } } } if(fieldMatchFound == OS_FAILED) { // if none of the method match were successfull or if no methodMatch node present // get the default routeTo for this routeNode. fieldMatchFound = getRouteTo(routeToString, routeMatchNode); } return fieldMatchFound; } OsStatus ForwardRules::parseFieldMatchContainer(const SipMessage& request, UtlString& routeToString, TiXmlNode* methodMatchNode, TiXmlNode* previousFieldMatchNode) { OsStatus getRouteFound = OS_FAILED; UtlBoolean fieldPatternFound = false; TiXmlNode* fieldMatchNode = previousFieldMatchNode; while ( (fieldMatchNode = methodMatchNode->IterateChildren( fieldMatchNode)) && (getRouteFound != OS_SUCCESS) ) { UtlBoolean fieldNameMatches = false; UtlBoolean noFieldPatternRequired = false; if(fieldMatchNode && fieldMatchNode->Type() != TiXmlNode::ELEMENT) { continue; } UtlString tagValue = fieldMatchNode->Value(); if(tagValue.compareTo(XML_TAG_FIELDMATCH) != 0 ) { continue; } TiXmlElement* fieldMatchElement = fieldMatchNode->ToElement(); TiXmlNode* fieldPatternNode = NULL; UtlBoolean fieldPatternPresent = false; UtlString fieldName; const char* fieldValuePtr = NULL; //check for fieldName parameter , if present check if it matches if(fieldMatchElement->Attribute(XML_ATT_FIELDNAME)) { UtlString fieldNameXml = fieldMatchElement->Attribute(XML_ATT_FIELDNAME); // If field name is specified, // check if it exists in the message if(!fieldNameXml.isNull()) { fieldValuePtr = request.getHeaderValue(0, fieldNameXml.data()); if(fieldValuePtr) { fieldNameMatches = true; } } } else { noFieldPatternRequired = true; } if(fieldNameMatches && !noFieldPatternRequired) { //get the user text value from it for( fieldPatternNode = fieldMatchElement->FirstChild( XML_TAG_FIELDPATTERN); fieldPatternNode; fieldPatternNode = fieldPatternNode->NextSibling(XML_TAG_FIELDPATTERN ) ) { fieldPatternPresent = true; TiXmlElement* fieldPatternElement = fieldPatternNode->ToElement(); TiXmlNode* fieldPatternText = fieldPatternElement->FirstChild(); if(fieldPatternText) { try { RegEx fieldPatternXml(fieldPatternText->Value(),PCRE_ANCHORED); if (fieldPatternXml.Search(fieldValuePtr)) { fieldPatternFound = true; } } catch(const char * ErrorMsg) { OsSysLog::add(FAC_SIP, PRI_ERR, "Illegal regular expression %s" " in forwardingrules.xml: %s", fieldPatternText->Value() ,ErrorMsg ); } } } } if( (fieldNameMatches && (fieldPatternFound || !fieldPatternPresent)) || noFieldPatternRequired ) { //get the routeTo field getRouteFound = getRouteTo(routeToString, fieldMatchNode); } } return getRouteFound; } OsStatus ForwardRules::getRouteTo(UtlString& RouteToString, TiXmlNode* nodeWithRouteToChild) { OsStatus currentStatus = OS_FAILED; nodeWithRouteToChild->ToElement(); TiXmlNode* routeToNode = NULL; TiXmlNode* routeToText = NULL; //get the user text value from it routeToNode = nodeWithRouteToChild->FirstChild( XML_TAG_ROUTETO); if(routeToNode) { currentStatus = OS_SUCCESS; if(routeToNode && routeToNode->Type() != TiXmlNode::ELEMENT) { return currentStatus; } TiXmlElement* routeToElement = routeToNode->ToElement(); routeToText = routeToElement->FirstChild(); if(routeToText && routeToText->Type() == TiXmlNode::TEXT) { TiXmlText* routeTo = routeToText->ToText(); if (routeTo) { currentStatus = OS_SUCCESS; RouteToString.append(routeTo->Value()); } } } return currentStatus; }