/*
    Copyright (C) 2002-2005  Thomas Ries <tries@gmx.net>

    This file is part of Siproxd.
    
    Siproxd is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
    
    Siproxd is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with Siproxd; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
*/

#include "config.h"

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include <osipparser2/osip_parser.h>

#include "siproxd.h"
#include "log.h"

static char const ident[]="$Id: register.c,v 1.46 2005/01/08 10:05:12 hb9xar Exp $";

/* configuration storage */
extern struct siproxd_config configuration;

struct urlmap_s urlmap[URLMAP_SIZE];		/* URL mapping table     */

extern int errno;
/*
 * initialize the URL mapping table
 */
void register_init(void) {
   FILE *stream;
   int sts, size, i;
   char buff[128];

   memset(urlmap, 0, sizeof(urlmap));

   if (configuration.registrationfile) {
      stream = fopen(configuration.registrationfile, "r");

      if (!stream) {
         /*
          * the file does not exist, or the size was incorrect,
          * delete it and start from scratch
          */
         unlink(configuration.registrationfile);
         WARN("registration file not found, starting with empty table");
      } else {
         /* read the url table from file */
         for (i=0;i < URLMAP_SIZE; i++) {
            fgets(buff, sizeof(buff), stream);
            sts=sscanf(buff, "***:%i:%i", &urlmap[i].active, &urlmap[i].expires);
            if (urlmap[i].active) {
               osip_uri_init(&urlmap[i].true_url);
               osip_uri_init(&urlmap[i].masq_url);
               osip_uri_init(&urlmap[i].reg_url);

	       #define R(X) {\
               fgets(buff, sizeof(buff), stream);\
               buff[sizeof(buff)-1]='\0';\
               if (strchr(buff, 10)) *strchr(buff, 10)='\0';\
               if (strchr(buff, 13)) *strchr(buff, 13)='\0';\
               if (strlen(buff) > 0) {\
                  size = strlen(buff);\
                  X    =(char*)malloc(size+1);\
                  sts=sscanf(buff,"%s",X);\
               } else {\
                  X = NULL;\
               }\
               }

               R(urlmap[i].true_url->scheme);
               R(urlmap[i].true_url->username);
               R(urlmap[i].true_url->host);
               R(urlmap[i].true_url->port);
               R(urlmap[i].masq_url->scheme);
               R(urlmap[i].masq_url->username);
               R(urlmap[i].masq_url->host);
               R(urlmap[i].masq_url->port);
               R(urlmap[i].reg_url->scheme);
               R(urlmap[i].reg_url->username);
               R(urlmap[i].reg_url->host);
               R(urlmap[i].reg_url->port);
            }
         }
         fclose(stream);
      }
   }
   return;
}


/*
 * shut down the URL mapping table
 */
void register_shut(void) {
   int i;
   FILE *stream;

   if (configuration.registrationfile) {
      /* write urlmap back to file */
      stream = fopen(configuration.registrationfile, "w+");
      if (!stream) {
         /* try to unlink it and open again */
         unlink(configuration.registrationfile);
         stream = fopen(configuration.registrationfile, "w+");

         /* open file for write failed, complain */
         if (!stream) {
            ERROR("unable to write registration file");
            return;
         }
      }

      for (i=0;i < URLMAP_SIZE; i++) {
         fprintf(stream, "***:%i:%i\n", urlmap[i].active, urlmap[i].expires);
         if (urlmap[i].active) {
            #define W(X) fprintf(stream, "%s\n", (X)? X:"");

            W(urlmap[i].true_url->scheme);
            W(urlmap[i].true_url->username);
            W(urlmap[i].true_url->host);
            W(urlmap[i].true_url->port);
            W(urlmap[i].masq_url->scheme);
            W(urlmap[i].masq_url->username);
            W(urlmap[i].masq_url->host);
            W(urlmap[i].masq_url->port);
            W(urlmap[i].reg_url->scheme);
            W(urlmap[i].reg_url->username);
            W(urlmap[i].reg_url->host);
            W(urlmap[i].reg_url->port);
         }
      }
      fclose(stream);
   }
   return;
}


