Implement call pickup on SIP, override context if appropriate
authorMark Spencer <markster@digium.com>
Wed, 9 Apr 2003 04:00:43 +0000 (04:00 +0000)
committerMark Spencer <markster@digium.com>
Wed, 9 Apr 2003 04:00:43 +0000 (04:00 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@791 65c4cc65-6c06-0410-ace0-fbb531ad65f3

CHANGES
channel.c
channels/chan_sip.c
channels/chan_zap.c
cli.c
configs/sip.conf.sample
include/asterisk/channel.h
include/asterisk/parking.h
res/res_parking.c

diff --git a/CHANGES b/CHANGES
index 77eeac8..751c71e 100755 (executable)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,4 @@
+ -- Support call pickup on SIP and compatibly with ZAP
  -- Support 302 Redirect on SIP
  -- Management interface improvements
  -- Add "hint" support
index 584f2bb..032b693 100755 (executable)
--- a/channel.c
+++ b/channel.c
@@ -1684,6 +1684,7 @@ int ast_channel_masquerade(struct ast_channel *original, struct ast_channel *clo
        /* XXX can't really hold the lock here, but at the same time, it' s
           not really safe not to XXX */
        ast_queue_frame(original, &null, 0);
+       ast_queue_frame(clone, &null, 0);
        return 0;
 }
 
index 64a976e..dbcb411 100755 (executable)
@@ -34,6 +34,7 @@
 #include <asterisk/app.h>
 #include <asterisk/musiconhold.h>
 #include <asterisk/dsp.h>
+#include <asterisk/parking.h>
 #include <sys/socket.h>
 #include <sys/ioctl.h>
 #include <net/if.h>
@@ -156,6 +157,8 @@ static struct sip_pvt {
        char randdata[80];      /* Random data */
        unsigned int ocseq;                                     /* Current outgoing seqno */
        unsigned int icseq;                                     /* Current incoming seqno */
+       unsigned int callgroup;
+       unsigned int pickupgroup;
        int lastinvite;                                         /* Last Cseq of invite */
        int alreadygone;                                        /* Whether or not we've already been destroyed by or peer */
        int needdestroy;                                        /* if we need to be destroyed */
@@ -235,6 +238,8 @@ struct sip_user {
        char callerid[80];
        char methods[80];
        char accountcode[80];
+       unsigned int callgroup;
+       unsigned int pickupgroup;
        int nat;
        int hascallerid;
        int amaflags;
@@ -262,6 +267,8 @@ struct sip_peer {
        int insecure;
        int nat;
        int canreinvite;
+       unsigned int callgroup;
+       unsigned int pickupgroup;
         int dtmfmode;
        struct sockaddr_in addr;
        struct in_addr mask;
@@ -583,6 +590,8 @@ static int create_addr(struct sip_pvt *r, char *peer)
                        r->insecure = p->insecure;
                        r->canreinvite = p->canreinvite;
                        r->maxtime = p->maxms;
+                       r->callgroup = p->callgroup;
+                       r->pickupgroup = p->pickupgroup;
                        if (p->dtmfmode) {
                                r->dtmfmode = p->dtmfmode;
                                if (r->dtmfmode & SIP_DTMF_RFC2833)
@@ -1109,6 +1118,8 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, char *title)
                tmp->pvt->fixup = sip_fixup;
                tmp->pvt->send_digit = sip_senddigit;
                tmp->pvt->bridge = ast_rtp_bridge;
+               tmp->callgroup = i->callgroup;
+               tmp->pickupgroup = i->pickupgroup;
                if (strlen(i->language))
                        strncpy(tmp->language, i->language, sizeof(tmp->language)-1);
                i->owner = tmp;
@@ -2971,13 +2982,15 @@ static int get_destination(struct sip_pvt *p, struct sip_request *oreq)
        }
        if (sipdebug)
                ast_verbose("Looking for %s in %s\n", c, p->context);
-       if (ast_exists_extension(NULL, p->context, c, 1, NULL)) {
+       if (ast_exists_extension(NULL, p->context, c, 1, NULL) ||
+               !strcmp(c, ast_pickup_ext())) {
                if (!oreq)
                        strncpy(p->exten, c, sizeof(p->exten) - 1);
                return 0;
        }
 
-       if (ast_canmatch_extension(NULL, p->context, c, 1, NULL)) {
+       if (ast_canmatch_extension(NULL, p->context, c, 1, NULL) ||
+           !strncmp(c, ast_pickup_ext(),strlen(c))) {
                return 1;
        }
        
@@ -3178,13 +3191,16 @@ static int check_user(struct sip_pvt *p, struct sip_request *req, char *cmd, cha
                        }
                        if (!(res = check_auth(p, req, p->randdata, sizeof(p->randdata), user->name, user->secret, cmd, uri, reliable))) {
                                sip_cancel_destroy(p);
-                               strncpy(p->context, user->context, sizeof(p->context) - 1);
+                               if (strlen(user->context))
+                                       strncpy(p->context, user->context, sizeof(p->context) - 1);
                                if (strlen(user->callerid) && strlen(p->callerid)) 
                                        strncpy(p->callerid, user->callerid, sizeof(p->callerid) - 1);
                                strncpy(p->username, user->name, sizeof(p->username) - 1);
                                strncpy(p->accountcode, user->accountcode, sizeof(p->accountcode)  -1);
                                p->canreinvite = user->canreinvite;
                                p->amaflags = user->amaflags;
+                               p->callgroup = user->callgroup;
+                               p->pickupgroup = user->pickupgroup;
                                if (user->dtmfmode) {
                                        p->dtmfmode = user->dtmfmode;
                                        if (p->dtmfmode & SIP_DTMF_RFC2833)
@@ -3212,6 +3228,10 @@ static int check_user(struct sip_pvt *p, struct sip_request *req, char *cmd, cha
                                }
                                p->canreinvite = peer->canreinvite;
                                strncpy(p->username, peer->name, sizeof(p->username) - 1);
+                               if (strlen(peer->context))
+                                       strncpy(p->context, peer->context, sizeof(p->context) - 1);
+                               p->callgroup = peer->callgroup;
+                               p->pickupgroup = peer->pickupgroup;
                                if (peer->dtmfmode) {
                                        p->dtmfmode = peer->dtmfmode;
                                        if (p->dtmfmode & SIP_DTMF_RFC2833)
@@ -4138,10 +4158,20 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc
                        case AST_STATE_DOWN:
                                transmit_response(p, "100 Trying", req);
                                ast_setstate(c, AST_STATE_RING);
-                               if (ast_pbx_start(c)) {
-                                       ast_log(LOG_WARNING, "Failed to start PBX :(\n");
+                               if (strcmp(p->exten, ast_pickup_ext())) {
+                                       if (ast_pbx_start(c)) {
+                                               ast_log(LOG_WARNING, "Failed to start PBX :(\n");
+                                               sip_hangup(c);
+                                               transmit_response_reliable(p, "503 Unavailable", req);
+                                               c = NULL;
+                                       }
+                               } else if (ast_pickup_call(c)) {
+                                       ast_log(LOG_WARNING, "Nothing to pick up\n");
                                        sip_hangup(c);
                                        transmit_response_reliable(p, "503 Unavailable", req);
+                               } else {
+                                       ast_pthread_mutex_unlock(&c->lock);
+                                       ast_hangup(c);
                                        c = NULL;
                                }
                                break;
@@ -4719,6 +4749,10 @@ static struct sip_user *build_user(char *name, struct ast_variable *v)
                        } else if (!strcasecmp(v->name, "callerid")) {
                                strncpy(user->callerid, v->value, sizeof(user->callerid)-1);
                                user->hascallerid=1;
+                       } else if (!strcasecmp(v->name, "callgroup")) {
+                               user->callgroup = ast_get_group(v->value);
+                       } else if (!strcasecmp(v->name, "pickupgroup")) {
+                               user->pickupgroup = ast_get_group(v->value);
                        } else if (!strcasecmp(v->name, "accountcode")) {
                                strncpy(user->accountcode, v->value, sizeof(user->accountcode)-1);
                        } else if (!strcasecmp(v->name, "amaflags")) {
@@ -4867,6 +4901,10 @@ static struct sip_peer *build_peer(char *name, struct ast_variable *v)
                                        ast_log(LOG_WARNING, "Cannot allow unknown format '%s'\n", v->value);
                                else
                                        peer->capability |= format;
+                       } else if (!strcasecmp(v->name, "callgroup")) {
+                               peer->callgroup = ast_get_group(v->value);
+                       } else if (!strcasecmp(v->name, "pickupgroup")) {
+                               peer->pickupgroup = ast_get_group(v->value);
                        } else if (!strcasecmp(v->name, "disallow")) {
                                format = ast_getformatbyname(v->value);
                                if (format < 1) 
index 0355764..fb3639d 100755 (executable)
@@ -134,9 +134,9 @@ static int use_callerid = 1;
 
 static int cur_signalling = -1;
 
-static int cur_group = 0;
-static int cur_callergroup = 0;
-static int cur_pickupgroup = 0;
+static unsigned int cur_group = 0;
+static unsigned int cur_callergroup = 0;
+static unsigned int cur_pickupgroup = 0;
 static int relaxdtmf = 0;
 
 static int immediate = 0;
@@ -370,13 +370,13 @@ static struct zt_pvt {
        char lastcallerid[AST_MAX_EXTENSION];
        char callwaitcid[AST_MAX_EXTENSION];
        char rdnis[AST_MAX_EXTENSION];
-       int group;
+       unsigned int group;
        int law;
        int confno;                                     /* Our conference */
        int confusers;                          /* Who is using our conference */
        int propconfno;                         /* Propagated conference number */
-       int callgroup;
-       int pickupgroup;
+       unsigned int callgroup;
+       unsigned int pickupgroup;
        int immediate;                          /* Answer before getting digits? */
        int channel;                            /* Channel Number */
        int span;                                       /* Span number */
@@ -475,32 +475,6 @@ static int cidrings[] = {
 #define CANBUSYDETECT(p) (ISTRUNK(p) || (p->sig & (SIG_EM | SIG_SF)) /* || (p->sig & __ZT_SIG_FXO) */)
 #define CANPROGRESSDETECT(p) (ISTRUNK(p) || (p->sig & (SIG_EM | SIG_SF)) /* || (p->sig & __ZT_SIG_FXO) */)
 
-#if 0
-/* return non-zero if clear dtmf is appropriate */
-static int CLEARDTMF(struct ast_channel *chan) {
-struct zt_pvt *p = chan->pvt->pvt,*themp;
-struct ast_channel *them;
-       if (!p)
-               return 0;
-         /* if not in a 3 way, we should be okay */
-       if (p->thirdcallindex == -1) return 1;
-         /* get the other side of the call's channel pointer */
-       if (p->owners[p->normalindex] == chan)
-               them = p->owners[p->thirdcallindex];
-       else
-               them = p->owners[p->normalindex];
-       if (!them)
-               return 0;
-       if (!them->bridge) return 1;
-         /* get their private structure, too */
-       themp = them->pvt->pvt;
-         /* if does not use zt bridge code, return 0 */
-       if (them->pvt->bridge != zt_bridge) return 0;
-       if (them->bridge->pvt->bridge != zt_bridge) return 0;
-       return 1; /* okay, I guess we are okay to be clear */
-}
-#endif
-
 static int zt_get_index(struct ast_channel *ast, struct zt_pvt *p, int nullok)
 {
        int res;
@@ -3449,6 +3423,11 @@ static struct ast_channel *zt_new(struct zt_pvt *i, int state, int startpbx, int
                tmp->pvt->indicate = zt_indicate;
                tmp->pvt->fixup = zt_fixup;
                tmp->pvt->setoption = zt_setoption;
+               if ((i->sig == SIG_FXOKS) || (i->sig == SIG_FXOGS) || (i->sig == SIG_FXOLS)) {
+                       /* Only FXO signalled stuff can be picked up */
+                       tmp->callgroup = i->callgroup;
+                       tmp->pickupgroup = i->pickupgroup;
+               }
                if (strlen(i->language))
                        strncpy(tmp->language, i->language, sizeof(tmp->language)-1);
                if (strlen(i->musicclass))
@@ -3814,46 +3793,34 @@ static void *ss_thread(void *data)
                                memset(exten, 0, sizeof(exten));
                                timeout = firstdigittimeout;
                                        
-                       } else if (!strcmp(exten,"*8#")){
+                       } else if (!strcmp(exten,ast_pickup_ext())) {
                                /* Scan all channels and see if any there
                                 * ringing channqels with that have call groups
                                 * that equal this channels pickup group  
                                 */
-                               struct zt_pvt *chan_pvt=iflist;
-                               while(chan_pvt!=NULL){
-                                       if((p!=chan_pvt) &&
-                                         (p->pickupgroup & chan_pvt->callgroup) &&
-                                         (chan_pvt->owner && (chan_pvt->owner->_state==AST_STATE_RING || chan_pvt->owner->_state == AST_STATE_RINGING)) &&
-                                         chan_pvt->dialing
-                                         ){
-                                               if (index == SUB_REAL) {
-                                                       if (p->subs[SUB_THREEWAY].owner) {
-                                                               /* If you make a threeway call and the *8# a call, it should actually 
-                                                                  look like a callwait */
-                                                               alloc_sub(p, SUB_CALLWAIT);
-                                                               swap_subs(p, SUB_CALLWAIT, SUB_THREEWAY);
-                                                               unalloc_sub(p, SUB_THREEWAY);
-                                                       }
-                                                       /* Switch us from Third call to Call Wait */
-                                                       ast_log(LOG_DEBUG, "Call pickup on chan %s\n",chan_pvt->owner->name);
-                                                       p->subs[index].needanswer=1;
-                                                       zt_enable_ec(p);
-                                                       if(ast_channel_masquerade(chan_pvt->owner,p->owner))
-                                                               printf("Error Masquerade failed on call-pickup\n");
-                                                       ast_hangup(p->owner);
-                                               } else {
-                                                       ast_log(LOG_WARNING, "Huh?  Got *8# on call not on real\n");
-                                                       ast_hangup(p->owner);
-                                               }
-                                               return NULL;
+                               if (index == SUB_REAL) {
+                                       /* Switch us from Third call to Call Wait */
+                                       if (p->subs[SUB_THREEWAY].owner) {
+                                               /* If you make a threeway call and the *8# a call, it should actually 
+                                                  look like a callwait */
+                                               alloc_sub(p, SUB_CALLWAIT);     
+                                               swap_subs(p, SUB_CALLWAIT, SUB_THREEWAY);
+                                               unalloc_sub(p, SUB_THREEWAY);
                                        }
-                                       chan_pvt=chan_pvt->next;
+                                       zt_enable_ec(p);
+                                       if (ast_pickup_call(chan)) {
+                                               ast_log(LOG_DEBUG, "No call pickup possible...\n");
+                                               res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_CONGESTION);
+                                               zt_wait_event(p->subs[index].zfd);
+                                       }
+                                       ast_hangup(chan);
+                                       return NULL;
+                               } else {
+                                       ast_log(LOG_WARNING, "Huh?  Got *8# on call not on real\n");
+                                       ast_hangup(chan);
+                                       return NULL;
                                }
-                               ast_log(LOG_DEBUG, "No call pickup possible...\n");
-                               res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_CONGESTION);
-                               zt_wait_event(p->subs[index].zfd);
-                               ast_hangup(chan);
-                               return NULL;
+                               
                        } else if (!p->hidecallerid && !strcmp(exten, "*67")) {
                                if (option_verbose > 2) 
                                        ast_verbose(VERBOSE_PREFIX_3 "Disabling Caller*ID on %s\n", chan->name);
@@ -5095,42 +5062,6 @@ static struct ast_channel *zt_request(char *type, int format, void *data)
 }
 
 
-static int get_group(char *s)
-{
-       char *copy;
-       char *piece;
-       char *c=NULL;
-       int start=0, finish=0,x;
-       int group = 0;
-       copy = strdup(s);
-       if (!copy) {
-               ast_log(LOG_ERROR, "Out of memory\n");
-               return 0;
-       }
-       c = copy;
-       piece = strsep(&c, ",");
-       while(piece) {
-               if (sscanf(piece, "%d-%d", &start, &finish) == 2) {
-                       /* Range */
-               } else if (sscanf(piece, "%d", &start)) {
-                       /* Just one */
-                       finish = start;
-               } else {
-                       ast_log(LOG_ERROR, "Syntax error parsing '%s' at '%s'.  Using '0'\n", s,piece);
-                       return 0;
-               }
-               piece = strsep(&c, ",");
-               for (x=start;x<=finish;x++) {
-                       if ((x > 31) || (x < 0)) {
-                               ast_log(LOG_WARNING, "Ignoring invalid group %d\n", x);
-                       } else
-                               group |= (1 << x);
-               }
-       }
-       free(copy);
-       return group;
-}
-
 #ifdef ZAPATA_PRI
 
 static int pri_find_empty_chan(struct zt_pri *pri)
@@ -6283,11 +6214,11 @@ int load_module()
                } else if (!strcasecmp(v->name, "stripmsd")) {
                        stripmsd = atoi(v->value);
                } else if (!strcasecmp(v->name, "group")) {
-                       cur_group = get_group(v->value);
+                       cur_group = ast_get_group(v->value);
                } else if (!strcasecmp(v->name, "callgroup")) {
-                       cur_callergroup = get_group(v->value);
+                       cur_callergroup = ast_get_group(v->value);
                } else if (!strcasecmp(v->name, "pickupgroup")) {
-                       cur_pickupgroup = get_group(v->value);
+                       cur_pickupgroup = ast_get_group(v->value);
                } else if (!strcasecmp(v->name, "immediate")) {
                        immediate = ast_true(v->value);
                } else if (!strcasecmp(v->name, "rxgain")) {
diff --git a/cli.c b/cli.c
index 340e8c1..04aa02c 100755 (executable)
--- a/cli.c
+++ b/cli.c
@@ -405,6 +405,8 @@ static int handle_showchan(int fd, int argc, char *argv[])
        "        Context: %s\n"
        "      Extension: %s\n"
        "       Priority: %d\n"
+       "     Call Group: %d\n"
+       "   Pickup Group: %d\n"
        "    Application: %s\n"
        "           Data: %s\n"
        "          Stack: %d\n"
@@ -414,7 +416,7 @@ static int handle_showchan(int fd, int argc, char *argv[])
        (c->dnid ? c->dnid : "(N/A)" ), ast_state2str(c->_state), c->_state, c->rings, c->nativeformats, c->writeformat, c->readformat,
        c->fds[0], c->fin & 0x7fffffff, (c->fin & 0x80000000) ? " (DEBUGGED)" : "",
        c->fout & 0x7fffffff, (c->fout & 0x80000000) ? " (DEBUGGED)" : "", c->whentohangup,
-       c->context, c->exten, c->priority, ( c->appl ? c->appl : "(N/A)" ),
+       c->context, c->exten, c->priority, c->callgroup, c->pickupgroup, ( c->appl ? c->appl : "(N/A)" ),
        ( c-> data ? (strlen(c->data) ? c->data : "(Empty)") : "(None)"),
        c->stack, (c->blocking ? c->blockproc : "(Not Blocking)"));
        
index fe91d88..ed1914c 100755 (executable)
@@ -27,6 +27,8 @@ context = default             ; Default for incoming calls
 ;secret=blah
 ;host=dynamic
 ;qualify=1000                  ; Consider it down if it's 1 second to reply
+;callgroup=1,3-4
+;pickupgroup=1,3-4
 ;defaultip=192.168.0.60
 
 ;[cisco]
index 09cd2a6..3ab0f64 100755 (executable)
@@ -186,6 +186,9 @@ struct ast_channel {
        /* A linked list for variables */
        struct ast_var_t *vars; 
        AST_LIST_HEAD(varshead,ast_var_t) varshead;
+
+       unsigned int callgroup;
+       unsigned int pickupgroup;
        
        /*! For easy linking */
        struct ast_channel *next;               
index 1434c5c..24bb682 100755 (executable)
@@ -1,7 +1,7 @@
 /*
  * Asterisk -- A telephony toolkit for Linux.
  *
- * Call Parking API 
+ * Call Parking and Pickup API 
  * 
  * Copyright (C) 1999, Mark Spencer
  *
@@ -42,10 +42,15 @@ extern int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *hos
 /*! Returns the call parking extension for drivers that provide special
     call parking help */
 extern char *ast_parking_ext(void);
+extern char *ast_pickup_ext(void);
 
 //! Bridge a call, optionally allowing redirection
 
 extern int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, int allowredirect, int allowdisconnect);
 
+extern unsigned int ast_get_group(char *s);
+
+extern int ast_pickup_call(struct ast_channel *chan);
+
 
 #endif /* _PARKING_H */
index da918bd..dc13af0 100755 (executable)
@@ -49,6 +49,8 @@ static char parking_con[AST_MAX_EXTENSION] = "parkedcalls";
 /* Extension you type to park the call */
 static char parking_ext[AST_MAX_EXTENSION] = "700";
 
+static char pickup_ext[AST_MAX_EXTENSION] = "*8";
+
 /* First available extension for parking */
 static int parking_start = 701;
 
@@ -93,6 +95,11 @@ char *ast_parking_ext(void)
        return parking_ext;
 }
 
+char *ast_pickup_ext(void)
+{
+       return pickup_ext;
+}
+
 int ast_park_call(struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout)
 {
        /* We put the user in the parking list, then wake up the parking thread to be sure it looks
@@ -565,6 +572,72 @@ int load_module(void)
        return res;
 }
 
+int ast_pickup_call(struct ast_channel *chan)
+{
+       struct ast_channel *cur;
+       int res = -1;
+       cur = ast_channel_walk(NULL);
+       while(cur) {
+               if (!cur->pbx && 
+                       (cur != chan) &&
+                       (chan->pickupgroup & cur->callgroup) &&
+                       ((cur->_state == AST_STATE_RINGING) ||
+                        (cur->_state == AST_STATE_RING))) {
+                               break;
+               }
+               cur = ast_channel_walk(cur);
+       }
+       if (cur) {
+               ast_log(LOG_DEBUG, "Call pickup on chan '%s' by '%s'\n",cur->name, chan->name);
+               res = ast_answer(chan);
+               if (res)
+                       ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
+               res = ast_queue_control(chan, AST_CONTROL_ANSWER, 0);
+               if (res)
+                       ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name);
+               res = ast_channel_masquerade(cur, chan);
+               if (res)
+                       ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, cur->name);           /* Done */
+       } else  {
+               ast_log(LOG_DEBUG, "No call pickup possible...\n");
+       }
+       return res;
+}
+
+unsigned int ast_get_group(char *s)
+{
+       char *copy;
+       char *piece;
+       char *c=NULL;
+       int start=0, finish=0,x;
+       unsigned int group = 0;
+       copy = strdupa(s);
+       if (!copy) {
+               ast_log(LOG_ERROR, "Out of memory\n");
+               return 0;
+       }
+       c = copy;
+       
+       while((piece = strsep(&c, ","))) {
+               if (sscanf(piece, "%d-%d", &start, &finish) == 2) {
+                       /* Range */
+               } else if (sscanf(piece, "%d", &start)) {
+                       /* Just one */
+                       finish = start;
+               } else {
+                       ast_log(LOG_ERROR, "Syntax error parsing '%s' at '%s'.  Using '0'\n", s,piece);
+                       return 0;
+               }
+               for (x=start;x<=finish;x++) {
+                       if ((x > 31) || (x < 0)) {
+                               ast_log(LOG_WARNING, "Ignoring invalid group %d\n", x);
+                       } else
+                               group |= (1 << x);
+               }
+       }
+       return group;
+}
+
 int unload_module(void)
 {
        STANDARD_HANGUP_LOCALUSERS;