From d5e50192a0e723ae235dd92ef8621b1d990fb1a6 Mon Sep 17 00:00:00 2001 From: Mark Spencer Date: Sat, 4 Dec 1999 21:35:07 +0000 Subject: Version 0.1.0 from FTP git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@84 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- apps/app_dial.c | 410 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 410 insertions(+) create mode 100755 apps/app_dial.c (limited to 'apps') diff --git a/apps/app_dial.c b/apps/app_dial.c new file mode 100755 index 000000000..021f0d7a8 --- /dev/null +++ b/apps/app_dial.c @@ -0,0 +1,410 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Trivial application to dial a channel + * + * Copyright (C) 1999, Adtran Inc. and Linux Support Services, LLC + * + * Mark Spencer + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +static char *tdesc = "Trivial Dialing Application"; + +static char *app = "Dial"; + +/* We define a customer "local user" structure because we + use it not only for keeping track of what is in use but + also for keeping track of who we're dialing. */ + +struct localuser { + struct ast_channel *chan; + int stillgoing; + int allowredirect; + struct localuser *next; +}; + +LOCAL_USER_DECL; + +static void hanguptree(struct localuser *outgoing, struct ast_channel *exception) +{ + /* Hang up a tree of stuff */ + struct localuser *oo; + while(outgoing) { + /* Hangup any existing lines we have open */ + if (outgoing->chan != exception) + ast_hangup(outgoing->chan); + oo = outgoing; + outgoing=outgoing->next; + free(oo); + } +} + +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 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); + o = outgoing; + found = -1; + numlines = 0; + while(o) { + 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; + } + numlines++; + o = o->next; + } + /* If nobody is left, just go ahead and stop */ + if (found<0) { + if (numlines == numbusies) { + if (option_verbose > 2) + ast_verbose( VERBOSE_PREFIX_2 "Everyone is busy at this time\n"); + /* See if there is a special busy message */ + if (ast_exists_extension(in, in->context, in->exten, in->priority + 101)) + in->priority+=100; + } else { + 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; + } + return NULL; + } + o = outgoing; + while(o) { + if (o->stillgoing) { + o->chan->blocking = 0; + if (FD_ISSET(o->chan->fd, &rfds) || FD_ISSET(o->chan->fd, &efds)) { + 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 (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_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 */ + f = ast_read(in); +#if 0 + if (f && (f->frametype != AST_FRAME_VOICE)) + printf("Frame type: %d, %d\n", f->frametype, f->subclass); +#endif + if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass = AST_CONTROL_HANGUP))) { + /* Got hung up */ + *to=-1; + return NULL; + } + } + + } + 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) +{ + /* Copy voice back and forth between the two channels. Give the peer + the ability to transfer calls with '#state != AST_STATE_UP) + if (ast_answer(chan)) + return -1; + cs[0] = chan; + cs[1] = peer; + for (/* ever */;;) { + who = ast_waitfor_n(cs, 2, &to); + if (!who) { + ast_log(LOG_WARNING, "Nobody there??\n"); + continue; + } + f = ast_read(who); + if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) + return -1; + if ((f->frametype == AST_FRAME_VOICE) || + (f->frametype == AST_FRAME_DTMF)) { + if ((f->frametype == AST_FRAME_DTMF) && (who == peer) && allowredirect) { + if (f->subclass == '#') { + memset(newext, 0, sizeof(newext)); + ptr = newext; + len = ast_pbx_longest_extension(chan->context) + 1; + + /* Transfer */ + if ((res=ast_streamfile(peer, "pbx-transfer"))) + break; + if ((res=ast_waitstream(peer, AST_DIGIT_ANY)) < 0) + break; + ast_stopstream(peer); + if (res > 0) { + /* If they've typed a digit already, handle it */ + newext[0] = res; + ptr++; + len --; + } + res = ast_readstring(peer, ptr, len, 3000, 2000, "#"); + if (res) + break; + if (ast_exists_extension(chan, chan->context, newext, 1)) { + /* Set the channel's new extension, since it exists */ + strncpy(chan->exten, newext, sizeof(chan->exten)); + chan->priority = 0; + ast_frfree(f); + res=0; + break; + } + res = ast_streamfile(peer, "pbx-invalid"); + if (res) + break; + res = ast_waitstream(peer, AST_DIGIT_ANY); + ast_stopstream(peer); + res = 0; + } + } else { +#if 0 + ast_log(LOG_DEBUG, "Read from %s\n", who->name); +#endif + if (who == chan) + ast_write(peer, f); + else + ast_write(chan, f); + } + ast_frfree(f); + + } else + ast_frfree(f); + /* Swap who gets priority */ + cs[2] = cs[0]; + cs[0] = cs[1]; + cs[1] = cs[2]; + } + return res; +} + +static int dial_exec(struct ast_channel *chan, void *data) +{ + int res=-1; + struct localuser *u; + char *info, *peers, *timeout, *tech, *number, *rest, *cur; + struct localuser *outgoing=NULL, *tmp; + struct ast_channel *peer; + int to; + int allowredir=0; + + if (!data) { + ast_log(LOG_WARNING, "Dial requires an argument (technology1/number1&technology2/number2...|optional timeout)\n"); + return -1; + } + + LOCAL_USER_ADD(u); + + /* Parse our arguments */ + info = strdup((char *)data); + peers = strtok(info, "|"); + if (!peers) { + ast_log(LOG_WARNING, "Dial argument takes format (technology1/number1&technology2/number2...|optional timeout)\n"); + goto out; + } + timeout = strtok(NULL, "|"); + rest = peers; + do { + cur = strtok(rest, "&"); + /* Remember where to start next time */ + rest = strtok(NULL, "\128"); + /* Get a technology/number pair */ + tech = strtok(cur, "/"); + number = strtok(NULL, "&"); + if (!number) { + ast_log(LOG_WARNING, "Dial argument takes format (technology1/number1&technology2/number2...|optional timeout)\n"); + goto out; + } + tmp = malloc(sizeof(struct localuser)); + if (!tmp) { + ast_log(LOG_WARNING, "Out of memory\n"); + goto out; + } + tmp->allowredirect = 1; + /* If we're dialing by extension, look at the extension to know what to dial */ + if (!strcasecmp(number, "BYEXTENSION")) { + if (option_debug) + ast_log(LOG_DEBUG, "Dialing by extension %s\n", chan->exten); + number = chan->exten; + /* By default, if we're dialing by extension, don't permit redirecting */ + tmp->allowredirect = 0; + } + /* Request the peer */ + tmp->chan = ast_request(tech, chan->format, number); + if (!tmp->chan) { + /* If we can't, just go on to the next call */ + ast_log(LOG_WARNING, "Unable to create channel of type '%s'\n", tech); + free(tmp); + continue; + } + /* Place the call, but don't wait on the answer */ + res = ast_call(tmp->chan, number, 0); + if (res) { + /* Again, keep going even if there's an error */ + if (option_debug) + ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res); + else if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", number); + ast_hangup(tmp->chan); + free(tmp); + continue; + } else + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", number); + /* Put them in the list of outgoing thingies... We're ready now. + XXX If we're forcibly removed, these outgoing calls won't get + hung up XXX */ + tmp->stillgoing = -1; + tmp->next = outgoing; + outgoing = tmp; + } while(rest); + if (timeout) + to = atoi(timeout) * 1000; + else + to = -1; + peer = wait_for_answer(chan, outgoing, &to, &allowredir); + if (!peer) { + if (to) + /* Musta gotten hung up */ + res = -1; + else + /* Nobody answered, next please? */ + res=0; + + goto out; + } + if (peer) { + /* Ah ha! Someone answered within the desired timeframe. Of course after this + we will always return with -1 so that it is hung up properly after the + conversation. */ + hanguptree(outgoing, peer); + outgoing = NULL; + res = bridge_call(chan, peer, allowredir); + ast_hangup(peer); + } +out: + hanguptree(outgoing, NULL); + free(info); + LOCAL_USER_REMOVE(u); + return res; +} + +int unload_module(void) +{ + STANDARD_HANGUP_LOCALUSERS; + return ast_unregister_application(app); +} + +int load_module(void) +{ + return ast_register_application(app, dial_exec); +} + +char *description(void) +{ + return tdesc; +} + +int usecount(void) +{ + int res; + STANDARD_USECOUNT(res); + return res; +} -- cgit v1.2.3