Add sip redirect support (bug #3419i, with mods)
authorMark Spencer <markster@digium.com>
Sat, 19 Feb 2005 17:34:06 +0000 (17:34 +0000)
committerMark Spencer <markster@digium.com>
Sat, 19 Feb 2005 17:34:06 +0000 (17:34 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@5055 65c4cc65-6c06-0410-ace0-fbb531ad65f3

apps/app_transfer.c
channels/chan_sip.c

index 212792c..d46e34f 100755 (executable)
@@ -29,7 +29,7 @@ static char *app = "Transfer";
 static char *synopsis = "Transfer caller to remote extension";
 
 static char *descrip = 
 static char *synopsis = "Transfer caller to remote extension";
 
 static char *descrip = 
-"  Transfer(exten):  Requests the remote caller be transferred to\n"
+"  Transfer([Tech/]dest):  Requests the remote caller be transferred\n"
 "a given extension. Returns -1 on hangup, or 0 on completion\n"
 "regardless of whether the transfer was successful.  If the transfer\n"
 "was *not* supported or successful and there exists a priority n + 101,\n"
 "a given extension. Returns -1 on hangup, or 0 on completion\n"
 "regardless of whether the transfer was successful.  If the transfer\n"
 "was *not* supported or successful and there exists a priority n + 101,\n"
@@ -42,14 +42,21 @@ LOCAL_USER_DECL;
 static int transfer_exec(struct ast_channel *chan, void *data)
 {
        int res=0;
 static int transfer_exec(struct ast_channel *chan, void *data)
 {
        int res=0;
+       int len;
        struct localuser *u;
        struct localuser *u;
+       char *slash;
        if (!data || !strlen(data)) {
        if (!data || !strlen(data)) {
-               ast_log(LOG_WARNING, "Transfer requires an argument (destination)\n");
+               ast_log(LOG_WARNING, "Transfer requires an argument ([Tech/]destination)\n");
                res = 1;
        }
                res = 1;
        }
+       if ((slash = strchr((char *)data, '/')) && (len = (slash - (char *)data))) {
+               /* Allow execution only if the Tech/destination agrees with the type of the channel */
+               if (strncasecmp(chan->type, (char *)data, len))
+                       return 0;
+       }
        LOCAL_USER_ADD(u);
        if (!res) {
        LOCAL_USER_ADD(u);
        if (!res) {
-               res = ast_transfer(chan, data);
+               res = ast_transfer(chan, data + strlen(chan->type) + 1);
        }
        if (!res) {
                /* Look for a "busy" place */
        }
        if (!res) {
                /* Look for a "busy" place */
index 9a8ad83..5e8827f 100755 (executable)
@@ -389,6 +389,7 @@ static struct sip_pvt {
        struct sip_request initreq;             /* Initial request */
        
        int maxtime;                            /* Max time for first response */
        struct sip_request initreq;             /* Initial request */
        
        int maxtime;                            /* Max time for first response */
+       int maxforwards;                        /* keep the max-forwards info */
        int initid;                             /* Auto-congest ID if appropriate */
        int autokillid;                         /* Auto-kill ID */
        time_t lastrtprx;                       /* Last RTP received */
        int initid;                             /* Auto-congest ID if appropriate */
        int autokillid;                         /* Auto-kill ID */
        time_t lastrtprx;                       /* Last RTP received */
@@ -598,6 +599,7 @@ static int transmit_reinvite_with_sdp(struct sip_pvt *p);
 static int transmit_info_with_digit(struct sip_pvt *p, char digit);
 static int transmit_message_with_text(struct sip_pvt *p, char *text);
 static int transmit_refer(struct sip_pvt *p, char *dest);
 static int transmit_info_with_digit(struct sip_pvt *p, char digit);
 static int transmit_message_with_text(struct sip_pvt *p, char *text);
 static int transmit_refer(struct sip_pvt *p, char *dest);
+static int sip_sipredirect(struct sip_pvt *p, char *dest);
 static struct sip_peer *temp_peer(char *name);
 static int do_proxy_auth(struct sip_pvt *p, struct sip_request *req, char *header, char *respheader, char *msg, int init);
 static void free_old_route(struct sip_route *route);
 static struct sip_peer *temp_peer(char *name);
 static int do_proxy_auth(struct sip_pvt *p, struct sip_request *req, char *header, char *respheader, char *msg, int init);
 static void free_old_route(struct sip_route *route);
@@ -1968,6 +1970,10 @@ static int sip_transfer(struct ast_channel *ast, char *dest)
        int res;
 
        ast_mutex_lock(&p->lock);
        int res;
 
        ast_mutex_lock(&p->lock);
+       if (ast->_state == AST_STATE_RING)
+               res = sip_sipredirect(p, dest);
+       else
+               res = transmit_refer(p, dest);
        res = transmit_refer(p, dest);
        ast_mutex_unlock(&p->lock);
        return res;
        res = transmit_refer(p, dest);
        ast_mutex_unlock(&p->lock);
        return res;
@@ -3252,6 +3258,11 @@ static int respprep(struct sip_request *resp, struct sip_pvt *p, char *msg, stru
        } else {
                add_header(resp, "Contact", p->our_contact);
        }
        } else {
                add_header(resp, "Contact", p->our_contact);
        }
+       if (p->maxforwards) {
+               char tmp[256];
+               snprintf(tmp, sizeof(tmp), "%d", p->maxforwards);
+               add_header(resp, "Max-Forwards", tmp);
+       }
        return 0;
 }
 
        return 0;
 }
 
@@ -9786,7 +9797,6 @@ static char *descrip_sipgetheader = ""
 "Skips to priority+101 if header does not exist\n"
 "Otherwise returns 0\n";
 
 "Skips to priority+101 if header does not exist\n"
 "Otherwise returns 0\n";
 
-
 /*--- sip_dtmfmode: change the DTMFmode for a SIP call (application) ---*/
 static int sip_dtmfmode(struct ast_channel *chan, void *data)
 {
 /*--- sip_dtmfmode: change the DTMFmode for a SIP call (application) ---*/
 static int sip_dtmfmode(struct ast_channel *chan, void *data)
 {
@@ -9923,6 +9933,87 @@ static int sip_getheader(struct ast_channel *chan, void *data)
        return 0;
 }
 
        return 0;
 }
 
+#define DEFAULT_MAX_FORWARDS   70
+
+/* This is 302 sipredirect function coded by Martin Pycko (m78pl@yahoo.com) */
+static int sip_sipredirect(struct sip_pvt *p, char *dest)
+{
+       char *cdest;
+       char *extension, *host, *port;
+       char tmp[80];
+       if (!dest || ast_strlen_zero(dest)) {
+               ast_log(LOG_WARNING, "This application requires these arguments: SIPRedirect(extension[@host[:port]])\n");
+               return 0;
+       }
+       cdest = ast_strdupa(dest);
+       if (!cdest) {
+               ast_log(LOG_ERROR, "Problem allocating the memory\n");
+               return 0;
+       }
+       extension = strsep(&cdest, "@");
+       host = strsep(&cdest, ":");
+       port = strsep(&cdest, ":");
+       if (!extension) {
+               ast_log(LOG_ERROR, "Missing mandatory argument: extension\n");
+               return 0;
+       }
+
+       /* we'll issue the redirect message here */
+       if (!host) {
+               char *localtmp;
+               strncpy(tmp, get_header(&p->initreq, "To"), sizeof(tmp) - 1);
+               if (!strlen(tmp)) {
+                       ast_log(LOG_ERROR, "Cannot retrieve the 'To' header from the original SIP request!\n");
+                       return 0;
+               }
+               if ((localtmp = strstr(tmp, "sip:")) && (localtmp = strchr(localtmp, '@'))) {
+                       char lhost[80], lport[80];
+                       memset(lhost, 0, sizeof(lhost));
+                       memset(lport, 0, sizeof(lport));
+                       localtmp++;
+                       /* This is okay becuase lhost and lport are as big as tmp */
+                       sscanf(localtmp, "%[^<>:; ]:%[^<>:; ]", lhost, lport);
+                       if (!strlen(lhost)) {
+                               ast_log(LOG_ERROR, "Can't find the host address\n");
+                               return 0;
+                       }
+                       host = ast_strdupa(lhost);
+                       if (!host) {
+                               ast_log(LOG_ERROR, "Problem allocating the memory\n");
+                               return 0;
+                       }
+                       if (!ast_strlen_zero(lport)) {
+                               port = ast_strdupa(lport);
+                               if (!port) {
+                                       ast_log(LOG_ERROR, "Problem allocating the memory\n");
+                                       return 0;
+                               }
+                       }
+               }
+       }
+
+       /* make sure the forwarding won't be forever */
+       strncpy(tmp, get_header(&p->initreq, "Max-Forwards"), sizeof(tmp) - 1);
+       if (strlen(tmp) && atoi(tmp)) {
+               /* we found Max-Forwards in the original SIP request */
+               p->maxforwards = atoi(tmp) - 1;
+       } else {
+               /* just send our 302 Moved Temporarily */
+               p->maxforwards = DEFAULT_MAX_FORWARDS - 1;
+       }
+       if (p->maxforwards > -1) {
+               snprintf(p->our_contact, sizeof(p->our_contact), "redirect <sip:%s@%s%s%s>", extension, host, port ? ":" : "", port ? port : "");
+               transmit_response_reliable(p, "302 Moved Temporarily", &p->initreq, 1);
+       } else {
+               transmit_response(p, "483 Too Many Hops", &p->initreq);
+       }
+       /* this is all that we want to send to that SIP device */
+       ast_set_flag(p, SIP_ALREADYGONE);
+
+       /* hangup here */
+       return -1;
+}
+
 /*--- sip_get_codec: Return peers codec ---*/
 static int sip_get_codec(struct ast_channel *chan)
 {
 /*--- sip_get_codec: Return peers codec ---*/
 static int sip_get_codec(struct ast_channel *chan)
 {