// 
// 
// 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 <assert.h>

// APPLICATION INCLUDES
#include <SipRouter.h>
#include <ForwardRules.h>
#include <net/SipUserAgent.h>
#include <os/OsSysLog.h>

// EXTERNAL FUNCTIONS
// EXTERNAL VARIABLES
// CONSTANTS
// STATIC VARIABLE INITIALIZATIONS

/* //////////////////////////// PUBLIC //////////////////////////////////// */

/* ============================ CREATORS ================================== */

// Constructor
SipRouter::SipRouter(SipUserAgent& sipUserAgent, 
                     ForwardRules& forwardingRules,
                     bool          useAuthServer,
                     const char*   authServer,
                     bool          shouldRecordRoute) :
   OsServerTask("SipRouter-%d", NULL, 2000),
   mpSipUserAgent(&sipUserAgent),
   mpForwardingRules(&forwardingRules),
   mShouldRecordRoute(shouldRecordRoute),
   mAuthEnabled(useAuthServer)
{
   if ( mAuthEnabled )
   {
      if ( authServer && *authServer != '\000' )
      {
         // Construct the loose route URI to be used
         Url nextHopUrl(authServer);
         // Add a loose route to the mapped server
         nextHopUrl.setUrlParameter("lr", NULL);
         nextHopUrl.toString(mAuthRoute);
         OsSysLog::add(FAC_SIP, PRI_INFO, "SipRouter: auth server route '%s'", mAuthRoute.data());
      }
      else
      {
         mAuthEnabled = false;
         OsSysLog::add(FAC_SIP, PRI_ERR,
                       "SipRouter::SipRouter auth server enabled but no address set");
      }
   }

   if ( shouldRecordRoute )
   {
      UtlString uriString;
      int port;

      mpSipUserAgent->getViaInfo(OsSocket::UDP, uriString, port);
      Url routeUrl;
      routeUrl.setHostAddress(uriString.data());
      routeUrl.setHostPort(port);
      routeUrl.setUrlParameter("lr", NULL);

      routeUrl.toString(mRecordRoute);

      OsSysLog::add(FAC_SIP, PRI_DEBUG,
                    "SipRouter Record-Route address: '%s'",
                    mRecordRoute.data());
   }

   // Register to get incoming requests
   OsMsgQ* queue = getMessageQueue();
   sipUserAgent.addMessageObserver(*queue,
                                   "", // All methods
                                   TRUE, // Requests,
                                   FALSE, //Responses,
                                   TRUE, //Incoming,
                                   FALSE, //OutGoing,
                                   "", //eventName,
                                   NULL, //   SipSession* pSession,
                                   NULL); //observerData)

}

// Destructor
SipRouter::~SipRouter()
{
   // Remove our message observer from *mpSipUserAgent, as it may
   // outlive us.
   mpSipUserAgent->removeMessageObserver(*getMessageQueue(),
                                         NULL);
}

/* ============================ MANIPULATORS ============================== */

UtlBoolean SipRouter::handleMessage(OsMsg& eventMessage)
{
	if(OsMsg::PHONE_APP == eventMessage.getMsgType())
	{
		SipMessageEvent* sipMsgEvent = dynamic_cast<SipMessageEvent*>(&eventMessage);

      int messageType = sipMsgEvent->getMessageStatus();
      if(messageType == SipMessageEvent::TRANSPORT_ERROR)
      {
         OsSysLog::add(FAC_SIP, PRI_CRIT,
                       "SipRouter::handleMessage received transport error message");
      }
		else
		{
         SipMessage* sipRequest = const_cast<SipMessage*>(sipMsgEvent->getMessage());
         if(sipRequest)
         {
            if(!sipRequest->isResponse())
            {
               if (proxyMessage(*sipRequest))
               {
                  // clear timestamps, protocol, and port information so send will recalculate it
                  sipRequest->resetTransport();
                  mpSipUserAgent->send(*sipRequest);
               }
            }
            else 
            {
               // got a response - should never happen
               OsSysLog::add(FAC_SIP, PRI_CRIT,
                             "SipRouter::handleMessage received response");
            }
         }
         else 
         {
            // not a SIP message - should never happen
            OsSysLog::add(FAC_SIP, PRI_CRIT,
                          "SipRouter::handleMessage is not a sip message");
         }
      }
   }

   return TRUE;
}