/*end added*/
/*
 * handles register requests and updates the URL mapping table
 *
 * RETURNS:
 *    STS_SUCCESS : successfully registered
 *    STS_FAILURE : registration failed
 *    STS_NEED_AUTH : authentication needed
 */
 #if 0
int register_client(sip_ticket_t *ticket, int force_lcl_masq) {
   int i, j, n, sts;
   int expires;
   time_t time_now;
   osip_uri_t *url1_to, *url1_contact=NULL;
   osip_uri_t *url2_to;
   osip_header_t *expires_hdr;
   osip_uri_param_t *expires_param=NULL;
   
   /*
    * Authorization - do only if I'm not just acting as outbound proxy
    * but am ment to be the registrar
    */
   if (force_lcl_masq == 0) {
      /*
       * RFC 3261, Section 16.3 step 6
       * Proxy Behavior - Request Validation - Proxy-Authorization
       */
      sts = authenticate_proxy(ticket);
      if (sts == STS_FAILURE) {
         /* failed */
         WARN("proxy authentication failed for %s@%s",
              (ticket->sipmsg->to->url->username)? 
              ticket->sipmsg->to->url->username : "*NULL*",
              ticket->sipmsg->to->url->host);
         return STS_FAILURE;
      } else if (sts == STS_NEED_AUTH) {
         /* needed */
         DEBUGC(DBCLASS_REG,"proxy authentication needed for %s@%s",
                ticket->sipmsg->to->url->username,
                ticket->sipmsg->to->url->host);
         return STS_NEED_AUTH;
      }
   }

/*
   fetch 1st Via entry and remember this address. Incoming requests
   for the registered address have to be passed on to that host.

   To: -> address to be registered
   Contact: -> host is reachable there
               Note: in case of un-REGISTER, the contact header may
                     contain '*' only - which means "all registrations
                     made by this UA"
   
   => Mapping is
   To: <1--n> Contact
   
*/
   time(&time_now);

   DEBUGC(DBCLASS_BABBLE,"sip_register:");

   /* evaluate Expires Header field */
   osip_message_get_expires(ticket->sipmsg, 0, &expires_hdr);

  /*
   * look for an Contact expires parameter - in case of REGISTER
   * these two are equal. The Contact expires has higher priority!
   */
   if (ticket->sipmsg->contacts && ticket->sipmsg->contacts->node &&
       ticket->sipmsg->contacts->node->element) {
      osip_contact_param_get_byname(
              (osip_contact_t*) ticket->sipmsg->contacts->node->element,
              EXPIRES, &expires_param);
   }

   if (expires_param && expires_param->gvalue) {
      /* get expires from contact Header */
      expires=atoi(expires_param->gvalue);
   } else if (expires_hdr && expires_hdr->hvalue) {
      /* get expires from expires Header */
      expires=atoi(expires_hdr->hvalue);
   } else {
      char tmp[16];
      /* it seems, the expires field is not present everywhere... */
      DEBUGC(DBCLASS_REG,"no 'expires' header found - set time to %i sec",
             configuration.default_expires);
      expires=configuration.default_expires;
      sprintf(tmp,"%i",expires);
      osip_message_set_expires(ticket->sipmsg, tmp);
   }

   url1_to=ticket->sipmsg->to->url;



   /*
    * REGISTER
    */
   if (expires > 0) {
      /*
       * First make sure, we have a prober Contact header:
       *  - url
       *  - url -> hostname
       *
       * Libosip parses an:
       * "Contact: *"
       * the following way (Note: Display name!! and URL is NULL)
       * (gdb) p *((osip_contact_t*)(sip->contacts->node->element))
       * $5 = {displayname = 0x8af8848 "*", url = 0x0, gen_params = 0x8af8838}
       */
      if (ticket->sipmsg->contacts && ticket->sipmsg->contacts->node &&
          ticket->sipmsg->contacts->node->element) {
         url1_contact=((osip_contact_t*)
                      (ticket->sipmsg->contacts->node->element))->url;
      }
      if ((url1_contact == NULL) || (url1_contact->host == NULL)) {
         /* Don't have required Contact fields */
         ERROR("tried registration with empty Contact header");
         return STS_FAILURE;
      }
         
      DEBUGC(DBCLASS_REG,"register: %s@%s expires=%i seconds",
          (url1_contact->username) ? url1_contact->username : "*NULL*",
          (url1_contact->host) ? url1_contact->host : "*NULL*",
          expires);

      /*
       * Update registration. There are two possibilities:
       * - already registered, then update the existing record
       * - not registered, then create a new record
       */

      j=-1;
      for (i=0; i<URLMAP_SIZE; i++) {
         if (urlmap[i].active == 0) {
	    if (j < 0) j=i; /* remember first hole */
            continue;
         }

         url2_to=urlmap[i].reg_url;

         /* check address-of-record ("public address" of user) */
         if (compare_url(url1_to, url2_to)==STS_SUCCESS) {
            DEBUGC(DBCLASS_REG, "found entry for %s@%s <-> %s@%s at "
                   "slot=%i, exp=%li",
	           (url1_contact->username) ? url1_contact->username : "*NULL*",
                   (url1_contact->host) ? url1_contact->host : "*NULL*",
	           (url2_to->username) ? url2_to->username : "*NULL*",
                   (url2_to->host) ? url2_to->host : "*NULL*",
		   i, urlmap[i].expires-time_now);
            break;
         }
      }

      if ( (j < 0) && (i >= URLMAP_SIZE) ) {
         /* oops, no free entries left... */
         ERROR("URLMAP is full - registration failed");
         return STS_FAILURE;
      }

      if (i >= URLMAP_SIZE) {
          DEBUGC(DBCLASS_REG,"register_client entry not existing, create new one\n");
         /* entry not existing, create new one */
         i=j;

         /* write entry */
         urlmap[i].active=1;
         /* Contact: field */
         osip_uri_clone( ((osip_contact_t*)
                         (ticket->sipmsg->contacts->node->element))->url, 
        	         &urlmap[i].true_url);
         /* To: field */
         osip_uri_clone( ticket->sipmsg->to->url, 
        	    &urlmap[i].reg_url);

         DEBUGC(DBCLASS_REG,"create new entry for %s@%s <-> %s@%s at slot=%i",
                (url1_contact->username) ? url1_contact->username : "*NULL*",
                (url1_contact->host) ? url1_contact->host : "*NULL*",
	        (urlmap[i].reg_url->username) ? urlmap[i].reg_url->username : "*NULL*",
                (urlmap[i].reg_url->host) ? urlmap[i].reg_url->host : "*NULL*",
                i);

         /*
          * try to figure out if we ought to do some masquerading
          */
         osip_uri_clone( ticket->sipmsg->to->url, 
        	         &urlmap[i].masq_url);

         n=configuration.mask_host.used;
         if (n != configuration.masked_host.used) {
            ERROR("# of mask_host is not equal to # of masked_host in config!");
            n=0;
         }

         DEBUG("%i entries in MASK config table", n);
         for (j=0; j<n; j++) {
            DEBUG("compare [%s] <-> [%s]",configuration.mask_host.string[j],
                  ticket->sipmsg->to->url->host);
            if (strcmp(configuration.mask_host.string[j],
                ticket->sipmsg->to->url->host)==0)
               break;
         }
         if (j<n) { 
            /* we are masquerading this UA, replace the host part of the url */
            DEBUGC(DBCLASS_REG,"masquerading UA %s@%s as %s@%s",
                   (url1_contact->username) ? url1_contact->username : "*NULL*",
                   (url1_contact->host) ? url1_contact->host : "*NULL*",
                   (url1_contact->username) ? url1_contact->username : "*NULL*",
                   configuration.masked_host.string[j]);
            urlmap[i].masq_url->host=realloc(urlmap[i].masq_url->host,
                                    strlen(configuration.masked_host.string[j])+1);
            strcpy(urlmap[i].masq_url->host, configuration.masked_host.string[j]);
         }

         /*
          * for transparent proxying: force device to be masqueraded
          * as with the outbound IP
          */
         if (force_lcl_masq) {
            struct in_addr addr;
            char *addrstr;

            if (get_ip_by_ifname(configuration.outbound_if, &addr) !=
                STS_SUCCESS) {
               ERROR("can't find outbound interface %s - configuration error?",
                     configuration.outbound_if);
               return STS_FAILURE;
            }

            /* host part */
            addrstr = utils_inet_ntoa(addr);
            DEBUGC(DBCLASS_REG,"masquerading UA %s@%s local %s@%s",
                   (url1_contact->username) ? url1_contact->username : "*NULL*",
                   (url1_contact->host) ? url1_contact->host : "*NULL*",
                   (url1_contact->username) ? url1_contact->username : "*NULL*",
                   addrstr);
            urlmap[i].masq_url->host=realloc(urlmap[i].masq_url->host,
                                             strlen(addrstr)+1);
            strcpy(urlmap[i].masq_url->host, addrstr);
            DEBUGC(DBCLASS_REG,"register_client urlmap i %d",i);
/*added by xuezhongbo for ALG v2 2007.11.08:11:35*/
#if 0
            /* port number if required */
            if (configuration.sip_listen_port != SIP_PORT) {
               urlmap[i].masq_url->port=realloc(urlmap[i].masq_url->port, 16);
               sprintf(urlmap[i].masq_url->port, "%i",
                       configuration.sip_listen_port);
            }
#else
               urlmap[i].masq_url->port=realloc(urlmap[i].masq_url->port, 16);
               sprintf(urlmap[i].masq_url->port, "%i",
                       configuration.sip_listen_port);
#endif
/*end added*/
         }

      } else { /* if new entry */
      /*
       * Some phones (like BudgeTones *may* dynamically grab a SIP port
       * so we might want to update the true_url and reg_url each time
       * we get an REGISTER
       */
         DEBUGC(DBCLASS_REG,"register_client use the old one");
         /* Contact: field */
         osip_uri_free(urlmap[i].true_url);
         osip_uri_clone( ((osip_contact_t*)
                         (ticket->sipmsg->contacts->node->element))->url, 
        	         &urlmap[i].true_url);
         /* To: field */
         osip_uri_free(urlmap[i].reg_url);
         osip_uri_clone( ticket->sipmsg->to->url, 
        	    &urlmap[i].reg_url);
      }
      /* give some safety margin for the next update */
      if (expires > 0) expires+=30;

      /* update registration timeout */
    //  urlmap[i].expires=time_now+expires;
        urlmap[i].expires = time_now + 2;
        DEBUGC(DBCLASS_REG,"=regist_client:  urlmap[%d].expires time_now %d %d=",i, urlmap[i].expires,time_now);

   /*
    * un-REGISTER
    */
   } else { /* expires > 0 */
      /*
       * Remove registration
       * Siproxd will ALWAYS remove ALL bindings for a given
       * address-of-record
       */
      for (i=0; i<URLMAP_SIZE; i++) {
         if (urlmap[i].active == 0) continue;

         url2_to=urlmap[i].reg_url;

         if (compare_url(url1_to, url2_to)==STS_SUCCESS) {
            DEBUGC(DBCLASS_REG, "removing registration for %s@%s at slot=%i",
	           (url2_to->username) ? url2_to->username : "*NULL*",
                   (url2_to->host) ? url2_to->host : "*NULL*", i);
            urlmap[i].expires=time_now + 2;
            DEBUGC(DBCLASS_REG,"=register_client unregister urlmap[%d].expires time_now %d %d=",i,urlmap[i].expires,time_now);
            break;
         }
      }
     /*added by xuezhongbo for ALG 2007.10.30*/
    if(i>=URLMAP_SIZE)
		return STS_FSTUNREGISTER;
    /*end added*/
   }

   return STS_SUCCESS;
}
#else
int register_client(sip_ticket_t *ticket, int force_lcl_masq){
   int i, j, n, sts;
   int expires;
   time_t time_now;
   osip_uri_t *url1_to, *url1_contact=NULL;
   osip_contact_t *contact = NULL; //add by xuezhongbo on 20071227 for unregister contact * */
   osip_uri_t *url2_to;
   osip_header_t *expires_hdr;
   osip_uri_param_t *expires_param=NULL;
   
   /*
    * Authorization - do only if I'm not just acting as outbound proxy
    * but am ment to be the registrar
    */
   if (force_lcl_masq == 0) {
      /*
       * RFC 3261, Section 16.3 step 6
       * Proxy Behavior - Request Validation - Proxy-Authorization
       */
      sts = authenticate_proxy(ticket);
      if (sts == STS_FAILURE) {
         /* failed */
         WARN("proxy authentication failed for %s@%s",
              (ticket->sipmsg->to->url->username)? 
              ticket->sipmsg->to->url->username : "*NULL*",
              ticket->sipmsg->to->url->host);
         return STS_FAILURE;
      } else if (sts == STS_NEED_AUTH) {
         /* needed */
         DEBUGC(DBCLASS_REG,"proxy authentication needed for %s@%s",
                ticket->sipmsg->to->url->username,
                ticket->sipmsg->to->url->host);
         return STS_NEED_AUTH;
      }
   }

/*
   fetch 1st Via entry and remember this address. Incoming requests
   for the registered address have to be passed on to that host.

   To: -> address to be registered
   Contact: -> host is reachable there
               Note: in case of un-REGISTER, the contact header may
                     contain '*' only - which means "all registrations
                     made by this UA"
   
   => Mapping is
   To: <1--n> Contact
   
*/
   time(&time_now);

   DEBUGC(DBCLASS_BABBLE,"sip_register:");

   /* evaluate Expires Header field */
   osip_message_get_expires(ticket->sipmsg, 0, &expires_hdr);

  /*
   * look for an Contact expires parameter - in case of REGISTER
   * these two are equal. The Contact expires has higher priority!
   */
   if (ticket->sipmsg->contacts && ticket->sipmsg->contacts->node &&
       ticket->sipmsg->contacts->node->element) {
      osip_contact_param_get_byname(
              (osip_contact_t*) ticket->sipmsg->contacts->node->element,
              EXPIRES, &expires_param);
   }

   if (expires_param && expires_param->gvalue) {
      /* get expires from contact Header */
      expires=atoi(expires_param->gvalue);
   } else if (expires_hdr && expires_hdr->hvalue) {
      /* get expires from expires Header */
      expires=atoi(expires_hdr->hvalue);
   } else {
      char tmp[16];
      /* it seems, the expires field is not present everywhere... */
      DEBUGC(DBCLASS_REG,"no 'expires' header found - set time to %i sec",
             configuration.default_expires);
      expires=configuration.default_expires;
      sprintf(tmp,"%i",expires);
      osip_message_set_expires(ticket->sipmsg, tmp);
   }

   url1_to=ticket->sipmsg->to->url;



   /*
    * REGISTER
    */
{
      /*
       * First make sure, we have a prober Contact header:
       *  - url
       *  - url -> hostname
       *
       * Libosip parses an:
       * "Contact: *"
       * the following way (Note: Display name!! and URL is NULL)
       * (gdb) p *((osip_contact_t*)(sip->contacts->node->element))
       * $5 = {displayname = 0x8af8848 "*", url = 0x0, gen_params = 0x8af8838}
       */  
       /*add by xuezhongbo on 20071227 for unregister contact * */
          if(osip_message_get_contact(ticket->sipmsg,0,&contact)==-1)
          {
              DEBUGC(DBCLASS_REG,"get contact error"); 
              return STS_FAILURE;
     
          }
          else
          {
              if(contact!=NULL)
              {
                 
                 if(contact->displayname)
                     {
                         DEBUGC(DBCLASS_REG,"contact->displaynamen %s",contact->displayname);
                     }
                 if(contact->url)
                     {
                         url1_contact = contact->url;
                         DEBUGC(DBCLASS_REG,"contact scheme username host port %s<%s@%s:%s>",
                                contact->url->scheme?contact->url->scheme:"*NULL*",
                                contact->url->username?contact->url->username:"*NULL*",
                                contact->url->host?contact->url->host:"*NULL*",
                                contact->url->port?contact->url->port:"*NULL*"
                                );
                     }                     
              }
              else
              {
                  return STS_FAILURE;
                  DEBUGC(DBCLASS_REG,"get contact error");                     
              }
          }
      /*end add*/
      
      /*
       * Update registration. There are two possibilities:
       * - already registered, then update the existing record
       * - not registered, then create a new record
       */

      j=-1;
      for (i=0; i<URLMAP_SIZE; i++) {
         if (urlmap[i].active == 0) {
	    if (j < 0) j=i; /* remember first hole */
            continue;
         }

         url2_to=urlmap[i].reg_url;

         /* check address-of-record ("public address" of user) */
         if (compare_url(url1_to, url2_to)==STS_SUCCESS) {
             if(url1_contact)
             {
                 DEBUGC(DBCLASS_REG, "found entry for %s@%s <-> %s@%s at "
                                     "slot=%i, exp=%li",
	                   (url1_contact->username) ? url1_contact->username : "*NULL*",
                       (url1_contact->host) ? url1_contact->host : "*NULL*",
	                   (url2_to->username) ? url2_to->username : "*NULL*",
                       (url2_to->host) ? url2_to->host : "*NULL*",
		               i, urlmap[i].expires-time_now);
		     }
            break;
         }
      }

      if ( (j < 0) && (i >= URLMAP_SIZE) ) {
         /* oops, no free entries left... */
         ERROR("URLMAP is full - registration failed");
         return STS_FAILURE;
      }

      if (i >= URLMAP_SIZE) {
          DEBUGC(DBCLASS_REG,"register_client entry not existing, create new one\n");
         /* entry not existing, create new one */
         i=j;

         /* write entry */
         urlmap[i].active=1;
         /* Contact: field */
         #if 0
         osip_uri_clone( ((osip_contact_t*)
                         (ticket->sipmsg->contacts->node->element))->url, 
        	         &urlmap[i].true_url);
         #else
         osip_uri_clone(contact->url,&urlmap[i].true_url);         
         #endif
         /* To: field */
         osip_uri_clone( ticket->sipmsg->to->url, 
        	    &urlmap[i].reg_url);

         if(url1_contact!=NULL)
             DEBUGC(DBCLASS_REG,"create new entry for %s@%s <-> %s@%s at slot=%i",
                   (url1_contact->username) ? url1_contact->username : "*NULL*",
                   (url1_contact->host) ? url1_contact->host : "*NULL*",
                   (urlmap[i].reg_url->username) ? urlmap[i].reg_url->username : "*NULL*",
                   (urlmap[i].reg_url->host) ? urlmap[i].reg_url->host : "*NULL*",
                   i);

         /*
          * try to figure out if we ought to do some masquerading
          */
         osip_uri_clone( ticket->sipmsg->to->url, 
        	         &urlmap[i].masq_url);

         n=configuration.mask_host.used;
         if (n != configuration.masked_host.used) {
            ERROR("# of mask_host is not equal to # of masked_host in config!");
            n=0;
         }

         DEBUG("%i entries in MASK config table", n);
         for (j=0; j<n; j++) {
            DEBUG("compare [%s] <-> [%s]",configuration.mask_host.string[j],
                  ticket->sipmsg->to->url->host);
            if (strcmp(configuration.mask_host.string[j],
                ticket->sipmsg->to->url->host)==0)
               break;
         }
         if (j<n) { 
            /* we are masquerading this UA, replace the host part of the url */
            if(url1_contact)
                DEBUGC(DBCLASS_REG,"masquerading UA %s@%s as %s@%s",
                      (url1_contact->username) ? url1_contact->username : "*NULL*",
                      (url1_contact->host) ? url1_contact->host : "*NULL*",
                      (url1_contact->username) ? url1_contact->username : "*NULL*",
                      configuration.masked_host.string[j]);
            urlmap[i].masq_url->host=realloc(urlmap[i].masq_url->host,
                                    strlen(configuration.masked_host.string[j])+1);
            strcpy(urlmap[i].masq_url->host, configuration.masked_host.string[j]);
         }

         /*
          * for transparent proxying: force device to be masqueraded
          * as with the outbound IP
          */
         if (force_lcl_masq) {
            struct in_addr addr;
            char *addrstr;

            /* mod by weigy on 20100531 for the multi wan route select */
            #if MULTI_WAN
            if (get_ip_by_ifname(ticket->sctable_num ? configuration.outbound[ticket->sctable_num-1].name : 
                    find_outboundif(ticket->lan), &addr) !=
                STS_SUCCESS) {
               ERROR("can't find outbound interface %s - configuration error?",
                     ticket->sctable_num ? configuration.outbound[ticket->sctable_num-1].name : 
                    find_outboundif(ticket->lan));
               return STS_FAILURE;
            }
            #else
            if (get_ip_by_ifname(configuration.outbound_if, &addr) !=
                STS_SUCCESS) {
               ERROR("can't find outbound interface %s - configuration error?",
                     configuration.outbound_if);
               return STS_FAILURE;
            }
            #endif
            /* mod end */

            /* host part */
            addrstr = utils_inet_ntoa(addr);
            if(url1_contact)
            DEBUGC(DBCLASS_REG,"masquerading UA %s@%s local %s@%s",
                   (url1_contact->username) ? url1_contact->username : "*NULL*",
                   (url1_contact->host) ? url1_contact->host : "*NULL*",
                   (url1_contact->username) ? url1_contact->username : "*NULL*",
                   addrstr);
            urlmap[i].masq_url->host=realloc(urlmap[i].masq_url->host,
                                             strlen(addrstr)+1);
            strcpy(urlmap[i].masq_url->host, addrstr);
            DEBUGC(DBCLASS_REG,"register_client urlmap i %d",i);
/*added by xuezhongbo for ALG v2 2007.11.08:11:35*/
#if 0
            /* port number if required */
            if (configuration.sip_listen_port != SIP_PORT) {
               urlmap[i].masq_url->port=realloc(urlmap[i].masq_url->port, 16);
               sprintf(urlmap[i].masq_url->port, "%i",
                       configuration.sip_listen_port);
            }
#else
               urlmap[i].masq_url->port=realloc(urlmap[i].masq_url->port, 16);
               sprintf(urlmap[i].masq_url->port, "%i",
                       configuration.sip_listen_port);
#endif
/*end added*/
         }

      } 
      else { /* if new entry */
      /*
       * Some phones (like BudgeTones *may* dynamically grab a SIP port
       * so we might want to update the true_url and reg_url each time
       * we get an REGISTER
       */
         DEBUGC(DBCLASS_REG,"register_client use the old one");
         /* Contact: field */
         osip_uri_free(urlmap[i].true_url);
         osip_uri_clone( ((osip_contact_t*)
                         (ticket->sipmsg->contacts->node->element))->url, 
        	             &urlmap[i].true_url);
         /* To: field */
         osip_uri_free(urlmap[i].reg_url);
         osip_uri_clone( ticket->sipmsg->to->url,&urlmap[i].reg_url);
      }
      /* give some safety margin for the next update */
      if (expires > 0) expires+=30;

      /* update registration timeout */
    //  urlmap[i].expires=time_now+expires;
        urlmap[i].expires = time_now + 2;
        DEBUGC(DBCLASS_REG,"=regist_client:  urlmap[%d].expires time_now %d %d=",i, urlmap[i].expires,time_now);
   } 

   return STS_SUCCESS;
}
#endif


