From b92c0a231fa237381b2e9d2a67e303d621c74d1b Mon Sep 17 00:00:00 2001 From: markster Date: Mon, 7 May 2001 03:15:48 +0000 Subject: [PATCH] Version 0.1.8 from FTP git-svn-id: http://svn.digium.com/svn/asterisk/trunk@316 f38db490-d61c-443f-a65b-d21fe96a405b --- apps/app_dial.c | 323 +++++++++++++++++++++++++++--------------------- 1 file changed, 181 insertions(+), 142 deletions(-) diff --git a/apps/app_dial.c b/apps/app_dial.c index abfb2dd37..c1739a0ce 100755 --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -27,6 +27,7 @@ #include #include #include +#include #include @@ -36,11 +37,31 @@ static char *app = "Dial"; static char *parkedcall = "ParkedCall"; -/* No more than 90 seconds parked before you do something with them */ -static int parkingtime = 90000; +static char *synopsis = "Place an call and connect to the current channel"; + +static char *registrar = "app_dial"; + +static char *descrip = +" Dial(Technology/resource[&Technology2/resource2...][|timeout][|transfer]): Requests one or more channels\n" +" and places specified outgoing calls on them. As soon as a channel answers, the Dial app\n" +" will answer the originating channel (if it needs to be answered) and will bridge a call\n" +" with the channel which first answered. All other calls placed by the Dial app will be\n" +" hung up. If a timeout is not specified, the Dial application will wait indefinitely until\n" +" either one of the called channels answers, the user hangs up, or all channels return busy or\n" +" error. In general, the dialler will return 0 if it was unable to place the call, or the\n" +" timeout expired. However, if all channels were busy, and there exists an extension with\n" +" priority n+101 (where n is the priority of the dialler instance), then it will be the\n" +" next executed extension (this allows you to setup different behavior on busy from no-answer)\n." +" This application returns -1 if the originating channel hangs up, or if the call is bridged and\n" +" either of the parties in the bridge terminate the call. The transfer string may contain a\n" +" 't' to allow the called user transfer a call or 'T' to allow the calling user to transfer the call.\n" +" In addition to transferring the call, a call may be parked and then picked up by another user.\n"; + +/* No more than 45 seconds parked before you do something with them */ +static int parkingtime = 45000; /* Context for which parking is made accessible */ -static char parking_con[AST_MAX_EXTENSION] = "default"; +static char parking_con[AST_MAX_EXTENSION] = "parkedcalls"; /* Extension you type to park the call */ static char parking_ext[AST_MAX_EXTENSION] = "700"; @@ -92,7 +113,7 @@ static int park_call(struct ast_channel *chan, struct ast_channel *peer) int x; pu = malloc(sizeof(struct parkeduser)); if (pu) { - pthread_mutex_lock(&parking_lock); + ast_pthread_mutex_lock(&parking_lock); for (x=parking_start;x<=parking_stop;x++) { cur = parkinglot; while(cur) { @@ -114,7 +135,7 @@ static int park_call(struct ast_channel *chan, struct ast_channel *peer) pu->priority = chan->priority; pu->next = parkinglot; parkinglot = pu; - pthread_mutex_unlock(&parking_lock); + ast_pthread_mutex_unlock(&parking_lock); /* Wake up the (presumably select()ing) thread */ pthread_kill(parking_thread, SIGURG); if (option_verbose > 1) @@ -124,7 +145,7 @@ static int park_call(struct ast_channel *chan, struct ast_channel *peer) } else { ast_log(LOG_WARNING, "No more parking spaces\n"); free(pu); - pthread_mutex_unlock(&parking_lock); + ast_pthread_mutex_unlock(&parking_lock); return -1; } } else { @@ -140,6 +161,7 @@ static void *do_parking_thread(void *ignore) struct parkeduser *pu, *pl, *pt = NULL; struct timeval tv; struct ast_frame *f; + int x; fd_set rfds, efds; fd_set nrfds, nefds; FD_ZERO(&rfds); @@ -147,7 +169,7 @@ static void *do_parking_thread(void *ignore) for (;;) { ms = -1; max = -1; - pthread_mutex_lock(&parking_lock); + ast_pthread_mutex_lock(&parking_lock); pl = NULL; pu = parkinglot; gettimeofday(&tv, NULL); @@ -174,43 +196,50 @@ static void *do_parking_thread(void *ignore) pt = pu; pu = pu->next; free(pt); - } else if (FD_ISSET(pu->chan->fd, &rfds) || FD_ISSET(pu->chan->fd, &efds)) { - if (FD_ISSET(pu->chan->fd, &efds)) - pu->chan->exception = 1; - /* See if they need servicing */ - f = ast_read(pu->chan); - if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) { - /* There's a problem, hang them up*/ - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "%s got tired of being parked\n", pu->chan->name); - ast_hangup(pu->chan); - /* And take them out of the parking lot */ - if (pl) - pl->next = pu->next; - else - parkinglot = pu->next; - pt = pu; - pu = pu->next; - free(pt); - } else { - /* XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */ - ast_frfree(f); - goto std; /* XXX Ick: jumping into an else statement??? XXX */ + } else { + for (x=0;xchan->fds[x] > -1) && (FD_ISSET(pu->chan->fds[x], &rfds) || FD_ISSET(pu->chan->fds[x], &efds))) { + if (FD_ISSET(pu->chan->fds[x], &efds)) + pu->chan->exception = 1; + pu->chan->fdno = x; + /* See if they need servicing */ + f = ast_read(pu->chan); + if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) { + /* There's a problem, hang them up*/ + if (option_verbose > 1) + ast_verbose(VERBOSE_PREFIX_2 "%s got tired of being parked\n", pu->chan->name); + ast_hangup(pu->chan); + /* And take them out of the parking lot */ + if (pl) + pl->next = pu->next; + else + parkinglot = pu->next; + pt = pu; + pu = pu->next; + free(pt); + break; + } else { + /* XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */ + ast_frfree(f); + goto std; /* XXX Ick: jumping into an else statement??? XXX */ + } + } + } + if (x >= AST_MAX_FDS) { + /* Keep this one for next one */ +std: FD_SET(pu->chan->fds[x], &nrfds); + FD_SET(pu->chan->fds[x], &nefds); + /* Keep track of our longest wait */ + if ((tms < ms) || (ms < 0)) + ms = tms; + if (pu->chan->fds[x] > max) + max = pu->chan->fds[x]; + pl = pu; + pu = pu->next; } - } else { - /* Keep this one for next one */ -std: FD_SET(pu->chan->fd, &nrfds); - FD_SET(pu->chan->fd, &nefds); - /* Keep track of our longest wait */ - if ((tms < ms) || (ms < 0)) - ms = tms; - if (pu->chan->fd > max) - max = pu->chan->fd; - pl = pu; - pu = pu->next; } } - pthread_mutex_unlock(&parking_lock); + ast_pthread_mutex_unlock(&parking_lock); rfds = nrfds; efds = nefds; tv.tv_sec = ms / 1000; @@ -236,43 +265,46 @@ static void hanguptree(struct localuser *outgoing, struct ast_channel *exception } } +#define MAX 256 + static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localuser *outgoing, int *to, int *allowredir) { - fd_set rfds, efds; struct localuser *o; int found; int numlines; + int sentringing = 0; int numbusies = 0; int orig = *to; - struct timeval tv; struct ast_frame *f; struct ast_channel *peer = NULL; - /* Watch all outgoing channels looking for an answer of some sort. */ - tv.tv_sec = *to / 1000; - tv.tv_usec = (*to % 1000) * 1000; - while((tv.tv_sec || tv.tv_usec) && !peer) { - FD_ZERO(&rfds); - FD_ZERO(&efds); - /* Always watch the input fd */ - FD_SET(in->fd, &rfds); - FD_SET(in->fd, &efds); + struct ast_channel *watchers[MAX]; + int pos; + int single; + struct ast_channel *winner; + + single = (outgoing && !outgoing->next); + + if (single) { + /* If we are calling a single channel, make them compatible for in-band tone purpose */ + ast_channel_make_compatible(outgoing->chan, in); + } + + while(*to && !peer) { o = outgoing; found = -1; + pos = 1; numlines = 0; + watchers[0] = in; while(o) { + /* Keep track of important channels */ if (o->stillgoing) { - /* Pay attention to this one */ - CHECK_BLOCKING(o->chan); - FD_SET(o->chan->fd, &rfds); - FD_SET(o->chan->fd, &efds); - if (o->chan->fd > found) - found = o->chan->fd; + watchers[pos++] = o->chan; + found = 1; } - numlines++; o = o->next; + numlines++; } - /* If nobody is left, just go ahead and stop */ - if (found<0) { + if (found < 0) { if (numlines == numbusies) { if (option_verbose > 2) ast_verbose( VERBOSE_PREFIX_2 "Everyone is busy at this time\n"); @@ -283,76 +315,58 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localu if (option_verbose > 2) ast_verbose( VERBOSE_PREFIX_2 "No one is available to answer at this time\n"); } - break; - } - if (in->fd > found) - found = in->fd; - if (*to > -1) - found = select(found + 1, &rfds, NULL, &efds, &tv); - else - found = select(found + 1, &rfds, NULL, &efds, NULL); - if (found < 0) { - ast_log(LOG_WARNING, "select failed, returned %d (%s)\n", errno, strerror(errno)); - *to = -1; - o = outgoing; - while(o) { - if (o->stillgoing) { - o->chan->blocking = 0; - } - o = o->next; - } + *to = 0; return NULL; } + winner = ast_waitfor_n(watchers, pos, to); o = outgoing; while(o) { - if (o->stillgoing) { - o->chan->blocking = 0; - if (FD_ISSET(o->chan->fd, &rfds) || FD_ISSET(o->chan->fd, &efds)) { - if (FD_ISSET(o->chan->fd, &efds)) - o->chan->exception = 1; - f = ast_read(o->chan); - if (f) { - if (f->frametype == AST_FRAME_CONTROL) { - switch(f->subclass) { - case AST_CONTROL_ANSWER: - /* This is our guy if someone answered. */ - if (!peer) { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name); - peer = o->chan; - *allowredir = o->allowredirect; - } - break; - case AST_CONTROL_BUSY: + if (o->chan == winner) { + f = ast_read(winner); + if (f) { + if (f->frametype == AST_FRAME_CONTROL) { + switch(f->subclass) { + case AST_CONTROL_ANSWER: + /* This is our guy if someone answered. */ + if (!peer) { if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name); - o->stillgoing = 0; - numbusies++; - break; - case AST_CONTROL_RINGING: - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name); - break; - case AST_CONTROL_OFFHOOK: - /* Ignore going off hook */ - break; - default: - ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass); + ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name); + peer = o->chan; + *allowredir = o->allowredirect; } + break; + case AST_CONTROL_BUSY: + if (option_verbose > 2) + ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name); + o->stillgoing = 0; + numbusies++; + break; + case AST_CONTROL_RINGING: + if (option_verbose > 2) + ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name); + if (!sentringing) { + ast_indicate(in, AST_CONTROL_RINGING); + sentringing++; + } + break; + case AST_CONTROL_OFFHOOK: + /* Ignore going off hook */ + break; + default: + ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass); } - ast_frfree(f); - } else { - o->stillgoing = 0; + } else if (single && (f->frametype == AST_FRAME_VOICE)) { + if (ast_write(in, f)) + ast_log(LOG_WARNING, "Unable to forward frame\n"); } - + ast_frfree(f); + } else { + o->stillgoing = 0; } } o = o->next; } - if (FD_ISSET(in->fd, &rfds) || FD_ISSET(in->fd, &efds)) { - /* After unblocking the entirity of the list, check for the main channel */ - if (FD_ISSET(in->fd, &efds)) - in->exception = 1; + if (winner == in) { f = ast_read(in); #if 0 if (f && (f->frametype != AST_FRAME_VOICE)) @@ -364,12 +378,11 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localu return NULL; } } - + if (!*to && (option_verbose > 2)) + ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig); } - if (!(tv.tv_sec || tv.tv_usec) && (option_verbose > 2)) - ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig); - *to = 0; return peer; + } static int bridge_call(struct ast_channel *chan, struct ast_channel *peer, int allowredirect) @@ -381,6 +394,7 @@ static int bridge_call(struct ast_channel *chan, struct ast_channel *peer, int a struct ast_channel *who; char newext[256], *ptr; int res; + struct ast_option_header *aoh; /* Answer if need be */ if (chan->state != AST_STATE_UP) if (ast_answer(chan)) @@ -399,6 +413,22 @@ static int bridge_call(struct ast_channel *chan, struct ast_channel *peer, int a res = -1; break; } + if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_RINGING)) { + if (who == chan) + ast_indicate(peer, AST_CONTROL_RINGING); + else + ast_indicate(chan, AST_CONTROL_RINGING); + } + if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_OPTION)) { + aoh = f->data; + /* Forward option Requests */ + if (aoh && (aoh->flag == AST_OPTION_FLAG_REQUEST)) { + if (who == chan) + ast_channel_setoption(peer, ntohs(aoh->option), aoh->data, f->datalen - sizeof(struct ast_option_header), 0); + else + ast_channel_setoption(chan, ntohs(aoh->option), aoh->data, f->datalen - sizeof(struct ast_option_header), 0); + } + } if ((f->frametype == AST_FRAME_DTMF) && (who == peer) && allowredirect && (f->subclass == '#')) { memset(newext, 0, sizeof(newext)); @@ -438,14 +468,8 @@ static int bridge_call(struct ast_channel *chan, struct ast_channel *peer, int a strncpy(chan->context, peer->context, sizeof(chan->context)); chan->priority = 0; ast_frfree(f); - res=0; - break; - } else if (ast_exists_extension(chan, "default", newext, 1)) { - /* Set the channel's new extension, since it exists, using peer context */ - strncpy(chan->exten, newext, sizeof(chan->exten)); - strncpy(chan->context, "default", sizeof(chan->context)); - chan->priority = 0; - ast_frfree(f); + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Transferring %s to '%s' (context %s) priority 1\n", chan->name, chan->exten, chan->context); res=0; break; } @@ -477,7 +501,7 @@ static int park_exec(struct ast_channel *chan, void *data) } LOCAL_USER_ADD(u); park = atoi((char *)data); - pthread_mutex_lock(&parking_lock); + ast_pthread_mutex_lock(&parking_lock); pu = parkinglot; while(pu) { if (pu->parkingnum == park) { @@ -489,7 +513,7 @@ static int park_exec(struct ast_channel *chan, void *data) } pu = pu->next; } - pthread_mutex_unlock(&parking_lock); + ast_pthread_mutex_unlock(&parking_lock); if (pu) { peer = pu->chan; free(pu); @@ -531,6 +555,7 @@ static int dial_exec(struct ast_channel *chan, void *data) int allowredir=0; char numsubst[AST_MAX_EXTENSION]; char restofit[AST_MAX_EXTENSION]; + char *transfer = NULL; char *newnum; if (!data) { @@ -553,6 +578,11 @@ static int dial_exec(struct ast_channel *chan, void *data) if (timeout) { *timeout = '\0'; timeout++; + transfer = strchr(timeout, '|'); + if (transfer) { + *transfer = '\0'; + transfer++; + } } } else timeout = NULL; @@ -583,14 +613,15 @@ static int dial_exec(struct ast_channel *chan, void *data) ast_log(LOG_WARNING, "Out of memory\n"); goto out; } - tmp->allowredirect = 1; + if (transfer && (strchr(transfer, 't'))) + tmp->allowredirect = 1; + else + tmp->allowredirect = 0; strncpy(numsubst, number, sizeof(numsubst)); /* If we're dialing by extension, look at the extension to know what to dial */ if ((newnum = strstr(numsubst, "BYEXTENSION"))) { strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit)); snprintf(newnum, sizeof(numsubst) - (newnum - numsubst), "%s%s", chan->exten,restofit); - /* By default, if we're dialing by extension, don't permit redirecting */ - tmp->allowredirect = 0; if (option_debug) ast_log(LOG_DEBUG, "Dialing by extension %s\n", numsubst); } @@ -605,8 +636,12 @@ static int dial_exec(struct ast_channel *chan, void *data) } tmp->chan->appl = "AppDial"; tmp->chan->data = "(Outgoing Line)"; + if (tmp->chan->callerid) + free(tmp->chan->callerid); if (chan->callerid) tmp->chan->callerid = strdup(chan->callerid); + else + tmp->chan->callerid = NULL; /* Place the call, but don't wait on the answer */ res = ast_call(tmp->chan, numsubst, 0); if (res) { @@ -617,6 +652,7 @@ static int dial_exec(struct ast_channel *chan, void *data) ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", numsubst); ast_hangup(tmp->chan); free(tmp); + cur = rest; continue; } else if (option_verbose > 2) @@ -630,7 +666,7 @@ static int dial_exec(struct ast_channel *chan, void *data) cur = rest; } while(cur); - if (timeout) + if (timeout && strlen(timeout)) to = atoi(timeout) * 1000; else to = -1; @@ -682,17 +718,20 @@ int load_module(void) char exten[AST_MAX_EXTENSION]; con = ast_context_find(parking_con); if (!con) { - ast_log(LOG_ERROR, "Parking context '%s' does not exist\n", parking_con); - return -1; + con = ast_context_create(parking_con, registrar); + if (!con) { + ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con); + return -1; + } } for(x=parking_start; x<=parking_stop;x++) { snprintf(exten, sizeof(exten), "%d", x); - ast_add_extension2(con, 1, exten, 1, parkedcall, strdup(exten), free); + ast_add_extension2(con, 1, exten, 1, parkedcall, strdup(exten), free, registrar); } pthread_create(&parking_thread, NULL, do_parking_thread, NULL); - res = ast_register_application(parkedcall, park_exec); + res = ast_register_application(parkedcall, park_exec, synopsis, descrip); if (!res) - res = ast_register_application(app, dial_exec); + res = ast_register_application(app, dial_exec, synopsis, descrip); return res; }