bool SipRouter::proxyMessage(SipMessage& sipRequest)
{
   bool proxyMessage = false;
   
   // TODO - [XPR-183] Check for loops here.

   /*
    * Check for a Proxy-Require header containing unsupported extensions
    */
   UtlString extension;
   UtlString disallowedExtensions;
   for (int extensionIndex = 0;
        sipRequest.getProxyRequireExtension(extensionIndex, &extension);
        extensionIndex++
        )
   {
      if(!mpSipUserAgent->isExtensionAllowed(extension.data()))
      {
         if(!disallowedExtensions.isNull())
         {
            disallowedExtensions.append(SIP_MULTIFIELD_SEPARATOR);
            disallowedExtensions.append(SIP_SINGLE_SPACE);
         }
         disallowedExtensions.append(extension.data());
      }
   }
   if (!disallowedExtensions.isNull())
   {
      SipMessage* response = new SipMessage();
      response->setRequestBadExtension(&sipRequest, disallowedExtensions.data());
      mpSipUserAgent->setServerHeader(*response);
      mpSipUserAgent->send(*response);
   }
   else
   {
   /*
    * Check the request URI and the topmost route
    *   - Detect and correct for any strict router upstream
    *     as specified by RFC 3261 section 16.4 Route Information Preprocessing
    *   - Pop off the topmost route until it is not me
    */
   Url requestUri;
   sipRequest.normalizeProxyRoutes(mpSipUserAgent, requestUri);
   // requestUri is set to the target uri from the request line after normalization
   
   UtlString topRouteValue;
   if (sipRequest.getRouteUri(0, &topRouteValue)) 
   {
      /*
       * There is a top route that is not to this domain
       * (if the top route were to this domain, it would have been removed),
       * so let the authproxy decide whether or not it can go through
       */
      addAuthRoute(sipRequest);
   }
   else // there is no Route header, so route on the Request URI
   {
      UtlString mappedTo;
      UtlString routeType;               
                  
      // see if we have a mapping for the normalized request uri
      if (   mpForwardingRules 
          && (mpForwardingRules->getRoute(requestUri, sipRequest, mappedTo, routeType)==OS_SUCCESS)
          )
      {
         // Yes, so add a loose route to the mapped server
         Url nextHopUrl(mappedTo);
         nextHopUrl.setUrlParameter("lr", NULL);
         UtlString routeString;
         nextHopUrl.toString(routeString);
         sipRequest.addRouteUri(routeString.data());

         OsSysLog::add(FAC_SIP, PRI_DEBUG,
                       "SipRouter fowardingrules added route type '%s' to: '%s'",
                       routeType.data(), routeString.data());
      }
      else
      {
         // the mapping rules didn't have any route for this,
         // so let the authproxy decide whether or not it can go through
         addAuthRoute(sipRequest);
      }
   }

   if(mShouldRecordRoute) // If record route is configureed
   {
      sipRequest.addRecordRouteUri(mRecordRoute);
   }

   // Decrement max forwards
   //   we don't need to check for zero - the stack would already have rejected it.
   int maxForwards;
   if (sipRequest.getMaxForwards(maxForwards))
   {
      maxForwards--;
   }
   else
   {
      maxForwards = mpSipUserAgent->getMaxForwards();
   }

   sipRequest.setMaxForwards(maxForwards);
   proxyMessage = true;
   }
   
   return proxyMessage;
}

void SipRouter::addAuthRoute(SipMessage& request)
{
   if (mAuthEnabled)
   {
      OsSysLog::add(FAC_SIP, PRI_DEBUG, "SipRouter routing through auth proxy");
      // Add a loose route to the auth server
      request.addRouteUri(mAuthRoute.data());
   }
}

/* ============================ ACCESSORS ================================= */

/* ============================ INQUIRY =================================== */

/* //////////////////////////// PROTECTED ///////////////////////////////// */

/* //////////////////////////// PRIVATE /////////////////////////////////// */

/* ============================ FUNCTIONS ================================= */



syntax highlighted by Code2HTML, v. 0.9.1