Version 0.1.0 from FTP

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@84 65c4cc65-6c06-0410-ace0-fbb531ad65f3
1.0
Mark Spencer 26 years ago
parent 9d7168d34d
commit d5e50192a0

@ -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 <markster@linux-support.net>
*
* This program is free software, distributed under the terms of
* the GNU General Public License
*/
#include <asterisk/file.h>
#include <asterisk/logger.h>
#include <asterisk/channel.h>
#include <asterisk/pbx.h>
#include <asterisk/options.h>
#include <asterisk/module.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <pthread.h>
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 '#<extension' syntax. */
struct ast_channel *cs[3];
int to = -1, len;
struct ast_frame *f;
struct ast_channel *who;
char newext[256], *ptr;
int res;
/* Answer if need be */
if (chan->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;
}

@ -0,0 +1,402 @@
/*
* Asterisk -- A telephony toolkit for Linux.
*
* Channel Management
*
* Copyright (C) 1999, Adtran Inc. and Linux Support Services, LLC
*
* Mark Spencer <markster@linux-support.net>
*
* This program is free software, distributed under the terms of
* the GNU General Public License
*/
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
#include <asterisk/sched.h>
#include <asterisk/options.h>
#include <asterisk/channel.h>
#include <asterisk/channel_pvt.h>
#include <asterisk/logger.h>
#include <asterisk/file.h>
struct chanlist {
char type[80];
char description[80];
int capabilities;
struct ast_channel * (*requester)(char *type, int format, void *data);
struct chanlist *next;
} *backends = NULL;
/* Protect the channel list (highly unlikely that two things would change
it at the same time, but still! */
static pthread_mutex_t chlock = PTHREAD_MUTEX_INITIALIZER;
int ast_channel_register(char *type, char *description, int capabilities,
struct ast_channel *(*requester)(char *type, int format, void *data))
{
struct chanlist *chan, *last=NULL;
if (pthread_mutex_lock(&chlock)) {
ast_log(LOG_WARNING, "Unable to lock channel list\n");
return -1;
}
chan = backends;
while(chan) {
if (!strcasecmp(type, chan->type)) {
ast_log(LOG_WARNING, "Already have a handler for type '%s'\n", type);
pthread_mutex_unlock(&chlock);
return -1;
}
last = chan;
chan = chan->next;
}
chan = malloc(sizeof(struct chanlist));
if (!chan) {
ast_log(LOG_WARNING, "Out of memory\n");
pthread_mutex_unlock(&chlock);
return -1;
}
strncpy(chan->type, type, sizeof(chan->type));
strncpy(chan->description, description, sizeof(chan->description));
chan->capabilities = capabilities;
chan->requester = requester;
chan->next = NULL;
if (last)
last->next = chan;
else
backends = chan;
if (option_debug)
ast_log(LOG_DEBUG, "Registered handler for '%s' (%s)\n", chan->type, chan->description);
else if (option_verbose > 1)
ast_verbose( VERBOSE_PREFIX_2 "Registered channel type '%s' (%s)\n", chan->type, chan->description);
pthread_mutex_unlock(&chlock);
return 0;
}
struct ast_channel *ast_channel_alloc(void)
{
struct ast_channel *tmp;
struct ast_channel_pvt *pvt;
tmp = malloc(sizeof(struct ast_channel));
memset(tmp, 0, sizeof(struct ast_channel));
if (tmp) {
pvt = malloc(sizeof(struct ast_channel_pvt));
if (pvt) {
memset(pvt, 0, sizeof(struct ast_channel_pvt));
tmp->sched = sched_context_create();
if (tmp->sched) {
tmp->fd = -1;
strncpy(tmp->name, "**Unknown**", sizeof(tmp->name));
tmp->pvt = pvt;
tmp->state = AST_STATE_DOWN;
tmp->stack = -1;
tmp->streamid = -1;
strncpy(tmp->context, "default", sizeof(tmp->context));
strncpy(tmp->exten, "s", sizeof(tmp->exten));
tmp->priority=1;
} else {
ast_log(LOG_WARNING, "Unable to create schedule context\n");
free(tmp);
tmp = NULL;
}
} else {
ast_log(LOG_WARNING, "Out of memory\n");
free(tmp);
tmp = NULL;
}
} else
ast_log(LOG_WARNING, "Out of memory\n");
return tmp;
}
int ast_softhangup(struct ast_channel *chan)
{
int res = 0;
if (chan->stream)
ast_stopstream(chan);
if (option_debug)
ast_log(LOG_DEBUG, "Soft-Hanging up channel '%s'\n", chan->name);
if (chan->trans)
ast_log(LOG_WARNING, "Soft hangup called on '%s' while a translator is in place! Expect a failure.\n", chan->name);
if (chan->pvt->hangup)
res = chan->pvt->hangup(chan);
if (chan->pvt->pvt)
ast_log(LOG_WARNING, "Channel '%s' may not have been hung up properly\n", chan->name);
if (chan->pbx)
ast_log(LOG_WARNING, "PBX may not have been terminated properly on '%s'\n", chan->name);
/* Interrupt any select call or such */
if (chan->blocking)
pthread_kill(chan->blocker, SIGURG);
return res;
}
int ast_hangup(struct ast_channel *chan)
{
int res = 0;
if (chan->stream)
ast_stopstream(chan);
if (chan->sched)
sched_context_destroy(chan->sched);
if (chan->blocking)
ast_log(LOG_WARNING, "Hard hangup called, while fd is blocking! Expect a failure\n");
if (option_debug)
ast_log(LOG_DEBUG, "Hanging up channel '%s'\n", chan->name);
if (chan->pvt->hangup)
res = chan->pvt->hangup(chan);
if (chan->pvt->pvt)
ast_log(LOG_WARNING, "Channel '%s' may not have been hung up properly\n", chan->name);
if (chan->trans)
ast_log(LOG_WARNING, "Hard hangup called on '%s' while a translator is in place! Expect a failure.\n", chan->name);
if (chan->pbx)
ast_log(LOG_WARNING, "PBX may not have been terminated properly on '%s'\n", chan->name);
if (chan->dnid)
free(chan->dnid);
if (chan->callerid)
free(chan->callerid);
free(chan->pvt);
free(chan);
return res;
}
void ast_channel_unregister(char *type)
{
struct chanlist *chan, *last=NULL;
if (option_debug)
ast_log(LOG_DEBUG, "Unregistering channel type '%s'\n", type);
if (pthread_mutex_lock(&chlock)) {
ast_log(LOG_WARNING, "Unable to lock channel list\n");
return;
}
chan = backends;
while(chan) {
if (!strcasecmp(chan->type, type)) {
if (last)
last->next = chan->next;
else
backends = backends->next;
free(chan);
pthread_mutex_unlock(&chlock);
return;
}
last = chan;
chan = chan->next;
}
pthread_mutex_unlock(&chlock);
}
int ast_answer(struct ast_channel *chan)
{
/* Answer the line, if possible */
if (chan->state == AST_STATE_RING) {
if (chan->pvt->answer)
return chan->pvt->answer(chan);
}
return 0;
}
int ast_waitfor_n_fd(int *fds, int n, int *ms)
{
/* Wait for x amount of time on a file descriptor to have input. */
struct timeval tv;
fd_set rfds, efds;
int res;
int x, max=-1;
int winner = -1;
tv.tv_sec = *ms / 1000;
tv.tv_usec = (*ms % 1000) * 1000;
FD_ZERO(&rfds);
FD_ZERO(&efds);
for (x=0;x<n;x++) {
FD_SET(fds[x], &rfds);
FD_SET(fds[x], &efds);
if (fds[x] > max)
max = fds[x];
}
if (*ms >= 0)
res = select(max + 1, &rfds, NULL, &efds, &tv);
else
res = select(max + 1, &rfds, NULL, &efds, NULL);
for (x=0;x<n;x++) {
if ((FD_ISSET(fds[x], &rfds) || FD_ISSET(fds[x], &efds)) && (winner < 0))
winner = fds[x];
}
*ms = tv.tv_sec * 1000 + tv.tv_usec / 1000;
if (res < 0)
*ms = -10;
return winner;
}
struct ast_channel *ast_waitfor_n(struct ast_channel **c, int n, int *ms)
{
/* Wait for x amount of time on a file descriptor to have input. */
struct timeval tv;
fd_set rfds, efds;
int res;
int x, max=-1;
struct ast_channel *winner = NULL;
tv.tv_sec = *ms / 1000;
tv.tv_usec = (*ms % 1000) * 1000;
FD_ZERO(&rfds);
FD_ZERO(&efds);
for (x=0;x<n;x++) {
FD_SET(c[x]->fd, &rfds);
FD_SET(c[x]->fd, &efds);
CHECK_BLOCKING(c[x]);
if (c[x]->fd > max)
max = c[x]->fd;
}
if (*ms >= 0)
res = select(max + 1, &rfds, NULL, &efds, &tv);
else
res = select(max + 1, &rfds, NULL, &efds, NULL);
for (x=0;x<n;x++) {
c[x]->blocking = 0;
if ((FD_ISSET(c[x]->fd, &rfds) || FD_ISSET(c[x]->fd, &efds)) && !winner)
winner = c[x];
}
*ms = tv.tv_sec * 1000 + tv.tv_usec / 1000;
if (res < 0)
*ms = -10;
return winner;
}
int ast_waitfor(struct ast_channel *c, int ms)
{
if (ast_waitfor_n(&c, 1, &ms)) {
if (ms < 0)
return -ms;
return ms;
}
/* Error if ms < 0 */
if (ms < 0)
return -1;
return 0;
}
char ast_waitfordigit(struct ast_channel *c, int ms)
{
struct ast_frame *f;
char result = 0;
/* Wait for a digit, no more than ms milliseconds total. */
while(ms && !result) {
ms = ast_waitfor(c, ms);
if (ms < 0) /* Error */
result = -1;
else if (ms > 0) {
/* Read something */
f = ast_read(c);
if (f) {
if (f->frametype == AST_FRAME_DTMF)
result = f->subclass;
ast_frfree(f);
} else
result = -1;
}
}
return result;
}
struct ast_frame *ast_read(struct ast_channel *chan)
{
struct ast_frame *f = NULL;
chan->blocker = pthread_self();
if (chan->pvt->read)
f = chan->pvt->read(chan);
else
ast_log(LOG_WARNING, "No read routine on channel %s\n", chan);
return f;
}
int ast_write(struct ast_channel *chan, struct ast_frame *fr)
{
int res = -1;
CHECK_BLOCKING(chan);
switch(fr->frametype) {
case AST_FRAME_CONTROL:
/* XXX Interpret control frames XXX */
ast_log(LOG_WARNING, "Don't know how to handle control frames yet\n");
break;
case AST_FRAME_DTMF:
if (chan->pvt->send_digit)
res = chan->pvt->send_digit(chan, fr->subclass);
break;
default:
if (chan->pvt->write)
res = chan->pvt->write(chan, fr);
}
chan->blocking = 0;
return res;
}
struct ast_channel *ast_request(char *type, int format, void *data)
{
struct chanlist *chan;
struct ast_channel *c = NULL;
if (pthread_mutex_lock(&chlock)) {
ast_log(LOG_WARNING, "Unable to lock channel list\n");
return NULL;
}
chan = backends;
while(chan) {
if (!strcasecmp(type, chan->type)) {
if (chan->requester)
c = chan->requester(type, format, data);
pthread_mutex_unlock(&chlock);
break;
}
chan = chan->next;
}
if (!chan)
ast_log(LOG_WARNING, "No channel type registered for '%s'\n", type);
return c;
}
int ast_call(struct ast_channel *chan, char *addr, int timeout)
{
/* Place an outgoing call, but don't wait any longer than timeout ms before returning.
If the remote end does not answer within the timeout, then do NOT hang up, but
return anyway. */
int res = -1;
if (chan->pvt->call)
res = chan->pvt->call(chan, addr, timeout);
return res;
}
int ast_readstring(struct ast_channel *c, char *s, int len, int timeout, int ftimeout, char *enders)
{
int pos=0;
int to = ftimeout;
char d;
if (!len)
return -1;
do {
if (c->streamid > -1) {
d = ast_waitstream(c, AST_DIGIT_ANY);
ast_stopstream(c);
if (!d)
d = ast_waitfordigit(c, to);
} else {
d = ast_waitfordigit(c, to);
}
if (d < 0)
return -1;
if (!strchr(enders, d))
s[pos++] = d;
if ((d == 0) || strchr(enders, d) || (pos >= len - 1)) {
s[pos]='\0';
return 0;
}
to = timeout;
} while(1);
/* Never reached */
return 0;
}

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save