/*
 * cyclically called to do the aging of the URL mapping table entries
 * and throw out expired entries.
 */
void register_agemap(void) {
   int i;
   time_t t;
   
   time(&t);
   //DEBUGC(DBCLASS_BABBLE,"sip_agemap, t=%i\n",(int)t);
   for (i=0; i<URLMAP_SIZE; i++) {
      if ((urlmap[i].active == 1) && (urlmap[i].expires < t)) {
	 DEBUGC(DBCLASS_REG,"cleaned entry:%i %s@%s nowtime %d", i,
	        urlmap[i].masq_url->username,  urlmap[i].masq_url->host,t);
         urlmap[i].active=0;
         osip_uri_free(urlmap[i].true_url);
         osip_uri_free(urlmap[i].masq_url);
         osip_uri_free(urlmap[i].reg_url);
    //osip_via_free(urlmap[i].via);
      }
      }
   return;
}


/*
 * send answer to a registration request.
 *  flag = STS_SUCCESS    -> positive answer (200)
 *  flag = STS_FAILURE    -> negative answer (503)
 *  flag = STS_NEED_AUTH  -> proxy authentication needed (407)
 *
 * RETURNS
 *	STS_SUCCESS on success
 *	STS_FAILURE on error
 */
int register_response(sip_ticket_t *ticket, int flag) {
   osip_message_t *response;
   int code;
   int sts;
   osip_via_t *via;
   int port;
   char *buffer;
   struct in_addr addr;
   osip_header_t *expires_hdr;

   /* ok -> 200, fail -> 503 */
   switch (flag) {
   case STS_SUCCESS:
      code = 200;	/* OK */
      break;
   case STS_FAILURE:
      code = 503;	/* failed */
      break;
   case STS_NEED_AUTH:
      code = 407;	/* proxy authentication needed */
      break;
   default:
      code = 503;	/* failed */
      break;
   }

   /* create the response template */
   if ((response=msg_make_template_reply(ticket, code))==NULL) {
      ERROR("register_response: error in msg_make_template_reply");
      return STS_FAILURE;
   }

   /* insert the expiration header */
   osip_message_get_expires(ticket->sipmsg, 0, &expires_hdr);
   if (expires_hdr) {
      osip_message_set_expires(response, expires_hdr->hvalue);
   }

   /* if we send back an proxy authentication needed, 
      include the Proxy-Authenticate field */
   if (code == 407) {
      auth_include_authrq(ticket);
   }

   /* get the IP address from existing VIA header */
   osip_message_get_via (response, 0, &via);
   if (via == NULL) {
      ERROR("register_response: Cannot send response - no via field");
      return STS_FAILURE;
   }

   /* name resolution needed? */
   if (utils_inet_aton(via->host,&addr) == 0) {
      /* yes, get IP address */
      sts = get_ip_by_host(via->host, &addr);
      if (sts == STS_FAILURE) {
         DEBUGC(DBCLASS_REG, "register_response: cannot resolve VIA [%s]",
                via->host);
         return STS_FAILURE;
      }
   }   

   sts = osip_message_to_str(response, &buffer);
   if (sts != 0) {
      ERROR("register_response: msg_2char failed");
      return STS_FAILURE;
   }

   /* send answer back */
   if (via->port) {
      port=atoi(via->port);
   } else {
      port=configuration.sip_listen_port;
   }

   sipsock_send(addr, port, ticket->protocol, buffer, strlen(buffer));

   /* free the resources */
   osip_message_free(response);
   free(buffer);
   return STS_SUCCESS;
}

