From 9d7168d34d54c819569f107d3eb0883c19da2729 Mon Sep 17 00:00:00 2001 From: Mark Spencer <markster@digium.com> Date: Sat, 4 Dec 1999 20:45:45 +0000 Subject: [PATCH] Version 0.1.0 from FTP git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@83 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- codecs/g723_slin_ex.h | 15 + pbx.c | 858 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 873 insertions(+) create mode 100755 codecs/g723_slin_ex.h create mode 100755 pbx.c diff --git a/codecs/g723_slin_ex.h b/codecs/g723_slin_ex.h new file mode 100755 index 0000000000..df88b7b334 --- /dev/null +++ b/codecs/g723_slin_ex.h @@ -0,0 +1,15 @@ +/* + * 8-bit raw data + * + * Source: g723.example + * + * Copyright (C) 1999, Mark Spencer and Linux Support Services + * + * Distributed under the terms of the GNU General Public License + * + */ + +static unsigned char g723_slin_ex[] = { +0x4c, 0x34, 0xc2, 0xd9, 0x81, 0x80, 0xa8, 0x50, 0xd7, 0x8d, +0x8, 0x80, 0xf0, 0xb4, 0x40, 0x53, 0xe3, 0xe1, 0x63, 0x4e, +0x1a, 0x37, 0xd6, 0x0037 }; diff --git a/pbx.c b/pbx.c new file mode 100755 index 0000000000..a95d4151b1 --- /dev/null +++ b/pbx.c @@ -0,0 +1,858 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Core PBX routines. + * + * 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 <pthread.h> +#include <asterisk/pbx.h> +#include <asterisk/channel.h> +#include <asterisk/options.h> +#include <asterisk/logger.h> +#include <asterisk/file.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <setjmp.h> +#include <ctype.h> + + +/* + * I M P O R T A N T : + * + * The speed of extension handling will likely be among the most important + * aspects of this PBX. The switching scheme as it exists right now isn't + * terribly bad (it's O(N+M), where N is the # of extensions and M is the avg # + * of priorities, but a constant search time here would be great ;-) + * + */ + + +struct ast_context; + +struct ast_pbx { + int dtimeout; /* Timeout between digits (seconds) */ + int rtimeout; /* Timeout for response (seconds) */ +}; + +/* An extension */ +struct ast_exten { + char exten[AST_MAX_EXTENSION]; + int priority; + /* An extension */ + struct ast_context *parent; + /* Application to execute */ + char app[AST_MAX_EXTENSION]; + /* Data to use */ + void *data; + /* Data destructor */ + void (*datad)(void *); + /* Next highest priority with our extension */ + struct ast_exten *peer; + /* Extension with a greater ID */ + struct ast_exten *next; +}; + +/* An extension context */ +struct ast_context { + /* Name of the context */ + char name[AST_MAX_EXTENSION]; + /* A lock to prevent multiple threads from clobbering the context */ + pthread_mutex_t lock; + /* The root of the list of extensions */ + struct ast_exten *root; + /* Link them together */ + struct ast_context *next; +}; + + +/* An application */ +struct ast_app { + /* Name of the application */ + char name[AST_MAX_APP]; + int (*execute)(struct ast_channel *chan, void *data); + struct ast_app *next; +}; + +static int pbx_builtin_answer(struct ast_channel *, void *); +static int pbx_builtin_goto(struct ast_channel *, void *); +static int pbx_builtin_hangup(struct ast_channel *, void *); +static int pbx_builtin_background(struct ast_channel *, void *); +static int pbx_builtin_dtimeout(struct ast_channel *, void *); +static int pbx_builtin_rtimeout(struct ast_channel *, void *); +static int pbx_builtin_wait(struct ast_channel *, void *); + +static struct pbx_builtin { + char name[AST_MAX_APP]; + int (*execute)(struct ast_channel *chan, void *data); +} builtins[] = +{ + /* These applications are built into the PBX core and do not + need separate modules */ + { "Answer", pbx_builtin_answer }, + { "Goto", pbx_builtin_goto }, + { "Hangup", pbx_builtin_hangup }, + { "DigitTimeout", pbx_builtin_dtimeout }, + { "ResponseTimeout", pbx_builtin_rtimeout }, + { "BackGround", pbx_builtin_background }, + { "Wait", pbx_builtin_wait }, +}; + +/* Lock for the application list */ +static pthread_mutex_t applock = PTHREAD_MUTEX_INITIALIZER; +static struct ast_context *contexts = NULL; +/* Lock for the ast_context list */ +static pthread_mutex_t conlock = PTHREAD_MUTEX_INITIALIZER; +static struct ast_app *apps = NULL; + +static int pbx_exec(struct ast_channel *c, /* Channel */ + int (*execute)(struct ast_channel *chan, void *data), + void *data, /* Data for execution */ + int newstack) /* Force stack increment */ +{ + /* This function is special. It saves the stack so that no matter + how many times it is called, it returns to the same place */ + int res; + int stack = c->stack; + if (newstack && stack > AST_CHANNEL_MAX_STACK - 2) { + /* Don't allow us to go over the max number of stacks we + permit saving. */ + ast_log(LOG_WARNING, "Stack overflow, cannot create another stack\n"); + return -1; + } + if (newstack && (res = setjmp(c->jmp[++c->stack]))) { + /* Okay, here's where it gets weird. If newstack is non-zero, + then we increase the stack increment, but setjmp is not going + to return until longjmp is called -- when the application + exec'd is finished running. */ + if (res == 1) + res = 0; + if (c->stack != stack + 1) + ast_log(LOG_WARNING, "Stack returned to an unexpected place!\n"); + else if (c->app[c->stack]) + ast_log(LOG_WARNING, "Application may have forgotten to free its memory\n"); + c->stack = stack; + return res; + } else { + res = execute(c, data); + /* Any application that returns, we longjmp back, just in case. */ + if (c->stack != stack + 1) + ast_log(LOG_WARNING, "Stack is not at expected value\n"); + longjmp(c->jmp[stack+1], res); + /* Never returns */ + } +} + + +#define HELPER_EXISTS 0 +#define HELPER_SPAWN 1 +#define HELPER_EXEC 2 + +static struct ast_app *pbx_findapp(char *app) +{ + struct ast_app *tmp; + if (pthread_mutex_lock(&applock)) { + ast_log(LOG_WARNING, "Unable to obtain application lock\n"); + return NULL; + } + tmp = apps; + while(tmp) { + if (!strcasecmp(tmp->name, app)) + break; + tmp = tmp->next; + } + pthread_mutex_unlock(&applock); + return tmp; +} + +static void pbx_destroy(struct ast_pbx *p) +{ + free(p); +} + +int extension_match(char *pattern, char *data) +{ + int match; + /* If they're the same return */ + if (!strcasecmp(pattern, data)) + return 1; + /* All patterns begin with _ */ + if (pattern[0] != '_') + return 0; + /* Obviously must be the same length */ + if (strlen(pattern) != strlen(data) + 1) + return 0; + /* Start optimistic */ + match=1; + pattern++; + while(match && *data && *pattern) { + switch(toupper(*pattern)) { + case 'N': + if ((*data < '2') || (*data > '9')) + match=0; + break; + case 'X': + if ((*data < '0') || (*data > '9')) + match = 0; + break; + default: + if (*data != *pattern) + match =0; + } + data++; + pattern++; + } + return match; +} + +static int pbx_extension_helper(struct ast_channel *c, char *context, char *exten, int priority, int action) +{ + struct ast_context *tmp; + struct ast_exten *e; + struct ast_app *app; + int newstack = 0; + if (pthread_mutex_lock(&conlock)) { + ast_log(LOG_WARNING, "Unable to obtain lock\n"); + if (action == HELPER_EXISTS) + return 0; + else + return -1; + } + tmp = contexts; + while(tmp) { + if (!strcasecmp(tmp->name, context)) { + /* By locking tmp, not only can the state of its entries not + change, but it cannot be destroyed either. */ + pthread_mutex_lock(&tmp->lock); + /* But we can relieve the conlock, as tmp will not change */ + pthread_mutex_unlock(&conlock); + e = tmp->root; + while(e) { + if (extension_match(e->exten, exten)) { + while(e) { + if (e->priority == priority) { + pthread_mutex_unlock(&tmp->lock); + /* We have a winner! Maybe there are some races + in here though. XXX */ + switch(action) { + case HELPER_EXISTS: + return -1; + case HELPER_SPAWN: + newstack++; + /* Fall through */ + case HELPER_EXEC: + app = pbx_findapp(e->app); + if (app) { + strncpy(c->context, context, sizeof(c->context)); + strncpy(c->exten, exten, sizeof(c->exten)); + c->priority = priority; + if (option_debug) + ast_log(LOG_DEBUG, "Launching '%s'\n", app->name); + else if (option_verbose > 2) + ast_verbose( VERBOSE_PREFIX_3 "Executing %s(\"%s\", \"%s\") %s\n", + app->name, c->name, (e->data ? (char *)e->data : NULL), (newstack ? "in new stack" : "in same stack")); + return pbx_exec(c, app->execute, e->data, newstack); + } else { + ast_log(LOG_WARNING, "No application '%s' for extension (%s, %s, %d)\n", e->app, context, exten, priority); + return -1; + } + default: + ast_log(LOG_WARNING, "Huh (%d)?\n", action); + } + } + e = e->peer; + } + pthread_mutex_unlock(&tmp->lock); + if (action != HELPER_EXISTS) { + ast_log(LOG_WARNING, "No such priority '%d' in '%s' in '%s'\n", priority, exten, context); + return -1; + } else + return 0; + } + e = e->next; + } + pthread_mutex_unlock(&tmp->lock); + if (action != HELPER_EXISTS) { + ast_log(LOG_WARNING, "No such extension '%s' in '%s'\n", exten, context); + return -1; + } else + return 0; + } + tmp = tmp->next; + } + pthread_mutex_unlock(&conlock); + if (action != HELPER_EXISTS) { + ast_log(LOG_WARNING, "No such context '%s'\n", context); + return -1; + } else + return 0; +} +int ast_pbx_longest_extension(char *context) +{ + struct ast_context *tmp; + struct ast_exten *e; + int len = 0; + if (pthread_mutex_lock(&conlock)) { + ast_log(LOG_WARNING, "Unable to obtain lock\n"); + return -1; + } + tmp = contexts; + while(tmp) { + if (!strcasecmp(tmp->name, context)) { + /* By locking tmp, not only can the state of its entries not + change, but it cannot be destroyed either. */ + pthread_mutex_lock(&tmp->lock); + /* But we can relieve the conlock, as tmp will not change */ + pthread_mutex_unlock(&conlock); + e = tmp->root; + while(e) { + if (strlen(e->exten) > len) + len = strlen(e->exten); + e = e->next; + } + pthread_mutex_unlock(&tmp->lock); + return len; + } + tmp = tmp->next; + } + ast_log(LOG_WARNING, "No such context '%s'\n", context); + return -1; +} + +int ast_exists_extension(struct ast_channel *c, char *context, char *exten, int priority) +{ + return pbx_extension_helper(c, context, exten, priority, HELPER_EXISTS); +} + +int ast_spawn_extension(struct ast_channel *c, char *context, char *exten, int priority) +{ + return pbx_extension_helper(c, context, exten, priority, HELPER_SPAWN); +} + +static void *pbx_thread(void *data) +{ + /* Oh joyeous kernel, we're a new thread, with nothing to do but + answer this channel and get it going. The setjmp stuff is fairly + confusing, but necessary to get smooth transitions between + the execution of different applications (without the use of + additional threads) */ + struct ast_channel *c = data; + int firstpass = 1; + char digit; + char exten[256]; + int pos; + int waittime; + if (option_debug) + ast_log(LOG_DEBUG, "PBX_THREAD(%s)\n", c->name); + else if (option_verbose > 1) + ast_verbose( VERBOSE_PREFIX_2 "Accepting call on '%s'\n", c->name); + + + /* Start by trying whatever the channel is set to */ + if (!ast_exists_extension(c, c->context, c->exten, c->priority)) { + strncpy(c->context, "default", sizeof(c->context)); + strncpy(c->exten, "s", sizeof(c->exten)); + c->priority = 1; + } + for(;;) { + memset(exten, 0, sizeof(exten)); + pos = 0; + digit = 0; + while(ast_exists_extension(c, c->context, c->exten, c->priority)) { + if (ast_spawn_extension(c, c->context, c->exten, c->priority)) { + /* Something bad happened, or a hangup has been requested. */ + if (option_debug) + ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name); + else if (option_verbose > 1) + ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name); + goto out; + } + /* If we're playing something in the background, wait for it to finish or for a digit */ + if (c->stream) { + digit = ast_waitstream(c, AST_DIGIT_ANY); + ast_stopstream(c); + /* Hang up if something goes wrong */ + if (digit < 0) + goto out; + else if (digit) { + ast_stopstream(c); + exten[pos++] = digit; + break; + } + } + firstpass = 0; + c->priority++; + } + /* Done, wait for an extension */ + if (digit) + waittime = c->pbx->dtimeout; + else + waittime = c->pbx->rtimeout; + while(!ast_exists_extension(c, c->context, exten, 1) && ( + strlen(exten) < ast_pbx_longest_extension(c->context))) { + /* As long as we're willing to wait, and as long as it's not defined, + keep reading digits until we can't possibly get a right answer anymore. */ + digit = ast_waitfordigit(c, waittime * 1000); + if (!digit) + /* No entry */ + break; + if (digit < 0) + /* Error, maybe a hangup */ + goto out; + exten[pos++] = digit; + waittime = c->pbx->dtimeout; + } + if (ast_exists_extension(c, c->context, exten, 1)) { + /* Prepare the next cycle */ + strncpy(c->exten, exten, sizeof(c->exten)); + c->priority = 1; + } else { + /* No such extension */ + if (strlen(exten)) { + /* An invalid extension */ + if (ast_exists_extension(c, c->context, "i", 1)) { + if (option_verbose > 2) + ast_verbose( VERBOSE_PREFIX_3 "Invalid extension '%s' in context '%s' on %s\n", exten, c->context, c->name); + strncpy(c->exten, "i", sizeof(c->exten)); + c->priority = 1; + } else { + ast_log(LOG_WARNING, "Invalid extension, but no rule 'i' in context '%s'\n", c->context); + goto out; + } + } else { + /* A simple timeout */ + if (ast_exists_extension(c, c->context, "t", 1)) { + if (option_verbose > 2) + ast_verbose( VERBOSE_PREFIX_3 "Timeout on %s\n", c->name); + strncpy(c->exten, "t", sizeof(c->exten)); + c->priority = 1; + } else { + ast_log(LOG_WARNING, "Timeout, but no rule 't' in context '%s'\n", c->context); + goto out; + } + } + } + } + if (firstpass) + ast_log(LOG_WARNING, "Don't know what to do with '%s'\n", c->name); +out: + pbx_destroy(c->pbx); + c->pbx = NULL; + ast_hangup(c); + pthread_exit(NULL); + +} + +int ast_pbx_start(struct ast_channel *c) +{ + pthread_t t; + if (!c) { + ast_log(LOG_WARNING, "Asked to start thread on NULL channel?\n"); + return -1; + } + if (c->pbx) + ast_log(LOG_WARNING, "%s already has PBX structure??\n"); + c->pbx = malloc(sizeof(struct ast_pbx)); + if (!c->pbx) { + ast_log(LOG_WARNING, "Out of memory\n"); + return -1; + } + memset(c->pbx, 0, sizeof(struct ast_pbx)); + /* Start a new thread, and get something handling this channel. */ + if (pthread_create(&t, NULL, pbx_thread, c)) { + ast_log(LOG_WARNING, "Failed to create new channel thread\n"); + return -1; + } + return 0; +} +#if 0 +int ast_remove_extension(struct ast_context *con, char *extension, int priority) +{ + /* XXX Implement me XXX */ + return -1; +} +#endif +int ast_register_application(char *app, int (*execute)(struct ast_channel *, void *)) +{ + struct ast_app *tmp; + if (pthread_mutex_lock(&applock)) { + ast_log(LOG_ERROR, "Unable to lock application list\n"); + return -1; + } + tmp = apps; + while(tmp) { + if (!strcasecmp(app, tmp->name)) { + ast_log(LOG_WARNING, "Already have an application '%s'\n", app); + pthread_mutex_unlock(&applock); + return -1; + } + tmp = tmp->next; + } + tmp = malloc(sizeof(struct ast_app)); + if (tmp) { + strncpy(tmp->name, app, sizeof(tmp->name)); + tmp->execute = execute; + tmp->next = apps; + apps = tmp; + } else { + ast_log(LOG_WARNING, "Out of memory\n"); + pthread_mutex_unlock(&applock); + return -1; + } + if (option_verbose > 1) + ast_verbose( VERBOSE_PREFIX_2 "Registered application '%s'\n", tmp->name); + pthread_mutex_unlock(&applock); + return 0; +} + +int ast_unregister_application(char *app) { + struct ast_app *tmp, *tmpl = NULL; + if (pthread_mutex_lock(&applock)) { + ast_log(LOG_ERROR, "Unable to lock application list\n"); + return -1; + } + tmp = apps; + while(tmp) { + if (!strcasecmp(app, tmp->name)) { + if (tmpl) + tmpl->next = tmp->next; + else + apps = tmp->next; + if (option_verbose > 1) + ast_verbose( VERBOSE_PREFIX_2 "Unregistered application '%s'\n", tmp->name); + pthread_mutex_unlock(&applock); + return 0; + } + tmpl = tmp; + tmp = tmp->next; + } + pthread_mutex_unlock(&applock); + return -1; +} + +struct ast_context *ast_context_create(char *name) +{ + struct ast_context *tmp; + + pthread_mutex_lock(&conlock); + tmp = contexts; + while(tmp) { + if (!strcasecmp(tmp->name, name)) { + pthread_mutex_unlock(&conlock); + ast_log(LOG_WARNING, "Tried to register context '%s', already in use\n", name); + return NULL; + } + tmp = tmp->next; + } + tmp = malloc(sizeof(struct ast_context)); + if (tmp) { + pthread_mutex_init(&tmp->lock, NULL); + strncpy(tmp->name, name, sizeof(tmp->name)); + tmp->root = NULL; + tmp->next = contexts; + contexts = tmp; + if (option_debug) + ast_log(LOG_DEBUG, "Registered context '%s'\n", tmp->name); + else if (option_verbose > 2) + ast_verbose( VERBOSE_PREFIX_3 "Registered extension context '%s'\n", tmp->name); + } else + ast_log(LOG_WARNING, "Out of memory\n"); + + pthread_mutex_unlock(&conlock); + return tmp; +} + +int ast_add_extension2(struct ast_context *con, + int replace, char *extension, int priority, + char *application, void *data, void (*datad)(void *)) +{ + +#define LOG { if (option_debug) \ + ast_log(LOG_DEBUG, "Added extension '%s' priority %d to %s\n", tmp->exten, tmp->priority, con->name); \ + else if (option_verbose > 2) \ + ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d to %s\n", tmp->exten, tmp->priority, con->name); \ + } + + /* + * This is a fairly complex routine. Different extensions are kept + * in order by the extension number. Then, extensions of different + * priorities (same extension) are kept in a list, according to the + * peer pointer. + */ + struct ast_exten *tmp, *e, *el = NULL, *ep = NULL; + int res; + /* Be optimistic: Build the extension structure first */ + tmp = malloc(sizeof(struct ast_exten)); + if (tmp) { + strncpy(tmp->exten, extension, sizeof(tmp->exten)); + tmp->priority = priority; + strncpy(tmp->app, application, sizeof(tmp->app)); + tmp->data = data; + tmp->datad = datad; + tmp->peer = NULL; + tmp->next = NULL; + } else { + ast_log(LOG_WARNING, "Out of memory\n"); + return -1; + } + if (pthread_mutex_lock(&con->lock)) { + free(tmp); + /* And properly destroy the data */ + datad(data); + ast_log(LOG_WARNING, "Failed to lock context '%s'\n", con->name); + return -1; + } + e = con->root; + while(e) { + res= strcasecmp(e->exten, extension); + if (res == 0) { + /* We have an exact match, now we find where we are + and be sure there's no duplicates */ + while(e) { + if (e->priority == tmp->priority) { + /* Can't have something exactly the same. Is this a + replacement? If so, replace, otherwise, bonk. */ + if (replace) { + if (ep) { + /* We're in the peer list, insert ourselves */ + ep->peer = tmp; + tmp->peer = e->peer; + } else if (el) { + /* We're the first extension. Take over e's functions */ + el->next = tmp; + tmp->next = e->next; + tmp->peer = e->peer; + } else { + /* We're the very first extension. */ + con->root = tmp; + tmp->next = e->next; + tmp->peer = e->peer; + } + /* Destroy the old one */ + e->datad(e->data); + free(e); + pthread_mutex_unlock(&con->lock); + /* And immediately return success. */ + LOG; + return 0; + } else { + ast_log(LOG_WARNING, "Unable to register extension '%s', priority %d in '%s', already in use\n", tmp->exten, tmp->priority, con->name); + tmp->datad(tmp->data); + free(tmp); + pthread_mutex_unlock(&con->lock); + return -1; + } + } else if (e->priority > tmp->priority) { + /* Slip ourselves in just before e */ + if (ep) { + /* Easy enough, we're just in the peer list */ + ep->peer = tmp; + tmp->peer = e; + } else if (el) { + /* We're the first extension in this peer list */ + el->next = tmp; + tmp->next = e->next; + e->next = NULL; + tmp->peer = e; + } else { + /* We're the very first extension altogether */ + tmp->next = con->root; + /* Con->root must always exist or we couldn't get here */ + tmp->peer = con->root->peer; + con->root = tmp; + } + pthread_mutex_unlock(&con->lock); + /* And immediately return success. */ + LOG; + return 0; + } + ep = e; + e = e->peer; + } + /* If we make it here, then it's time for us to go at the very end. + ep *must* be defined or we couldn't have gotten here. */ + ep->peer = tmp; + pthread_mutex_unlock(&con->lock); + /* And immediately return success. */ + LOG; + return 0; + + } else if (res > 0) { + /* Insert ourselves just before 'e'. We're the first extension of + this kind */ + tmp->next = e; + if (el) { + /* We're in the list somewhere */ + el->next = tmp; + } else { + /* We're at the top of the list */ + con->root = tmp; + } + pthread_mutex_unlock(&con->lock); + /* And immediately return success. */ + LOG; + return 0; + } + + el = e; + e = e->next; + } + /* If we fall all the way through to here, then we need to be on the end. */ + if (el) + el->next = tmp; + else + con->root = tmp; + pthread_mutex_unlock(&con->lock); + LOG; + return 0; +} + +void ast_context_destroy(struct ast_context *con) +{ + struct ast_context *tmp, *tmpl=NULL; + pthread_mutex_lock(&conlock); + tmp = contexts; + while(tmp) { + if (tmp == con) { + /* Okay, let's lock the structure to be sure nobody else + is searching through it. */ + if (pthread_mutex_lock(&tmp->lock)) { + ast_log(LOG_WARNING, "Unable to lock context lock\n"); + return; + } + if (tmpl) + tmpl->next = tmp->next; + else + contexts = tmp->next; + /* Okay, now we're safe to let it go -- in a sense, we were + ready to let it go as soon as we locked it. */ + pthread_mutex_unlock(&tmp->lock); + free(tmp); + pthread_mutex_unlock(&conlock); + return; + } + tmpl = tmp; + tmp = tmp->next; + } + pthread_mutex_unlock(&conlock); +} + +int pbx_builtin_answer(struct ast_channel *chan, void *data) +{ + if (chan->state != AST_STATE_RING) { + ast_log(LOG_WARNING, "Ignoring answer request since line is not ringing\n"); + return 0; + } else + return ast_answer(chan); +} + +int pbx_builtin_hangup(struct ast_channel *chan, void *data) +{ + /* Just return non-zero and it will hang up */ + return -1; +} + +int pbx_builtin_wait(struct ast_channel *chan, void *data) +{ + /* Wait for "n" seconds */ + if (data && atoi((char *)data)) + sleep(atoi((char *)data)); + return 0; +} + +int pbx_builtin_background(struct ast_channel *chan, void *data) +{ + int res; + /* Stop anything playing */ + ast_stopstream(chan); + /* Stream a file */ + res = ast_streamfile(chan, (char *)data); + return res; +} + +int pbx_builtin_rtimeout(struct ast_channel *chan, void *data) +{ + /* Set the timeout for how long to wait between digits */ + chan->pbx->rtimeout = atoi((char *)data); + if (option_verbose > 2) + ast_verbose( VERBOSE_PREFIX_3 "Set Response Timeout to %d\n", chan->pbx->rtimeout); + return 0; +} + +int pbx_builtin_dtimeout(struct ast_channel *chan, void *data) +{ + /* Set the timeout for how long to wait between digits */ + chan->pbx->dtimeout = atoi((char *)data); + if (option_verbose > 2) + ast_verbose( VERBOSE_PREFIX_3 "Set Digit Timeout to %d\n", chan->pbx->dtimeout); + return 0; +} + +int pbx_builtin_goto(struct ast_channel *chan, void *data) +{ + char *s; + char *exten, *pri, *context; + if (!data) { + ast_log(LOG_WARNING, "Goto requires an argument (optional context|optional extension|priority)\n"); + return -1; + } + s = strdup((void *) data); + context = strtok(s, "|"); + exten = strtok(NULL, "|"); + if (!exten) { + /* Only a priority in this one */ + pri = context; + exten = NULL; + context = NULL; + } else { + pri = strtok(NULL, "|"); + if (!pri) { + /* Only an extension and priority in this one */ + pri = exten; + exten = context; + context = NULL; + } + } + if (atoi(pri) < 0) { + ast_log(LOG_WARNING, "Priority '%s' must be a number > 0\n", pri); + free(s); + return -1; + } + /* At this point we have a priority and maybe an extension and a context */ + chan->priority = atoi(pri) - 1; + if (exten) + strncpy(chan->exten, exten, sizeof(chan->exten)); + if (context) + strncpy(chan->context, context, sizeof(chan->context)); + if (option_verbose > 2) + ast_verbose( VERBOSE_PREFIX_3 "Goto (%s,%s,%d)\n", chan->context,chan->exten, chan->priority+1); + return 0; +} + +int load_pbx(void) +{ + int x; + /* Initialize the PBX */ + if (option_verbose) { + ast_verbose( "Asterisk PBX Core Initializing\n"); + ast_verbose( "Registering builtin applications:\n"); + } + for (x=0;x<sizeof(builtins) / sizeof(struct pbx_builtin); x++) { + if (option_verbose) + ast_verbose( VERBOSE_PREFIX_1 "[%s]\n", builtins[x].name); + if (ast_register_application(builtins[x].name, builtins[x].execute)) { + ast_log(LOG_ERROR, "Unable to register builtin application '%s'\n", builtins[x].name); + return -1; + } + } + return 0; +} + -- GitLab