diff --git a/CHANGES b/CHANGES index 77eeac81da..751c71ef7b 100755 --- 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 diff --git a/channel.c b/channel.c index 584f2bb7a5..032b693bcf 100755 --- 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; } diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 64a976e6e1..dbcb41128e 100755 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -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) diff --git a/channels/chan_zap.c b/channels/chan_zap.c index 035576451e..fb3639d18c 100755 --- a/channels/chan_zap.c +++ b/channels/chan_zap.c @@ -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 340e8c1d7f..04aa02cdbc 100755 --- 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)")); diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample index fe91d88188..ed1914cb68 100755 --- a/configs/sip.conf.sample +++ b/configs/sip.conf.sample @@ -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] diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 09cd2a6cfb..3ab0f647ab 100755 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -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; diff --git a/include/asterisk/parking.h b/include/asterisk/parking.h index 1434c5c3ad..24bb6826f4 100755 --- a/include/asterisk/parking.h +++ b/include/asterisk/parking.h @@ -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 */ diff --git a/res/res_parking.c b/res/res_parking.c index da918bd882..dc13af0246 100755 --- a/res/res_parking.c +++ b/res/res_parking.c @@ -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;