aboutsummaryrefslogtreecommitdiffstats
path: root/1.2-netsec/pbx.c
diff options
context:
space:
mode:
Diffstat (limited to '1.2-netsec/pbx.c')
-rw-r--r--1.2-netsec/pbx.c6487
1 files changed, 6487 insertions, 0 deletions
diff --git a/1.2-netsec/pbx.c b/1.2-netsec/pbx.c
new file mode 100644
index 000000000..f7d26c92a
--- /dev/null
+++ b/1.2-netsec/pbx.c
@@ -0,0 +1,6487 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Core PBX routines.
+ *
+ */
+
+#include <sys/types.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/lock.h"
+#include "asterisk/cli.h"
+#include "asterisk/pbx.h"
+#include "asterisk/channel.h"
+#include "asterisk/options.h"
+#include "asterisk/logger.h"
+#include "asterisk/file.h"
+#include "asterisk/callerid.h"
+#include "asterisk/cdr.h"
+#include "asterisk/config.h"
+#include "asterisk/term.h"
+#include "asterisk/manager.h"
+#include "asterisk/ast_expr.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/say.h"
+#include "asterisk/utils.h"
+#include "asterisk/causes.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/app.h"
+#include "asterisk/devicestate.h"
+#include "asterisk/compat.h"
+
+/*!
+ * \note 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 ;-)
+ *
+ */
+
+#ifdef LOW_MEMORY
+#define EXT_DATA_SIZE 256
+#else
+#define EXT_DATA_SIZE 8192
+#endif
+
+#define SWITCH_DATA_LENGTH 256
+
+#define VAR_BUF_SIZE 4096
+
+#define VAR_NORMAL 1
+#define VAR_SOFTTRAN 2
+#define VAR_HARDTRAN 3
+
+#define BACKGROUND_SKIP (1 << 0)
+#define BACKGROUND_NOANSWER (1 << 1)
+#define BACKGROUND_MATCHEXTEN (1 << 2)
+#define BACKGROUND_PLAYBACK (1 << 3)
+
+AST_APP_OPTIONS(background_opts, {
+ AST_APP_OPTION('s', BACKGROUND_SKIP),
+ AST_APP_OPTION('n', BACKGROUND_NOANSWER),
+ AST_APP_OPTION('m', BACKGROUND_MATCHEXTEN),
+ AST_APP_OPTION('p', BACKGROUND_PLAYBACK),
+});
+
+#define WAITEXTEN_MOH (1 << 0)
+
+AST_APP_OPTIONS(waitexten_opts, {
+ AST_APP_OPTION_ARG('m', WAITEXTEN_MOH, 1),
+});
+
+struct ast_context;
+
+/*!\brief ast_exten: An extension
+ The dialplan is saved as a linked list with each context
+ having it's own linked list of extensions - one item per
+ priority.
+*/
+struct ast_exten {
+ char *exten; /* Extension name */
+ int matchcid; /* Match caller id ? */
+ char *cidmatch; /* Caller id to match for this extension */
+ int priority; /* Priority */
+ char *label; /* Label */
+ struct ast_context *parent; /* The context this extension belongs to */
+ char *app; /* Application to execute */
+ void *data; /* Data to use (arguments) */
+ void (*datad)(void *); /* Data destructor */
+ struct ast_exten *peer; /* Next higher priority with our extension */
+ const char *registrar; /* Registrar */
+ struct ast_exten *next; /* Extension with a greater ID */
+ char stuff[0];
+};
+
+/*! \brief ast_include: include= support in extensions.conf */
+struct ast_include {
+ char *name;
+ char *rname; /* Context to include */
+ const char *registrar; /* Registrar */
+ int hastime; /* If time construct exists */
+ struct ast_timing timing; /* time construct */
+ struct ast_include *next; /* Link them together */
+ char stuff[0];
+};
+
+/*! \brief ast_sw: Switch statement in extensions.conf */
+struct ast_sw {
+ char *name;
+ const char *registrar; /* Registrar */
+ char *data; /* Data load */
+ int eval;
+ struct ast_sw *next; /* Link them together */
+ char *tmpdata;
+ char stuff[0];
+};
+
+/*! \brief ast_ignorepat: Ignore patterns in dial plan */
+struct ast_ignorepat {
+ const char *registrar;
+ struct ast_ignorepat *next;
+ char pattern[0];
+};
+
+/*! \brief ast_context: An extension context */
+struct ast_context {
+ ast_mutex_t lock; /*!< A lock to prevent multiple threads from clobbering the context */
+ struct ast_exten *root; /*!< The root of the list of extensions */
+ struct ast_context *next; /*!< Link them together */
+ struct ast_include *includes; /*!< Include other contexts */
+ struct ast_ignorepat *ignorepats; /*!< Patterns for which to continue playing dialtone */
+ const char *registrar; /*!< Registrar */
+ struct ast_sw *alts; /*!< Alternative switches */
+ char name[0]; /*!< Name of the context */
+};
+
+
+/*! \brief ast_app: A registered application */
+struct ast_app {
+ int (*execute)(struct ast_channel *chan, void *data);
+ const char *synopsis; /* Synopsis text for 'show applications' */
+ const char *description; /* Description (help text) for 'show application <name>' */
+ struct ast_app *next; /* Next app in list */
+ char name[0]; /* Name of the application */
+};
+
+/*! \brief ast_state_cb: An extension state notify register item */
+struct ast_state_cb {
+ int id;
+ void *data;
+ ast_state_cb_type callback;
+ struct ast_state_cb *next;
+};
+
+/*! \brief Structure for dial plan hints
+
+ Hints are pointers from an extension in the dialplan to one or
+ more devices (tech/name) */
+struct ast_hint {
+ struct ast_exten *exten; /*!< Extension */
+ int laststate; /*!< Last known state */
+ struct ast_state_cb *callbacks; /*!< Callback list for this extension */
+ struct ast_hint *next; /*!< Pointer to next hint in list */
+};
+
+int ast_pbx_outgoing_cdr_failed(void);
+
+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_atimeout(struct ast_channel *, void *);
+static int pbx_builtin_wait(struct ast_channel *, void *);
+static int pbx_builtin_waitexten(struct ast_channel *, void *);
+static int pbx_builtin_setlanguage(struct ast_channel *, void *);
+static int pbx_builtin_resetcdr(struct ast_channel *, void *);
+static int pbx_builtin_setaccount(struct ast_channel *, void *);
+static int pbx_builtin_setamaflags(struct ast_channel *, void *);
+static int pbx_builtin_ringing(struct ast_channel *, void *);
+static int pbx_builtin_progress(struct ast_channel *, void *);
+static int pbx_builtin_congestion(struct ast_channel *, void *);
+static int pbx_builtin_busy(struct ast_channel *, void *);
+static int pbx_builtin_setglobalvar(struct ast_channel *, void *);
+static int pbx_builtin_noop(struct ast_channel *, void *);
+static int pbx_builtin_gotoif(struct ast_channel *, void *);
+static int pbx_builtin_gotoiftime(struct ast_channel *, void *);
+static int pbx_builtin_execiftime(struct ast_channel *, void *);
+static int pbx_builtin_saynumber(struct ast_channel *, void *);
+static int pbx_builtin_saydigits(struct ast_channel *, void *);
+static int pbx_builtin_saycharacters(struct ast_channel *, void *);
+static int pbx_builtin_sayphonetic(struct ast_channel *, void *);
+static int pbx_builtin_setvar_old(struct ast_channel *, void *);
+int pbx_builtin_setvar(struct ast_channel *, void *);
+static int pbx_builtin_importvar(struct ast_channel *, void *);
+
+static struct varshead globals;
+
+static int autofallthrough = 0;
+
+AST_MUTEX_DEFINE_STATIC(maxcalllock);
+static int countcalls = 0;
+
+AST_MUTEX_DEFINE_STATIC(acflock); /*!< Lock for the custom function list */
+static struct ast_custom_function *acf_root = NULL;
+
+/*! \brief Declaration of builtin applications */
+static struct pbx_builtin {
+ char name[AST_MAX_APP];
+ int (*execute)(struct ast_channel *chan, void *data);
+ char *synopsis;
+ char *description;
+} builtins[] =
+{
+ /* These applications are built into the PBX core and do not
+ need separate modules */
+
+ { "AbsoluteTimeout", pbx_builtin_atimeout,
+ "Set absolute maximum time of call",
+ " AbsoluteTimeout(seconds): This application will set the absolute maximum\n"
+ "amount of time permitted for a call. A setting of 0 disables the timeout.\n"
+ " AbsoluteTimeout has been deprecated in favor of Set(TIMEOUT(absolute)=timeout)\n"
+ },
+
+ { "Answer", pbx_builtin_answer,
+ "Answer a channel if ringing",
+ " Answer([delay]): If the call has not been answered, this application will\n"
+ "answer it. Otherwise, it has no effect on the call. If a delay is specified,\n"
+ "Asterisk will wait this number of milliseconds before answering the call.\n"
+ },
+
+ { "BackGround", pbx_builtin_background,
+ "Play a file while awaiting extension",
+ " Background(filename1[&filename2...][|options[|langoverride][|context]]):\n"
+ "This application will play the given list of files while waiting for an\n"
+ "extension to be dialed by the calling channel. To continue waiting for digits\n"
+ "after this application has finished playing files, the WaitExten application\n"
+ "should be used. The 'langoverride' option explicity specifies which language\n"
+ "to attempt to use for the requested sound files. If a 'context' is specified,\n"
+ "this is the dialplan context that this application will use when exiting to a\n"
+ "dialed extension."
+ " If one of the requested sound files does not exist, call processing will be\n"
+ "terminated.\n"
+ " Options:\n"
+ " s - causes the playback of the message to be skipped\n"
+ " if the channel is not in the 'up' state (i.e. it\n"
+ " hasn't been answered yet.) If this happens, the\n"
+ " application will return immediately.\n"
+ " n - don't answer the channel before playing the files\n"
+ " m - only break if a digit hit matches a one digit\n"
+ " extension in the destination context\n"
+ },
+
+ { "Busy", pbx_builtin_busy,
+ "Indicate the Busy condition",
+ " Busy([timeout]): This application will indicate the busy condition to\n"
+ "the calling channel. If the optional timeout is specified, the calling channel\n"
+ "will be hung up after the specified number of seconds. Otherwise, this\n"
+ "application will wait until the calling channel hangs up.\n"
+ },
+
+ { "Congestion", pbx_builtin_congestion,
+ "Indicate the Congestion condition",
+ " Congestion([timeout]): This application will indicate the congenstion\n"
+ "condition to the calling channel. If the optional timeout is specified, the\n"
+ "calling channel will be hung up after the specified number of seconds.\n"
+ "Otherwise, this application will wait until the calling channel hangs up.\n"
+ },
+
+ { "DigitTimeout", pbx_builtin_dtimeout,
+ "Set maximum timeout between digits",
+ " DigitTimeout(seconds): Set the maximum amount of time permitted between\n"
+ "digits when the user is typing in an extension. When this timeout expires,\n"
+ "after the user has started to type in an extension, the extension will be\n"
+ "considered complete, and will be interpreted. Note that if an extension\n"
+ "typed in is valid, it will not have to timeout to be tested, so typically\n"
+ "at the expiry of this timeout, the extension will be considered invalid\n"
+ "(and thus control would be passed to the 'i' extension, or if it doesn't\n"
+ "exist the call would be terminated). The default timeout is 5 seconds.\n"
+ " DigitTimeout has been deprecated in favor of Set(TIMEOUT(digit)=timeout)\n"
+ },
+
+ { "Goto", pbx_builtin_goto,
+ "Jump to a particular priority, extension, or context",
+ " Goto([[context|]extension|]priority): This application will cause the\n"
+ "calling channel to continue dialplan execution at the specified priority.\n"
+ "If no specific extension, or extension and context, are specified, then this\n"
+ "application will jump to the specified priority of the current extension.\n"
+ " If the attempt to jump to another location in the dialplan is not successful,\n"
+ "then the channel will continue at the next priority of the current extension.\n"
+ },
+
+ { "GotoIf", pbx_builtin_gotoif,
+ "Conditional goto",
+ " GotoIf(Condition?[label1]:[label2]): This application will cause the calling\n"
+ "channel to jump to the speicifed location in the dialplan based on the\n"
+ "evaluation of the given condition. The channel will continue at 'label1' if the\n"
+ "condition is true, or 'label2' if the condition is false. The labels are\n"
+ "specified in the same syntax that is used with the Goto application.\n"
+ },
+
+ { "GotoIfTime", pbx_builtin_gotoiftime,
+ "Conditional Goto based on the current time",
+ " GotoIfTime(<times>|<weekdays>|<mdays>|<months>?[[context|]exten|]priority):\n"
+ "This application will have the calling channel jump to the speicified location\n"
+ "int the dialplan if the current time matches the given time specification.\n"
+ "Further information on the time specification can be found in examples\n"
+ "illustrating how to do time-based context includes in the dialplan.\n"
+ },
+
+ { "ExecIfTime", pbx_builtin_execiftime,
+ "Conditional application execution based on the current time",
+ " ExecIfTime(<times>|<weekdays>|<mdays>|<months>?appname[|appargs]):\n"
+ "This application will execute the specified dialplan application, with optional\n"
+ "arguments, if the current time matches the given time specification. Further\n"
+ "information on the time speicification can be found in examples illustrating\n"
+ "how to do time-based context includes in the dialplan.\n"
+ },
+
+ { "Hangup", pbx_builtin_hangup,
+ "Hang up the calling channel",
+ " Hangup(): This application will hang up the calling channel.\n"
+ },
+
+ { "NoOp", pbx_builtin_noop,
+ "Do Nothing",
+ " NoOp(): This applicatiion does nothing. However, it is useful for debugging\n"
+ "purposes. Any text that is provided as arguments to this application can be\n"
+ "viewed at the Asterisk CLI. This method can be used to see the evaluations of\n"
+ "variables or functions without having any effect."
+ },
+
+ { "Progress", pbx_builtin_progress,
+ "Indicate progress",
+ " Progress(): This application will request that in-band progress information\n"
+ "be provided to the calling channel.\n"
+ },
+
+ { "ResetCDR", pbx_builtin_resetcdr,
+ "Resets the Call Data Record",
+ " ResetCDR([options]): This application causes the Call Data Record to be\n"
+ "reset.\n"
+ " Options:\n"
+ " w -- Store the current CDR record before resetting it.\n"
+ " a -- Store any stacked records.\n"
+ " v -- Save CDR variables.\n"
+ },
+
+ { "ResponseTimeout", pbx_builtin_rtimeout,
+ "Set maximum timeout awaiting response",
+ " ResponseTimeout(seconds): This will set the maximum amount of time permitted\n"
+ "to wait for an extension to dialed (see the WaitExten application), before the\n"
+ "timeout occurs. If this timeout is reached, dialplan execution will continue at\n"
+ "the 't' extension, if it exists.\n"
+ " ResponseTimeout has been deprecated in favor of Set(TIMEOUT(response)=timeout)\n"
+ },
+
+ { "Ringing", pbx_builtin_ringing,
+ "Indicate ringing tone",
+ " Ringing(): This application will request that the channel indicate a ringing\n"
+ "tone to the user.\n"
+ },
+
+ { "SayNumber", pbx_builtin_saynumber,
+ "Say Number",
+ " SayNumber(digits[,gender]): This application will play the sounds that\n"
+ "correspond to the given number. Optionally, a gender may be specified.\n"
+ "This will use the language that is currently set for the channel. See the\n"
+ "LANGUAGE function for more information on setting the language for the channel.\n"
+ },
+
+ { "SayDigits", pbx_builtin_saydigits,
+ "Say Digits",
+ " SayDigits(digits): This application will play the sounds that correspond\n"
+ "to the digits of the given number. This will use the language that is currently\n"
+ "set for the channel. See the LANGUAGE function for more information on setting\n"
+ "the language for the channel.\n"
+ },
+
+ { "SayAlpha", pbx_builtin_saycharacters,
+ "Say Alpha",
+ " SayAlpha(string): This application will play the sounds that correspond to\n"
+ "the letters of the given string.\n"
+ },
+
+ { "SayPhonetic", pbx_builtin_sayphonetic,
+ "Say Phonetic",
+ " SayPhonetic(string): This application will play the sounds from the phonetic\n"
+ "alphabet that correspond to the letters in the given string.\n"
+ },
+
+ { "SetAccount", pbx_builtin_setaccount,
+ "Set the CDR Account Code",
+ " SetAccount([account]): This application will set the channel account code for\n"
+ "billing purposes.\n"
+ " SetAccount has been deprecated in favor of the Set(CDR(accountcode)=account).\n"
+ },
+
+ { "SetAMAFlags", pbx_builtin_setamaflags,
+ "Set the AMA Flags",
+ " SetAMAFlags([flag]): This channel will set the channel's AMA Flags for billing\n"
+ "purposes.\n"
+ },
+
+ { "SetGlobalVar", pbx_builtin_setglobalvar,
+ "Set a global variable to a given value",
+ " SetGlobalVar(variable=value): This application sets a given global variable to\n"
+ "the specified value.\n"
+ },
+
+ { "SetLanguage", pbx_builtin_setlanguage,
+ "Set the channel's preferred language",
+ " SetLanguage(language): This will set the channel language to the given value.\n"
+ "This information is used for the syntax in generation of numbers, and to choose\n"
+ "a sound file in the given language, when it is available.\n"
+ " For example, if language is set to 'fr' and the file 'demo-congrats' is \n"
+ "requested to be played, if the file 'fr/demo-congrats' exists, then\n"
+ "it will play that file. If not, it will play the normal 'demo-congrats'.\n"
+ "For some language codes, SetLanguage also changes the syntax of some\n"
+ "Asterisk functions, like SayNumber.\n"
+ " SetLanguage has been deprecated in favor of Set(LANGUAGE()=language)\n"
+ },
+
+ { "Set", pbx_builtin_setvar,
+ "Set channel variable(s) or function value(s)",
+ " Set(name1=value1|name2=value2|..[|options])\n"
+ "This function can be used to set the value of channel variables or dialplan\n"
+ "functions. It will accept up to 24 name/value pairs. When setting variables,\n"
+ "if the variable name is prefixed with _, the variable will be inherited into\n"
+ "channels created from the current channel. If the variable name is prefixed\n"
+ "with __, the variable will be inherited into channels created from the current\n"
+ "channel and all children channels.\n"
+ " Options:\n"
+ " g - Set variable globally instead of on the channel\n"
+ " (applies only to variables, not functions)\n"
+ },
+
+ { "SetVar", pbx_builtin_setvar_old,
+ "Set channel variable(s)",
+ " SetVar(name1=value1|name2=value2|..[|options]): This application has been\n"
+ "deprecated in favor of using the Set application.\n"
+ },
+
+ { "ImportVar", pbx_builtin_importvar,
+ "Import a variable from a channel into a new variable",
+ " ImportVar(newvar=channelname|variable): This application imports a variable\n"
+ "from the specified channel (as opposed to the current one) and stores it as\n"
+ "a variable in the current channel (the channel that is calling this\n"
+ "application). Variables created by this application have the same inheritance\n"
+ "properties as those created with the Set application. See the documentation for\n"
+ "Set for more information.\n"
+ },
+
+ { "Wait", pbx_builtin_wait,
+ "Waits for some time",
+ " Wait(seconds): This application waits for a specified number of seconds.\n"
+ "Then, dialplan execution will continue at the next priority.\n"
+ " Note that the seconds can be passed with fractions of a second. For example,\n"
+ "'1.5' will ask the application to wait for 1.5 seconds.\n"
+ },
+
+ { "WaitExten", pbx_builtin_waitexten,
+ "Waits for an extension to be entered",
+ " WaitExten([seconds][|options]): This application waits for the user to enter\n"
+ "a new extension for a specified number of seconds.\n"
+ " Note that the seconds can be passed with fractions of a second. For example,\n"
+ "'1.5' will ask the application to wait for 1.5 seconds.\n"
+ " Options:\n"
+ " m[(x)] - Provide music on hold to the caller while waiting for an extension.\n"
+ " Optionally, specify the class for music on hold within parenthesis.\n"
+ },
+
+};
+
+static struct ast_context *contexts = NULL;
+AST_MUTEX_DEFINE_STATIC(conlock); /* Lock for the ast_context list */
+static struct ast_app *apps = NULL;
+AST_MUTEX_DEFINE_STATIC(applock); /* Lock for the application list */
+
+struct ast_switch *switches = NULL;
+AST_MUTEX_DEFINE_STATIC(switchlock); /* Lock for switches */
+
+AST_MUTEX_DEFINE_STATIC(hintlock); /* Lock for extension state notifys */
+static int stateid = 1;
+struct ast_hint *hints = NULL;
+struct ast_state_cb *statecbs = NULL;
+
+/*
+ \note 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 pbx_exec(struct ast_channel *c, /*!< Channel */
+ struct ast_app *app, /*!< Application */
+ void *data, /*!< Data for execution */
+ int newstack) /*!< Force stack increment */
+{
+ int res;
+
+ char *saved_c_appl;
+ char *saved_c_data;
+
+ int (*execute)(struct ast_channel *chan, void *data) = app->execute;
+
+ if (newstack) {
+ if (c->cdr)
+ ast_cdr_setapp(c->cdr, app->name, data);
+
+ /* save channel values */
+ saved_c_appl= c->appl;
+ saved_c_data= c->data;
+
+ c->appl = app->name;
+ c->data = data;
+ res = execute(c, data);
+ /* restore channel values */
+ c->appl= saved_c_appl;
+ c->data= saved_c_data;
+ return res;
+ } else
+ ast_log(LOG_WARNING, "You really didn't want to call this function with newstack set to 0\n");
+ return -1;
+}
+
+
+/*! Go no deeper than this through includes (not counting loops) */
+#define AST_PBX_MAX_STACK 128
+
+#define HELPER_EXISTS 0
+#define HELPER_SPAWN 1
+#define HELPER_EXEC 2
+#define HELPER_CANMATCH 3
+#define HELPER_MATCHMORE 4
+#define HELPER_FINDLABEL 5
+
+/*! \brief Find application handle in linked list
+ */
+struct ast_app *pbx_findapp(const char *app)
+{
+ struct ast_app *tmp;
+
+ if (ast_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;
+ }
+ ast_mutex_unlock(&applock);
+ return tmp;
+}
+
+static struct ast_switch *pbx_findswitch(const char *sw)
+{
+ struct ast_switch *asw;
+
+ if (ast_mutex_lock(&switchlock)) {
+ ast_log(LOG_WARNING, "Unable to obtain application lock\n");
+ return NULL;
+ }
+ asw = switches;
+ while(asw) {
+ if (!strcasecmp(asw->name, sw))
+ break;
+ asw = asw->next;
+ }
+ ast_mutex_unlock(&switchlock);
+ return asw;
+}
+
+static inline int include_valid(struct ast_include *i)
+{
+ if (!i->hastime)
+ return 1;
+
+ return ast_check_timing(&(i->timing));
+}
+
+static void pbx_destroy(struct ast_pbx *p)
+{
+ free(p);
+}
+
+#define EXTENSION_MATCH_CORE(data,pattern,match) {\
+ /* All patterns begin with _ */\
+ if (pattern[0] != '_') \
+ return 0;\
+ /* Start optimistic */\
+ match=1;\
+ pattern++;\
+ while(match && *data && *pattern && (*pattern != '/')) {\
+ while (*data == '-' && (*(data+1) != '\0')) data++;\
+ switch(toupper(*pattern)) {\
+ case '[': \
+ {\
+ int i,border=0;\
+ char *where;\
+ match=0;\
+ pattern++;\
+ where=strchr(pattern,']');\
+ if (where)\
+ border=(int)(where-pattern);\
+ if (!where || border > strlen(pattern)) {\
+ ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n");\
+ return match;\
+ }\
+ for (i=0; i<border; i++) {\
+ int res=0;\
+ if (i+2<border)\
+ if (pattern[i+1]=='-') {\
+ if (*data >= pattern[i] && *data <= pattern[i+2]) {\
+ res=1;\
+ } else {\
+ i+=2;\
+ continue;\
+ }\
+ }\
+ if (res==1 || *data==pattern[i]) {\
+ match = 1;\
+ break;\
+ }\
+ }\
+ pattern+=border;\
+ break;\
+ }\
+ case 'N':\
+ if ((*data < '2') || (*data > '9'))\
+ match=0;\
+ break;\
+ case 'X':\
+ if ((*data < '0') || (*data > '9'))\
+ match = 0;\
+ break;\
+ case 'Z':\
+ if ((*data < '1') || (*data > '9'))\
+ match = 0;\
+ break;\
+ case '.':\
+ /* Must match */\
+ return 1;\
+ case '!':\
+ /* Early match */\
+ return 2;\
+ case ' ':\
+ case '-':\
+ /* Ignore these characters */\
+ data--;\
+ break;\
+ default:\
+ if (*data != *pattern)\
+ match =0;\
+ }\
+ data++;\
+ pattern++;\
+ }\
+ /* If we ran off the end of the data and the pattern ends in '!', match */\
+ if (match && !*data && (*pattern == '!'))\
+ return 2;\
+}
+
+int ast_extension_match(const char *pattern, const char *data)
+{
+ int match;
+ /* If they're the same return */
+ if (!strcmp(pattern, data))
+ return 1;
+ EXTENSION_MATCH_CORE(data,pattern,match);
+ /* Must be at the end of both */
+ if (*data || (*pattern && (*pattern != '/')))
+ match = 0;
+ return match;
+}
+
+int ast_extension_close(const char *pattern, const char *data, int needmore)
+{
+ int match;
+ /* If "data" is longer, it can'be a subset of pattern unless
+ pattern is a pattern match */
+ if ((strlen(pattern) < strlen(data)) && (pattern[0] != '_'))
+ return 0;
+
+ if ((ast_strlen_zero((char *)data) || !strncasecmp(pattern, data, strlen(data))) &&
+ (!needmore || (strlen(pattern) > strlen(data)))) {
+ return 1;
+ }
+ EXTENSION_MATCH_CORE(data,pattern,match);
+ /* If there's more or we don't care about more, or if it's a possible early match,
+ return non-zero; otherwise it's a miss */
+ if (!needmore || *pattern || match == 2) {
+ return match;
+ } else
+ return 0;
+}
+
+struct ast_context *ast_context_find(const char *name)
+{
+ struct ast_context *tmp;
+ ast_mutex_lock(&conlock);
+ if (name) {
+ tmp = contexts;
+ while(tmp) {
+ if (!strcasecmp(name, tmp->name))
+ break;
+ tmp = tmp->next;
+ }
+ } else
+ tmp = contexts;
+ ast_mutex_unlock(&conlock);
+ return tmp;
+}
+
+#define STATUS_NO_CONTEXT 1
+#define STATUS_NO_EXTENSION 2
+#define STATUS_NO_PRIORITY 3
+#define STATUS_NO_LABEL 4
+#define STATUS_SUCCESS 5
+
+static int matchcid(const char *cidpattern, const char *callerid)
+{
+ int failresult;
+
+ /* If the Caller*ID pattern is empty, then we're matching NO Caller*ID, so
+ failing to get a number should count as a match, otherwise not */
+
+ if (!ast_strlen_zero(cidpattern))
+ failresult = 0;
+ else
+ failresult = 1;
+
+ if (!callerid)
+ return failresult;
+
+ return ast_extension_match(cidpattern, callerid);
+}
+
+static struct ast_exten *pbx_find_extension(struct ast_channel *chan, struct ast_context *bypass, const char *context, const char *exten, int priority, const char *label, const char *callerid, int action, char *incstack[], int *stacklen, int *status, struct ast_switch **swo, char **data, const char **foundcontext)
+{
+ int x, res;
+ struct ast_context *tmp;
+ struct ast_exten *e, *eroot;
+ struct ast_include *i;
+ struct ast_sw *sw;
+ struct ast_switch *asw;
+
+ /* Initialize status if appropriate */
+ if (!*stacklen) {
+ *status = STATUS_NO_CONTEXT;
+ *swo = NULL;
+ *data = NULL;
+ }
+ /* Check for stack overflow */
+ if (*stacklen >= AST_PBX_MAX_STACK) {
+ ast_log(LOG_WARNING, "Maximum PBX stack exceeded\n");
+ return NULL;
+ }
+ /* Check first to see if we've already been checked */
+ for (x=0; x<*stacklen; x++) {
+ if (!strcasecmp(incstack[x], context))
+ return NULL;
+ }
+ if (bypass)
+ tmp = bypass;
+ else
+ tmp = contexts;
+ while(tmp) {
+ /* Match context */
+ if (bypass || !strcmp(tmp->name, context)) {
+ struct ast_exten *earlymatch = NULL;
+
+ if (*status < STATUS_NO_EXTENSION)
+ *status = STATUS_NO_EXTENSION;
+ for (eroot = tmp->root; eroot; eroot=eroot->next) {
+ int match = 0;
+ /* Match extension */
+ if ((((action != HELPER_MATCHMORE) && ast_extension_match(eroot->exten, exten)) ||
+ ((action == HELPER_CANMATCH) && (ast_extension_close(eroot->exten, exten, 0))) ||
+ ((action == HELPER_MATCHMORE) && (match = ast_extension_close(eroot->exten, exten, 1)))) &&
+ (!eroot->matchcid || matchcid(eroot->cidmatch, callerid))) {
+
+ if (action == HELPER_MATCHMORE && match == 2 && !earlymatch) {
+ /* It matched an extension ending in a '!' wildcard
+ So ignore it for now, unless there's a better match */
+ earlymatch = eroot;
+ } else {
+ e = eroot;
+ if (*status < STATUS_NO_PRIORITY)
+ *status = STATUS_NO_PRIORITY;
+ while(e) {
+ /* Match priority */
+ if (action == HELPER_FINDLABEL) {
+ if (*status < STATUS_NO_LABEL)
+ *status = STATUS_NO_LABEL;
+ if (label && e->label && !strcmp(label, e->label)) {
+ *status = STATUS_SUCCESS;
+ *foundcontext = context;
+ return e;
+ }
+ } else if (e->priority == priority) {
+ *status = STATUS_SUCCESS;
+ *foundcontext = context;
+ return e;
+ }
+ e = e->peer;
+ }
+ }
+ }
+ }
+ if (earlymatch) {
+ /* Bizarre logic for HELPER_MATCHMORE. We return zero to break out
+ of the loop waiting for more digits, and _then_ match (normally)
+ the extension we ended up with. We got an early-matching wildcard
+ pattern, so return NULL to break out of the loop. */
+ return NULL;
+ }
+ /* Check alternative switches */
+ sw = tmp->alts;
+ while(sw) {
+ if ((asw = pbx_findswitch(sw->name))) {
+ /* Substitute variables now */
+ if (sw->eval)
+ pbx_substitute_variables_helper(chan, sw->data, sw->tmpdata, SWITCH_DATA_LENGTH - 1);
+ if (action == HELPER_CANMATCH)
+ res = asw->canmatch ? asw->canmatch(chan, context, exten, priority, callerid, sw->eval ? sw->tmpdata : sw->data) : 0;
+ else if (action == HELPER_MATCHMORE)
+ res = asw->matchmore ? asw->matchmore(chan, context, exten, priority, callerid, sw->eval ? sw->tmpdata : sw->data) : 0;
+ else
+ res = asw->exists ? asw->exists(chan, context, exten, priority, callerid, sw->eval ? sw->tmpdata : sw->data) : 0;
+ if (res) {
+ /* Got a match */
+ *swo = asw;
+ *data = sw->eval ? sw->tmpdata : sw->data;
+ *foundcontext = context;
+ return NULL;
+ }
+ } else {
+ ast_log(LOG_WARNING, "No such switch '%s'\n", sw->name);
+ }
+ sw = sw->next;
+ }
+ /* Setup the stack */
+ incstack[*stacklen] = tmp->name;
+ (*stacklen)++;
+ /* Now try any includes we have in this context */
+ i = tmp->includes;
+ while(i) {
+ if (include_valid(i)) {
+ if ((e = pbx_find_extension(chan, bypass, i->rname, exten, priority, label, callerid, action, incstack, stacklen, status, swo, data, foundcontext)))
+ return e;
+ if (*swo)
+ return NULL;
+ }
+ i = i->next;
+ }
+ break;
+ }
+ tmp = tmp->next;
+ }
+ return NULL;
+}
+
+/* Note that it's negative -- that's important later. */
+#define DONT_HAVE_LENGTH 0x80000000
+
+static int parse_variable_name(char *var, int *offset, int *length, int *isfunc)
+{
+ char *varchar, *offsetchar = NULL;
+ int parens=0;
+
+ *offset = 0;
+ *length = DONT_HAVE_LENGTH;
+ *isfunc = 0;
+ for (varchar=var; *varchar; varchar++) {
+ switch (*varchar) {
+ case '(':
+ (*isfunc)++;
+ parens++;
+ break;
+ case ')':
+ parens--;
+ break;
+ case ':':
+ if (parens == 0) {
+ offsetchar = varchar + 1;
+ *varchar = '\0';
+ goto pvn_endfor;
+ }
+ }
+ }
+pvn_endfor:
+ if (offsetchar) {
+ sscanf(offsetchar, "%d:%d", offset, length);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/*! \brief takes a substring. It is ok to call with value == workspace.
+ *
+ * offset < 0 means start from the end of the string and set the beginning
+ * to be that many characters back.
+ * length is the length of the substring, -1 means unlimited
+ * (we take any negative value).
+ * Always return a copy in workspace.
+ */
+static char *substring(const char *value, int offset, int length, char *workspace, size_t workspace_len)
+{
+ char *ret = workspace;
+ int lr; /* length of the input string after the copy */
+
+ ast_copy_string(workspace, value, workspace_len); /* always make a copy */
+
+ if (offset == 0 && length < 0) /* take the whole string */
+ return ret;
+
+ lr = strlen(ret); /* compute length after copy, so we never go out of the workspace */
+
+ if (offset < 0) { /* translate negative offset into positive ones */
+ offset = lr + offset;
+ if (offset < 0) /* If the negative offset was greater than the length of the string, just start at the beginning */
+ offset = 0;
+ }
+
+ /* too large offset result in empty string so we know what to return */
+ if (offset >= lr)
+ return ret + lr; /* the final '\0' */
+
+ ret += offset; /* move to the start position */
+ if (length >= 0 && length < lr - offset) /* truncate if necessary */
+ ret[length] = '\0';
+
+ return ret;
+}
+
+/*! \brief pbx_retrieve_variable: Support for Asterisk built-in variables and
+ functions in the dialplan
+ ---*/
+void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp)
+{
+ char tmpvar[80];
+ time_t thistime;
+ struct tm brokentime;
+ int offset, offset2, isfunc;
+ struct ast_var_t *variables;
+
+ if (c)
+ headp=&c->varshead;
+ *ret=NULL;
+ ast_copy_string(tmpvar, var, sizeof(tmpvar));
+ if (parse_variable_name(tmpvar, &offset, &offset2, &isfunc)) {
+ pbx_retrieve_variable(c, tmpvar, ret, workspace, workspacelen, headp);
+ if (!(*ret))
+ return;
+ *ret = substring(*ret, offset, offset2, workspace, workspacelen);
+ } else if (c && !strncmp(var, "CALL", 4)) {
+ if (!strncmp(var + 4, "ER", 2)) {
+ if (!strncmp(var + 6, "ID", 2)) {
+ if (!var[8]) { /* CALLERID */
+ if (c->cid.cid_num) {
+ if (c->cid.cid_name) {
+ snprintf(workspace, workspacelen, "\"%s\" <%s>", c->cid.cid_name, c->cid.cid_num);
+ } else {
+ ast_copy_string(workspace, c->cid.cid_num, workspacelen);
+ }
+ *ret = workspace;
+ } else if (c->cid.cid_name) {
+ ast_copy_string(workspace, c->cid.cid_name, workspacelen);
+ *ret = workspace;
+ } else
+ *ret = NULL;
+ } else if (!strcmp(var + 8, "NUM")) {
+ /* CALLERIDNUM */
+ if (c->cid.cid_num) {
+ ast_copy_string(workspace, c->cid.cid_num, workspacelen);
+ *ret = workspace;
+ } else
+ *ret = NULL;
+ } else if (!strcmp(var + 8, "NAME")) {
+ /* CALLERIDNAME */
+ if (c->cid.cid_name) {
+ ast_copy_string(workspace, c->cid.cid_name, workspacelen);
+ *ret = workspace;
+ } else
+ *ret = NULL;
+ }
+ } else if (!strcmp(var + 6, "ANI")) {
+ /* CALLERANI */
+ if (c->cid.cid_ani) {
+ ast_copy_string(workspace, c->cid.cid_ani, workspacelen);
+ *ret = workspace;
+ } else
+ *ret = NULL;
+ } else
+ goto icky;
+ } else if (!strncmp(var + 4, "ING", 3)) {
+ if (!strcmp(var + 7, "PRES")) {
+ /* CALLINGPRES */
+ snprintf(workspace, workspacelen, "%d", c->cid.cid_pres);
+ *ret = workspace;
+ } else if (!strcmp(var + 7, "ANI2")) {
+ /* CALLINGANI2 */
+ snprintf(workspace, workspacelen, "%d", c->cid.cid_ani2);
+ *ret = workspace;
+ } else if (!strcmp(var + 7, "TON")) {
+ /* CALLINGTON */
+ snprintf(workspace, workspacelen, "%d", c->cid.cid_ton);
+ *ret = workspace;
+ } else if (!strcmp(var + 7, "TNS")) {
+ /* CALLINGTNS */
+ snprintf(workspace, workspacelen, "%d", c->cid.cid_tns);
+ *ret = workspace;
+ } else
+ goto icky;
+ } else
+ goto icky;
+ } else if (c && !strcmp(var, "DNID")) {
+ if (c->cid.cid_dnid) {
+ ast_copy_string(workspace, c->cid.cid_dnid, workspacelen);
+ *ret = workspace;
+ } else
+ *ret = NULL;
+ } else if (c && !strcmp(var, "HINT")) {
+ if (!ast_get_hint(workspace, workspacelen, NULL, 0, c, c->context, c->exten))
+ *ret = NULL;
+ else
+ *ret = workspace;
+ } else if (c && !strcmp(var, "HINTNAME")) {
+ if (!ast_get_hint(NULL, 0, workspace, workspacelen, c, c->context, c->exten))
+ *ret = NULL;
+ else
+ *ret = workspace;
+ } else if (c && !strcmp(var, "EXTEN")) {
+ ast_copy_string(workspace, c->exten, workspacelen);
+ *ret = workspace;
+ } else if (c && !strcmp(var, "RDNIS")) {
+ if (c->cid.cid_rdnis) {
+ ast_copy_string(workspace, c->cid.cid_rdnis, workspacelen);
+ *ret = workspace;
+ } else
+ *ret = NULL;
+ } else if (c && !strcmp(var, "CONTEXT")) {
+ ast_copy_string(workspace, c->context, workspacelen);
+ *ret = workspace;
+ } else if (c && !strcmp(var, "PRIORITY")) {
+ snprintf(workspace, workspacelen, "%d", c->priority);
+ *ret = workspace;
+ } else if (c && !strcmp(var, "CHANNEL")) {
+ ast_copy_string(workspace, c->name, workspacelen);
+ *ret = workspace;
+ } else if (!strcmp(var, "EPOCH")) {
+ snprintf(workspace, workspacelen, "%u",(int)time(NULL));
+ *ret = workspace;
+ } else if (!strcmp(var, "DATETIME")) {
+ thistime=time(NULL);
+ localtime_r(&thistime, &brokentime);
+ snprintf(workspace, workspacelen, "%02d%02d%04d-%02d:%02d:%02d",
+ brokentime.tm_mday,
+ brokentime.tm_mon+1,
+ brokentime.tm_year+1900,
+ brokentime.tm_hour,
+ brokentime.tm_min,
+ brokentime.tm_sec
+ );
+ *ret = workspace;
+ } else if (!strcmp(var, "TIMESTAMP")) {
+ thistime=time(NULL);
+ localtime_r(&thistime, &brokentime);
+ /* 20031130-150612 */
+ snprintf(workspace, workspacelen, "%04d%02d%02d-%02d%02d%02d",
+ brokentime.tm_year+1900,
+ brokentime.tm_mon+1,
+ brokentime.tm_mday,
+ brokentime.tm_hour,
+ brokentime.tm_min,
+ brokentime.tm_sec
+ );
+ *ret = workspace;
+ } else if (c && !strcmp(var, "UNIQUEID")) {
+ snprintf(workspace, workspacelen, "%s", c->uniqueid);
+ *ret = workspace;
+ } else if (c && !strcmp(var, "HANGUPCAUSE")) {
+ snprintf(workspace, workspacelen, "%d", c->hangupcause);
+ *ret = workspace;
+ } else if (c && !strcmp(var, "ACCOUNTCODE")) {
+ ast_copy_string(workspace, c->accountcode, workspacelen);
+ *ret = workspace;
+ } else if (c && !strcmp(var, "LANGUAGE")) {
+ ast_copy_string(workspace, c->language, workspacelen);
+ *ret = workspace;
+ } else {
+icky:
+ if (headp) {
+ AST_LIST_TRAVERSE(headp,variables,entries) {
+#if 0
+ ast_log(LOG_WARNING,"Comparing variable '%s' with '%s'\n",var,ast_var_name(variables));
+#endif
+ if (strcasecmp(ast_var_name(variables),var)==0) {
+ *ret=ast_var_value(variables);
+ if (*ret) {
+ ast_copy_string(workspace, *ret, workspacelen);
+ *ret = workspace;
+ }
+ break;
+ }
+ }
+ }
+ if (!(*ret)) {
+ /* Try globals */
+ AST_LIST_TRAVERSE(&globals,variables,entries) {
+#if 0
+ ast_log(LOG_WARNING,"Comparing variable '%s' with '%s'\n",var,ast_var_name(variables));
+#endif
+ if (strcasecmp(ast_var_name(variables),var)==0) {
+ *ret = ast_var_value(variables);
+ if (*ret) {
+ ast_copy_string(workspace, *ret, workspacelen);
+ *ret = workspace;
+ }
+ }
+ }
+ }
+ }
+}
+
+/*! \brief CLI function to show installed custom functions
+ \addtogroup CLI_functions
+ */
+static int handle_show_functions(int fd, int argc, char *argv[])
+{
+ struct ast_custom_function *acf;
+ int count_acf = 0;
+
+ ast_cli(fd, "Installed Custom Functions:\n--------------------------------------------------------------------------------\n");
+ for (acf = acf_root ; acf; acf = acf->next) {
+ ast_cli(fd, "%-20.20s %-35.35s %s\n", acf->name, acf->syntax, acf->synopsis);
+ count_acf++;
+ }
+ ast_cli(fd, "%d custom functions installed.\n", count_acf);
+ return 0;
+}
+
+static int handle_show_function(int fd, int argc, char *argv[])
+{
+ struct ast_custom_function *acf;
+ /* Maximum number of characters added by terminal coloring is 22 */
+ char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
+ char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
+ char stxtitle[40], *syntax = NULL;
+ int synopsis_size, description_size, syntax_size;
+
+ if (argc < 3) return RESULT_SHOWUSAGE;
+
+ if (!(acf = ast_custom_function_find(argv[2]))) {
+ ast_cli(fd, "No function by that name registered.\n");
+ return RESULT_FAILURE;
+
+ }
+
+ if (acf->synopsis)
+ synopsis_size = strlen(acf->synopsis) + 23;
+ else
+ synopsis_size = strlen("Not available") + 23;
+ synopsis = alloca(synopsis_size);
+
+ if (acf->desc)
+ description_size = strlen(acf->desc) + 23;
+ else
+ description_size = strlen("Not available") + 23;
+ description = alloca(description_size);
+
+ if (acf->syntax)
+ syntax_size = strlen(acf->syntax) + 23;
+ else
+ syntax_size = strlen("Not available") + 23;
+ syntax = alloca(syntax_size);
+
+ snprintf(info, 64 + AST_MAX_APP, "\n -= Info about function '%s' =- \n\n", acf->name);
+ term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
+ term_color(stxtitle, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
+ term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
+ term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
+ term_color(syntax,
+ acf->syntax ? acf->syntax : "Not available",
+ COLOR_CYAN, 0, syntax_size);
+ term_color(synopsis,
+ acf->synopsis ? acf->synopsis : "Not available",
+ COLOR_CYAN, 0, synopsis_size);
+ term_color(description,
+ acf->desc ? acf->desc : "Not available",
+ COLOR_CYAN, 0, description_size);
+
+ ast_cli(fd,"%s%s%s\n\n%s%s\n\n%s%s\n", infotitle, stxtitle, syntax, syntitle, synopsis, destitle, description);
+
+ return RESULT_SUCCESS;
+}
+
+static char *complete_show_function(char *line, char *word, int pos, int state)
+{
+ struct ast_custom_function *acf;
+ int which = 0;
+
+ /* try to lock functions list ... */
+ if (ast_mutex_lock(&acflock)) {
+ ast_log(LOG_ERROR, "Unable to lock function list\n");
+ return NULL;
+ }
+
+ acf = acf_root;
+ while (acf) {
+ if (!strncasecmp(word, acf->name, strlen(word))) {
+ if (++which > state) {
+ char *ret = strdup(acf->name);
+ ast_mutex_unlock(&acflock);
+ return ret;
+ }
+ }
+ acf = acf->next;
+ }
+
+ ast_mutex_unlock(&acflock);
+ return NULL;
+}
+
+struct ast_custom_function* ast_custom_function_find(char *name)
+{
+ struct ast_custom_function *acfptr;
+
+ /* try to lock functions list ... */
+ if (ast_mutex_lock(&acflock)) {
+ ast_log(LOG_ERROR, "Unable to lock function list\n");
+ return NULL;
+ }
+
+ for (acfptr = acf_root; acfptr; acfptr = acfptr->next) {
+ if (!strcmp(name, acfptr->name)) {
+ break;
+ }
+ }
+
+ ast_mutex_unlock(&acflock);
+
+ return acfptr;
+}
+
+int ast_custom_function_unregister(struct ast_custom_function *acf)
+{
+ struct ast_custom_function *acfptr, *lastacf = NULL;
+ int res = -1;
+
+ if (!acf)
+ return -1;
+
+ /* try to lock functions list ... */
+ if (ast_mutex_lock(&acflock)) {
+ ast_log(LOG_ERROR, "Unable to lock function list\n");
+ return -1;
+ }
+
+ for (acfptr = acf_root; acfptr; acfptr = acfptr->next) {
+ if (acfptr == acf) {
+ if (lastacf) {
+ lastacf->next = acf->next;
+ } else {
+ acf_root = acf->next;
+ }
+ res = 0;
+ break;
+ }
+ lastacf = acfptr;
+ }
+
+ ast_mutex_unlock(&acflock);
+
+ if (!res && (option_verbose > 1))
+ ast_verbose(VERBOSE_PREFIX_2 "Unregistered custom function %s\n", acf->name);
+
+ return res;
+}
+
+int ast_custom_function_register(struct ast_custom_function *acf)
+{
+ if (!acf)
+ return -1;
+
+ /* try to lock functions list ... */
+ if (ast_mutex_lock(&acflock)) {
+ ast_log(LOG_ERROR, "Unable to lock function list. Failed registering function %s\n", acf->name);
+ return -1;
+ }
+
+ if (ast_custom_function_find(acf->name)) {
+ ast_log(LOG_ERROR, "Function %s already registered.\n", acf->name);
+ ast_mutex_unlock(&acflock);
+ return -1;
+ }
+
+ acf->next = acf_root;
+ acf_root = acf;
+
+ ast_mutex_unlock(&acflock);
+
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Registered custom function %s\n", acf->name);
+
+ return 0;
+}
+
+char *ast_func_read(struct ast_channel *chan, const char *in, char *workspace, size_t len)
+{
+ char *args = NULL, *function, *p;
+ char *ret = "0";
+ struct ast_custom_function *acfptr;
+
+ function = ast_strdupa(in);
+ if (!function) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ return ret;
+ }
+ if ((args = strchr(function, '('))) {
+ *args = '\0';
+ args++;
+ if ((p = strrchr(args, ')'))) {
+ *p = '\0';
+ } else {
+ ast_log(LOG_WARNING, "Can't find trailing parenthesis?\n");
+ }
+ } else {
+ ast_log(LOG_WARNING, "Function doesn't contain parentheses. Assuming null argument.\n");
+ }
+
+ if ((acfptr = ast_custom_function_find(function))) {
+ /* run the custom function */
+ if (acfptr->read) {
+ return acfptr->read(chan, function, args, workspace, len);
+ } else {
+ ast_log(LOG_ERROR, "Function %s cannot be read\n", function);
+ }
+ } else {
+ ast_log(LOG_ERROR, "Function %s not registered\n", function);
+ }
+ return ret;
+}
+
+void ast_func_write(struct ast_channel *chan, const char *in, const char *value)
+{
+ char *args = NULL, *function, *p;
+ struct ast_custom_function *acfptr;
+
+ function = ast_strdupa(in);
+ if (!function) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ return;
+ }
+ if ((args = strchr(function, '('))) {
+ *args = '\0';
+ args++;
+ if ((p = strrchr(args, ')'))) {
+ *p = '\0';
+ } else {
+ ast_log(LOG_WARNING, "Can't find trailing parenthesis?\n");
+ }
+ } else {
+ ast_log(LOG_WARNING, "Function doesn't contain parentheses. Assuming null argument.\n");
+ }
+
+ if ((acfptr = ast_custom_function_find(function))) {
+ /* run the custom function */
+ if (acfptr->write) {
+ acfptr->write(chan, function, args, value);
+ } else {
+ ast_log(LOG_ERROR, "Function %s is read-only, it cannot be written to\n", function);
+ }
+ } else {
+ ast_log(LOG_ERROR, "Function %s not registered\n", function);
+ }
+}
+
+static void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count)
+{
+ char *cp4;
+ const char *tmp, *whereweare;
+ int length, offset, offset2, isfunction;
+ char *workspace = NULL;
+ char *ltmp = NULL, *var = NULL;
+ char *nextvar, *nextexp, *nextthing;
+ char *vars, *vare;
+ int pos, brackets, needsub, len;
+
+ /* Substitutes variables into cp2, based on string cp1, and assuming cp2 to be
+ zero-filled */
+ whereweare=tmp=cp1;
+ while(!ast_strlen_zero(whereweare) && count) {
+ /* Assume we're copying the whole remaining string */
+ pos = strlen(whereweare);
+ nextvar = NULL;
+ nextexp = NULL;
+ nextthing = strchr(whereweare, '$');
+ if (nextthing) {
+ switch(nextthing[1]) {
+ case '{':
+ nextvar = nextthing;
+ pos = nextvar - whereweare;
+ break;
+ case '[':
+ nextexp = nextthing;
+ pos = nextexp - whereweare;
+ break;
+ }
+ }
+
+ if (pos) {
+ /* Can't copy more than 'count' bytes */
+ if (pos > count)
+ pos = count;
+
+ /* Copy that many bytes */
+ memcpy(cp2, whereweare, pos);
+
+ count -= pos;
+ cp2 += pos;
+ whereweare += pos;
+ }
+
+ if (nextvar) {
+ /* We have a variable. Find the start and end, and determine
+ if we are going to have to recursively call ourselves on the
+ contents */
+ vars = vare = nextvar + 2;
+ brackets = 1;
+ needsub = 0;
+
+ /* Find the end of it */
+ while(brackets && *vare) {
+ if ((vare[0] == '$') && (vare[1] == '{')) {
+ needsub++;
+ brackets++;
+ } else if (vare[0] == '}') {
+ brackets--;
+ } else if ((vare[0] == '$') && (vare[1] == '['))
+ needsub++;
+ vare++;
+ }
+ if (brackets)
+ ast_log(LOG_NOTICE, "Error in extension logic (missing '}')\n");
+ len = vare - vars - 1;
+
+ /* Skip totally over variable string */
+ whereweare += (len + 3);
+
+ if (!var)
+ var = alloca(VAR_BUF_SIZE);
+
+ /* Store variable name (and truncate) */
+ ast_copy_string(var, vars, len + 1);
+
+ /* Substitute if necessary */
+ if (needsub) {
+ if (!ltmp)
+ ltmp = alloca(VAR_BUF_SIZE);
+
+ memset(ltmp, 0, VAR_BUF_SIZE);
+ pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1);
+ vars = ltmp;
+ } else {
+ vars = var;
+ }
+
+ if (!workspace)
+ workspace = alloca(VAR_BUF_SIZE);
+
+ workspace[0] = '\0';
+
+ parse_variable_name(vars, &offset, &offset2, &isfunction);
+ if (isfunction) {
+ /* Evaluate function */
+ cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE);
+
+ ast_log(LOG_DEBUG, "Function result is '%s'\n", cp4 ? cp4 : "(null)");
+ } else {
+ /* Retrieve variable value */
+ pbx_retrieve_variable(c, vars, &cp4, workspace, VAR_BUF_SIZE, headp);
+ }
+ if (cp4) {
+ cp4 = substring(cp4, offset, offset2, workspace, VAR_BUF_SIZE);
+
+ length = strlen(cp4);
+ if (length > count)
+ length = count;
+ memcpy(cp2, cp4, length);
+ count -= length;
+ cp2 += length;
+ }
+ } else if (nextexp) {
+ /* We have an expression. Find the start and end, and determine
+ if we are going to have to recursively call ourselves on the
+ contents */
+ vars = vare = nextexp + 2;
+ brackets = 1;
+ needsub = 0;
+
+ /* Find the end of it */
+ while(brackets && *vare) {
+ if ((vare[0] == '$') && (vare[1] == '[')) {
+ needsub++;
+ brackets++;
+ vare++;
+ } else if (vare[0] == '[') {
+ brackets++;
+ } else if (vare[0] == ']') {
+ brackets--;
+ } else if ((vare[0] == '$') && (vare[1] == '{')) {
+ needsub++;
+ vare++;
+ }
+ vare++;
+ }
+ if (brackets)
+ ast_log(LOG_NOTICE, "Error in extension logic (missing ']')\n");
+ len = vare - vars - 1;
+
+ /* Skip totally over expression */
+ whereweare += (len + 3);
+
+ if (!var)
+ var = alloca(VAR_BUF_SIZE);
+
+ /* Store variable name (and truncate) */
+ ast_copy_string(var, vars, len + 1);
+
+ /* Substitute if necessary */
+ if (needsub) {
+ if (!ltmp)
+ ltmp = alloca(VAR_BUF_SIZE);
+
+ memset(ltmp, 0, VAR_BUF_SIZE);
+ pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1);
+ vars = ltmp;
+ } else {
+ vars = var;
+ }
+
+ length = ast_expr(vars, cp2, count);
+
+ if (length) {
+ ast_log(LOG_DEBUG, "Expression result is '%s'\n", cp2);
+ count -= length;
+ cp2 += length;
+ }
+ } else
+ break;
+ }
+}
+
+void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
+{
+ pbx_substitute_variables_helper_full(c, (c) ? &c->varshead : NULL, cp1, cp2, count);
+}
+
+void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count)
+{
+ pbx_substitute_variables_helper_full(NULL, headp, cp1, cp2, count);
+}
+
+static void pbx_substitute_variables(char *passdata, int datalen, struct ast_channel *c, struct ast_exten *e)
+{
+ memset(passdata, 0, datalen);
+
+ /* No variables or expressions in e->data, so why scan it? */
+ if (!strchr(e->data, '$') && !strstr(e->data,"${") && !strstr(e->data,"$[") && !strstr(e->data,"$(")) {
+ ast_copy_string(passdata, e->data, datalen);
+ return;
+ }
+
+ pbx_substitute_variables_helper(c, e->data, passdata, datalen - 1);
+}
+
+static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con, const char *context, const char *exten, int priority, const char *label, const char *callerid, int action)
+{
+ struct ast_exten *e;
+ struct ast_app *app;
+ struct ast_switch *sw;
+ char *data;
+ const char *foundcontext=NULL;
+ int newstack = 0;
+ int res;
+ int status = 0;
+ char *incstack[AST_PBX_MAX_STACK];
+ char passdata[EXT_DATA_SIZE];
+ int stacklen = 0;
+ char tmp[80];
+ char tmp2[80];
+ char tmp3[EXT_DATA_SIZE];
+ char atmp[80];
+ char atmp2[EXT_DATA_SIZE+100];
+
+ if (ast_mutex_lock(&conlock)) {
+ ast_log(LOG_WARNING, "Unable to obtain lock\n");
+ if ((action == HELPER_EXISTS) || (action == HELPER_CANMATCH) || (action == HELPER_MATCHMORE))
+ return 0;
+ else
+ return -1;
+ }
+ e = pbx_find_extension(c, con, context, exten, priority, label, callerid, action, incstack, &stacklen, &status, &sw, &data, &foundcontext);
+ if (e) {
+ switch(action) {
+ case HELPER_CANMATCH:
+ ast_mutex_unlock(&conlock);
+ return -1;
+ case HELPER_EXISTS:
+ ast_mutex_unlock(&conlock);
+ return -1;
+ case HELPER_FINDLABEL:
+ res = e->priority;
+ ast_mutex_unlock(&conlock);
+ return res;
+ case HELPER_MATCHMORE:
+ ast_mutex_unlock(&conlock);
+ return -1;
+ case HELPER_SPAWN:
+ newstack++;
+ /* Fall through */
+ case HELPER_EXEC:
+ app = pbx_findapp(e->app);
+ ast_mutex_unlock(&conlock);
+ if (app) {
+ if (c->context != context)
+ ast_copy_string(c->context, context, sizeof(c->context));
+ if (c->exten != exten)
+ ast_copy_string(c->exten, exten, sizeof(c->exten));
+ c->priority = priority;
+ pbx_substitute_variables(passdata, sizeof(passdata), c, e);
+ if (option_debug) {
+ ast_log(LOG_DEBUG, "Launching '%s'\n", app->name);
+ snprintf(atmp, 80, "STACK-%s-%s-%d", context, exten, priority);
+ snprintf(atmp2, EXT_DATA_SIZE+100, "%s(\"%s\", \"%s\") %s", app->name, c->name, (!ast_strlen_zero(passdata) ? (char *)passdata : ""), (newstack ? "in new stack" : "in same stack"));
+ pbx_builtin_setvar_helper(c, atmp, atmp2);
+ }
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "Executing %s(\"%s\", \"%s\") %s\n",
+ term_color(tmp, app->name, COLOR_BRCYAN, 0, sizeof(tmp)),
+ term_color(tmp2, c->name, COLOR_BRMAGENTA, 0, sizeof(tmp2)),
+ term_color(tmp3, (!ast_strlen_zero(passdata) ? (char *)passdata : ""), COLOR_BRMAGENTA, 0, sizeof(tmp3)),
+ (newstack ? "in new stack" : "in same stack"));
+ manager_event(EVENT_FLAG_CALL, "Newexten",
+ "Channel: %s\r\n"
+ "Context: %s\r\n"
+ "Extension: %s\r\n"
+ "Priority: %d\r\n"
+ "Application: %s\r\n"
+ "AppData: %s\r\n"
+ "Uniqueid: %s\r\n",
+ c->name, c->context, c->exten, c->priority, app->name, passdata ? passdata : "(NULL)", c->uniqueid);
+ res = pbx_exec(c, app, passdata, newstack);
+ return res;
+ } 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); return -1;
+ }
+ } else if (sw) {
+ switch(action) {
+ case HELPER_CANMATCH:
+ ast_mutex_unlock(&conlock);
+ return -1;
+ case HELPER_EXISTS:
+ ast_mutex_unlock(&conlock);
+ return -1;
+ case HELPER_MATCHMORE:
+ ast_mutex_unlock(&conlock);
+ return -1;
+ case HELPER_FINDLABEL:
+ ast_mutex_unlock(&conlock);
+ return -1;
+ case HELPER_SPAWN:
+ newstack++;
+ /* Fall through */
+ case HELPER_EXEC:
+ ast_mutex_unlock(&conlock);
+ if (sw->exec)
+ res = sw->exec(c, foundcontext ? foundcontext : context, exten, priority, callerid, newstack, data);
+ else {
+ ast_log(LOG_WARNING, "No execution engine for switch %s\n", sw->name);
+ res = -1;
+ }
+ return res;
+ default:
+ ast_log(LOG_WARNING, "Huh (%d)?\n", action);
+ return -1;
+ }
+ } else {
+ ast_mutex_unlock(&conlock);
+ switch(status) {
+ case STATUS_NO_CONTEXT:
+ if ((action != HELPER_EXISTS) && (action != HELPER_MATCHMORE))
+ ast_log(LOG_NOTICE, "Cannot find extension context '%s'\n", context);
+ break;
+ case STATUS_NO_EXTENSION:
+ if ((action != HELPER_EXISTS) && (action != HELPER_CANMATCH) && (action != HELPER_MATCHMORE))
+ ast_log(LOG_NOTICE, "Cannot find extension '%s' in context '%s'\n", exten, context);
+ break;
+ case STATUS_NO_PRIORITY:
+ if ((action != HELPER_EXISTS) && (action != HELPER_CANMATCH) && (action != HELPER_MATCHMORE))
+ ast_log(LOG_NOTICE, "No such priority %d in extension '%s' in context '%s'\n", priority, exten, context);
+ break;
+ case STATUS_NO_LABEL:
+ if (context)
+ ast_log(LOG_NOTICE, "No such label '%s' in extension '%s' in context '%s'\n", label, exten, context);
+ break;
+ default:
+ ast_log(LOG_DEBUG, "Shouldn't happen!\n");
+ }
+
+ if ((action != HELPER_EXISTS) && (action != HELPER_CANMATCH) && (action != HELPER_MATCHMORE))
+ return -1;
+ else
+ return 0;
+ }
+
+}
+
+/*! \brief ast_hint_extension: Find hint for given extension in context */
+static struct ast_exten *ast_hint_extension(struct ast_channel *c, const char *context, const char *exten)
+{
+ struct ast_exten *e;
+ struct ast_switch *sw;
+ char *data;
+ const char *foundcontext = NULL;
+ int status = 0;
+ char *incstack[AST_PBX_MAX_STACK];
+ int stacklen = 0;
+
+ if (ast_mutex_lock(&conlock)) {
+ ast_log(LOG_WARNING, "Unable to obtain lock\n");
+ return NULL;
+ }
+ e = pbx_find_extension(c, NULL, context, exten, PRIORITY_HINT, NULL, "", HELPER_EXISTS, incstack, &stacklen, &status, &sw, &data, &foundcontext);
+ ast_mutex_unlock(&conlock);
+ return e;
+}
+
+/*! \brief ast_extensions_state2: Check state of extension by using hints */
+static int ast_extension_state2(struct ast_exten *e)
+{
+ char hint[AST_MAX_EXTENSION] = "";
+ char *cur, *rest;
+ int res = -1;
+ int allunavailable = 1, allbusy = 1, allfree = 1;
+ int busy = 0, inuse = 0, ring = 0;
+
+ if (!e)
+ return -1;
+
+ ast_copy_string(hint, ast_get_extension_app(e), sizeof(hint));
+
+ cur = hint; /* On or more devices separated with a & character */
+ do {
+ rest = strchr(cur, '&');
+ if (rest) {
+ *rest = 0;
+ rest++;
+ }
+
+ res = ast_device_state(cur);
+ switch (res) {
+ case AST_DEVICE_NOT_INUSE:
+ allunavailable = 0;
+ allbusy = 0;
+ break;
+ case AST_DEVICE_INUSE:
+ inuse = 1;
+ allunavailable = 0;
+ allfree = 0;
+ break;
+ case AST_DEVICE_RINGING:
+ ring = 1;
+ allunavailable = 0;
+ allfree = 0;
+ break;
+ case AST_DEVICE_BUSY:
+ allunavailable = 0;
+ allfree = 0;
+ busy = 1;
+ break;
+ case AST_DEVICE_UNAVAILABLE:
+ case AST_DEVICE_INVALID:
+ allbusy = 0;
+ allfree = 0;
+ break;
+ default:
+ allunavailable = 0;
+ allbusy = 0;
+ allfree = 0;
+ }
+ cur = rest;
+ } while (cur);
+
+ if (!inuse && ring)
+ return AST_EXTENSION_RINGING;
+ if (inuse && ring)
+ return (AST_EXTENSION_INUSE | AST_EXTENSION_RINGING);
+ if (inuse)
+ return AST_EXTENSION_INUSE;
+ if (allfree)
+ return AST_EXTENSION_NOT_INUSE;
+ if (allbusy)
+ return AST_EXTENSION_BUSY;
+ if (allunavailable)
+ return AST_EXTENSION_UNAVAILABLE;
+ if (busy)
+ return AST_EXTENSION_INUSE;
+
+ return AST_EXTENSION_NOT_INUSE;
+}
+
+/*! \brief ast_extension_state2str: Return extension_state as string */
+const char *ast_extension_state2str(int extension_state)
+{
+ int i;
+
+ for (i = 0; (i < (sizeof(extension_states) / sizeof(extension_states[0]))); i++) {
+ if (extension_states[i].extension_state == extension_state) {
+ return extension_states[i].text;
+ }
+ }
+ return "Unknown";
+}
+
+/*! \brief ast_extension_state: Check extension state for an extension by using hint */
+int ast_extension_state(struct ast_channel *c, char *context, char *exten)
+{
+ struct ast_exten *e;
+
+ e = ast_hint_extension(c, context, exten); /* Do we have a hint for this extension ? */
+ if (!e)
+ return -1; /* No hint, return -1 */
+
+ return ast_extension_state2(e); /* Check all devices in the hint */
+}
+
+void ast_hint_state_changed(const char *device)
+{
+ struct ast_hint *hint;
+ struct ast_state_cb *cblist;
+ char buf[AST_MAX_EXTENSION];
+ char *parse;
+ char *cur;
+ int state;
+
+ ast_mutex_lock(&hintlock);
+
+ for (hint = hints; hint; hint = hint->next) {
+ ast_copy_string(buf, ast_get_extension_app(hint->exten), sizeof(buf));
+ parse = buf;
+ for (cur = strsep(&parse, "&"); cur; cur = strsep(&parse, "&")) {
+ if (strcasecmp(cur, device))
+ continue;
+
+ /* Get device state for this hint */
+ state = ast_extension_state2(hint->exten);
+
+ if ((state == -1) || (state == hint->laststate))
+ continue;
+
+ /* Device state changed since last check - notify the watchers */
+
+ /* For general callbacks */
+ for (cblist = statecbs; cblist; cblist = cblist->next)
+ cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
+
+ /* For extension callbacks */
+ for (cblist = hint->callbacks; cblist; cblist = cblist->next)
+ cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
+
+ hint->laststate = state;
+ break;
+ }
+ }
+
+ ast_mutex_unlock(&hintlock);
+}
+
+/*! \brief ast_extension_state_add: Add watcher for extension states */
+int ast_extension_state_add(const char *context, const char *exten,
+ ast_state_cb_type callback, void *data)
+{
+ struct ast_hint *list;
+ struct ast_state_cb *cblist;
+ struct ast_exten *e;
+
+ /* If there's no context and extension: add callback to statecbs list */
+ if (!context && !exten) {
+ ast_mutex_lock(&hintlock);
+
+ cblist = statecbs;
+ while (cblist) {
+ if (cblist->callback == callback) {
+ cblist->data = data;
+ ast_mutex_unlock(&hintlock);
+ return 0;
+ }
+ cblist = cblist->next;
+ }
+
+ /* Now insert the callback */
+ cblist = malloc(sizeof(struct ast_state_cb));
+ if (!cblist) {
+ ast_mutex_unlock(&hintlock);
+ return -1;
+ }
+ memset(cblist, 0, sizeof(struct ast_state_cb));
+ cblist->id = 0;
+ cblist->callback = callback;
+ cblist->data = data;
+
+ cblist->next = statecbs;
+ statecbs = cblist;
+
+ ast_mutex_unlock(&hintlock);
+ return 0;
+ }
+
+ if (!context || !exten)
+ return -1;
+
+ /* This callback type is for only one hint, so get the hint */
+ e = ast_hint_extension(NULL, context, exten);
+ if (!e) {
+ return -1;
+ }
+
+ /* Find the hint in the list of hints */
+ ast_mutex_lock(&hintlock);
+ list = hints;
+
+ while (list) {
+ if (list->exten == e)
+ break;
+ list = list->next;
+ }
+
+ if (!list) {
+ /* We have no hint, sorry */
+ ast_mutex_unlock(&hintlock);
+ return -1;
+ }
+
+ /* Now insert the callback in the callback list */
+ cblist = malloc(sizeof(struct ast_state_cb));
+ if (!cblist) {
+ ast_mutex_unlock(&hintlock);
+ return -1;
+ }
+ memset(cblist, 0, sizeof(struct ast_state_cb));
+ cblist->id = stateid++; /* Unique ID for this callback */
+ cblist->callback = callback; /* Pointer to callback routine */
+ cblist->data = data; /* Data for the callback */
+
+ cblist->next = list->callbacks;
+ list->callbacks = cblist;
+
+ ast_mutex_unlock(&hintlock);
+ return cblist->id;
+}
+
+/*! \brief ast_extension_state_del: Remove a watcher from the callback list */
+int ast_extension_state_del(int id, ast_state_cb_type callback)
+{
+ struct ast_hint *list;
+ struct ast_state_cb *cblist, *cbprev;
+
+ if (!id && !callback)
+ return -1;
+
+ ast_mutex_lock(&hintlock);
+
+ /* id is zero is a callback without extension */
+ if (!id) {
+ cbprev = NULL;
+ cblist = statecbs;
+ while (cblist) {
+ if (cblist->callback == callback) {
+ if (!cbprev)
+ statecbs = cblist->next;
+ else
+ cbprev->next = cblist->next;
+
+ free(cblist);
+
+ ast_mutex_unlock(&hintlock);
+ return 0;
+ }
+ cbprev = cblist;
+ cblist = cblist->next;
+ }
+
+ ast_mutex_unlock(&hintlock);
+ return -1;
+ }
+
+ /* id greater than zero is a callback with extension */
+ /* Find the callback based on ID */
+ list = hints;
+ while (list) {
+ cblist = list->callbacks;
+ cbprev = NULL;
+ while (cblist) {
+ if (cblist->id==id) {
+ if (!cbprev)
+ list->callbacks = cblist->next;
+ else
+ cbprev->next = cblist->next;
+
+ free(cblist);
+
+ ast_mutex_unlock(&hintlock);
+ return 0;
+ }
+ cbprev = cblist;
+ cblist = cblist->next;
+ }
+ list = list->next;
+ }
+
+ ast_mutex_unlock(&hintlock);
+ return -1;
+}
+
+/*! \brief ast_add_hint: Add hint to hint list, check initial extension state */
+static int ast_add_hint(struct ast_exten *e)
+{
+ struct ast_hint *list;
+
+ if (!e)
+ return -1;
+
+ ast_mutex_lock(&hintlock);
+ list = hints;
+
+ /* Search if hint exists, do nothing */
+ while (list) {
+ if (list->exten == e) {
+ ast_mutex_unlock(&hintlock);
+ if (option_debug > 1)
+ ast_log(LOG_DEBUG, "HINTS: Not re-adding existing hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
+ return -1;
+ }
+ list = list->next;
+ }
+
+ if (option_debug > 1)
+ ast_log(LOG_DEBUG, "HINTS: Adding hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
+
+ list = malloc(sizeof(struct ast_hint));
+ if (!list) {
+ ast_mutex_unlock(&hintlock);
+ if (option_debug > 1)
+ ast_log(LOG_DEBUG, "HINTS: Out of memory...\n");
+ return -1;
+ }
+ /* Initialize and insert new item at the top */
+ memset(list, 0, sizeof(struct ast_hint));
+ list->exten = e;
+ list->laststate = ast_extension_state2(e);
+ list->next = hints;
+ hints = list;
+
+ ast_mutex_unlock(&hintlock);
+ return 0;
+}
+
+/*! \brief ast_change_hint: Change hint for an extension */
+static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
+{
+ struct ast_hint *list;
+
+ ast_mutex_lock(&hintlock);
+ list = hints;
+
+ while(list) {
+ if (list->exten == oe) {
+ list->exten = ne;
+ ast_mutex_unlock(&hintlock);
+ return 0;
+ }
+ list = list->next;
+ }
+ ast_mutex_unlock(&hintlock);
+
+ return -1;
+}
+
+/*! \brief ast_remove_hint: Remove hint from extension */
+static int ast_remove_hint(struct ast_exten *e)
+{
+ /* Cleanup the Notifys if hint is removed */
+ struct ast_hint *list, *prev = NULL;
+ struct ast_state_cb *cblist, *cbprev;
+
+ if (!e)
+ return -1;
+
+ ast_mutex_lock(&hintlock);
+
+ list = hints;
+ while(list) {
+ if (list->exten==e) {
+ cbprev = NULL;
+ cblist = list->callbacks;
+ while (cblist) {
+ /* Notify with -1 and remove all callbacks */
+ cbprev = cblist;
+ cblist = cblist->next;
+ cbprev->callback(list->exten->parent->name, list->exten->exten, AST_EXTENSION_DEACTIVATED, cbprev->data);
+ free(cbprev);
+ }
+ list->callbacks = NULL;
+
+ if (!prev)
+ hints = list->next;
+ else
+ prev->next = list->next;
+ free(list);
+
+ ast_mutex_unlock(&hintlock);
+ return 0;
+ } else {
+ prev = list;
+ list = list->next;
+ }
+ }
+
+ ast_mutex_unlock(&hintlock);
+ return -1;
+}
+
+
+/*! \brief ast_get_hint: Get hint for channel */
+int ast_get_hint(char *hint, int hintsize, char *name, int namesize, struct ast_channel *c, const char *context, const char *exten)
+{
+ struct ast_exten *e;
+ void *tmp;
+
+ e = ast_hint_extension(c, context, exten);
+ if (e) {
+ if (hint)
+ ast_copy_string(hint, ast_get_extension_app(e), hintsize);
+ if (name) {
+ tmp = ast_get_extension_app_data(e);
+ if (tmp)
+ ast_copy_string(name, (char *) tmp, namesize);
+ }
+ return -1;
+ }
+ return 0;
+}
+
+int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
+{
+ return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, HELPER_EXISTS);
+}
+
+int ast_findlabel_extension(struct ast_channel *c, const char *context, const char *exten, const char *label, const char *callerid)
+{
+ return pbx_extension_helper(c, NULL, context, exten, 0, label, callerid, HELPER_FINDLABEL);
+}
+
+int ast_findlabel_extension2(struct ast_channel *c, struct ast_context *con, const char *exten, const char *label, const char *callerid)
+{
+ return pbx_extension_helper(c, con, NULL, exten, 0, label, callerid, HELPER_FINDLABEL);
+}
+
+int ast_canmatch_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
+{
+ return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, HELPER_CANMATCH);
+}
+
+int ast_matchmore_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
+{
+ return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, HELPER_MATCHMORE);
+}
+
+int ast_spawn_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
+{
+ return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, HELPER_SPAWN);
+}
+
+int ast_exec_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
+{
+ return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, HELPER_EXEC);
+}
+
+static int __ast_pbx_run(struct ast_channel *c)
+{
+ int firstpass = 1;
+ int digit;
+ char exten[256];
+ int pos;
+ int waittime;
+ int res=0;
+ int autoloopflag;
+
+ /* A little initial setup here */
+ if (c->pbx)
+ ast_log(LOG_WARNING, "%s already has PBX structure??\n", c->name);
+ c->pbx = malloc(sizeof(struct ast_pbx));
+ if (!c->pbx) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ return -1;
+ }
+ if (c->amaflags) {
+ if (!c->cdr) {
+ c->cdr = ast_cdr_alloc();
+ if (!c->cdr) {
+ ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
+ free(c->pbx);
+ return -1;
+ }
+ ast_cdr_init(c->cdr, c);
+ }
+ }
+ memset(c->pbx, 0, sizeof(struct ast_pbx));
+ /* Set reasonable defaults */
+ c->pbx->rtimeout = 10;
+ c->pbx->dtimeout = 5;
+
+ autoloopflag = ast_test_flag(c, AST_FLAG_IN_AUTOLOOP);
+ ast_set_flag(c, AST_FLAG_IN_AUTOLOOP);
+
+ /* Start by trying whatever the channel is set to */
+ if (!ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
+ /* If not successful fall back to 's' */
+ if (option_verbose > 1)
+ ast_verbose( VERBOSE_PREFIX_2 "Starting %s at %s,%s,%d failed so falling back to exten 's'\n", c->name, c->context, c->exten, c->priority);
+ ast_copy_string(c->exten, "s", sizeof(c->exten));
+ if (!ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
+ /* JK02: And finally back to default if everything else failed */
+ if (option_verbose > 1)
+ ast_verbose( VERBOSE_PREFIX_2 "Starting %s at %s,%s,%d still failed so falling back to context 'default'\n", c->name, c->context, c->exten, c->priority);
+ ast_copy_string(c->context, "default", sizeof(c->context));
+ }
+ c->priority = 1;
+ }
+ if (c->cdr && !c->cdr->start.tv_sec && !c->cdr->start.tv_usec)
+ ast_cdr_start(c->cdr);
+ for(;;) {
+ pos = 0;
+ digit = 0;
+ while(ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
+ memset(exten, 0, sizeof(exten));
+ if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num))) {
+ /* Something bad happened, or a hangup has been requested. */
+ if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
+ (res == '*') || (res == '#')) {
+ ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res);
+ memset(exten, 0, sizeof(exten));
+ pos = 0;
+ exten[pos++] = digit = res;
+ break;
+ }
+ switch(res) {
+ case AST_PBX_KEEPALIVE:
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited KEEPALIVE 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 KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
+ goto out;
+ break;
+ default:
+ 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);
+ if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
+ c->_softhangup =0;
+ break;
+ }
+ /* atimeout */
+ if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
+ break;
+ }
+
+ if (c->cdr) {
+ ast_cdr_update(c);
+ }
+ goto out;
+ }
+ }
+ if ((c->_softhangup == AST_SOFTHANGUP_TIMEOUT) && (ast_exists_extension(c,c->context,"T",1,c->cid.cid_num))) {
+ ast_copy_string(c->exten, "T", sizeof(c->exten));
+ /* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */
+ c->whentohangup = 0;
+ c->priority = 0;
+ c->_softhangup &= ~AST_SOFTHANGUP_TIMEOUT;
+ } else if (c->_softhangup) {
+ ast_log(LOG_DEBUG, "Extension %s, priority %d returned normally even though call was hung up\n",
+ c->exten, c->priority);
+ goto out;
+ }
+ firstpass = 0;
+ c->priority++;
+ }
+ if (!ast_exists_extension(c, c->context, c->exten, 1, c->cid.cid_num)) {
+ /* It's not a valid extension anymore */
+ if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Sent into invalid extension '%s' in context '%s' on %s\n", c->exten, c->context, c->name);
+ pbx_builtin_setvar_helper(c, "INVALID_EXTEN", c->exten);
+ ast_copy_string(c->exten, "i", sizeof(c->exten));
+ c->priority = 1;
+ } else {
+ ast_log(LOG_WARNING, "Channel '%s' sent into invalid extension '%s' in context '%s', but no invalid handler\n",
+ c->name, c->exten, c->context);
+ goto out;
+ }
+ } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
+ /* If we get this far with AST_SOFTHANGUP_TIMEOUT, then we know that the "T" extension is next. */
+ c->_softhangup = 0;
+ } else {
+ /* Done, wait for an extension */
+ waittime = 0;
+ if (digit)
+ waittime = c->pbx->dtimeout;
+ else if (!autofallthrough)
+ waittime = c->pbx->rtimeout;
+ if (waittime) {
+ while (ast_matchmore_extension(c, c->context, exten, 1, c->cid.cid_num)) {
+ /* 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 (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
+ c->_softhangup = 0;
+ } else {
+ 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, c->cid.cid_num)) {
+ /* Prepare the next cycle */
+ ast_copy_string(c->exten, exten, sizeof(c->exten));
+ c->priority = 1;
+ } else {
+ /* No such extension */
+ if (!ast_strlen_zero(exten)) {
+ /* An invalid extension */
+ if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "Invalid extension '%s' in context '%s' on %s\n", exten, c->context, c->name);
+ pbx_builtin_setvar_helper(c, "INVALID_EXTEN", exten);
+ ast_copy_string(c->exten, "i", sizeof(c->exten));
+ c->priority = 1;
+ } else {
+ ast_log(LOG_WARNING, "Invalid extension '%s', but no rule 'i' in context '%s'\n", exten, c->context);
+ goto out;
+ }
+ } else {
+ /* A simple timeout */
+ if (ast_exists_extension(c, c->context, "t", 1, c->cid.cid_num)) {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "Timeout on %s\n", c->name);
+ ast_copy_string(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 (c->cdr) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_2 "CDR updated on %s\n",c->name);
+ ast_cdr_update(c);
+ }
+ } else {
+ char *status;
+
+ status = pbx_builtin_getvar_helper(c, "DIALSTATUS");
+ if (!status)
+ status = "UNKNOWN";
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_2 "Auto fallthrough, channel '%s' status is '%s'\n", c->name, status);
+ if (!strcasecmp(status, "CONGESTION"))
+ res = pbx_builtin_congestion(c, "10");
+ else if (!strcasecmp(status, "CHANUNAVAIL"))
+ res = pbx_builtin_congestion(c, "10");
+ else if (!strcasecmp(status, "BUSY"))
+ res = pbx_builtin_busy(c, "10");
+ goto out;
+ }
+ }
+ }
+ if (firstpass)
+ ast_log(LOG_WARNING, "Don't know what to do with '%s'\n", c->name);
+out:
+ if ((res != AST_PBX_KEEPALIVE) && ast_exists_extension(c, c->context, "h", 1, c->cid.cid_num)) {
+ c->exten[0] = 'h';
+ c->exten[1] = '\0';
+ c->priority = 1;
+ while(ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
+ if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num))) {
+ /* 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);
+ break;
+ }
+ c->priority++;
+ }
+ }
+ ast_set2_flag(c, autoloopflag, AST_FLAG_IN_AUTOLOOP);
+
+ pbx_destroy(c->pbx);
+ c->pbx = NULL;
+ if (res != AST_PBX_KEEPALIVE)
+ ast_hangup(c);
+ return 0;
+}
+
+/* Returns 0 on success, non-zero if call limit was reached */
+static int increase_call_count(const struct ast_channel *c)
+{
+ int failed = 0;
+ double curloadavg;
+ ast_mutex_lock(&maxcalllock);
+ if (option_maxcalls) {
+ if (countcalls >= option_maxcalls) {
+ ast_log(LOG_NOTICE, "Maximum call limit of %d calls exceeded by '%s'!\n", option_maxcalls, c->name);
+ failed = -1;
+ }
+ }
+ if (option_maxload) {
+ getloadavg(&curloadavg, 1);
+ if (curloadavg >= option_maxload) {
+ ast_log(LOG_NOTICE, "Maximum loadavg limit of %lf load exceeded by '%s' (currently %f)!\n", option_maxload, c->name, curloadavg);
+ failed = -1;
+ }
+ }
+ if (!failed)
+ countcalls++;
+ ast_mutex_unlock(&maxcalllock);
+
+ return failed;
+}
+
+static void decrease_call_count(void)
+{
+ ast_mutex_lock(&maxcalllock);
+ if (countcalls > 0)
+ countcalls--;
+ ast_mutex_unlock(&maxcalllock);
+}
+
+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.
+ */
+ /* NOTE:
+ The launcher of this function _MUST_ increment 'countcalls'
+ before invoking the function; it will be decremented when the
+ PBX has finished running on the channel
+ */
+ struct ast_channel *c = data;
+
+ __ast_pbx_run(c);
+ decrease_call_count();
+
+ pthread_exit(NULL);
+
+ return NULL;
+}
+
+enum ast_pbx_result ast_pbx_start(struct ast_channel *c)
+{
+ pthread_t t;
+ pthread_attr_t attr;
+
+ if (!c) {
+ ast_log(LOG_WARNING, "Asked to start thread on NULL channel?\n");
+ return AST_PBX_FAILED;
+ }
+
+ if (increase_call_count(c))
+ return AST_PBX_CALL_LIMIT;
+
+ /* Start a new thread, and get something handling this channel. */
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (ast_pthread_create(&t, &attr, pbx_thread, c)) {
+ ast_log(LOG_WARNING, "Failed to create new channel thread\n");
+ return AST_PBX_FAILED;
+ }
+
+ return AST_PBX_SUCCESS;
+}
+
+enum ast_pbx_result ast_pbx_run(struct ast_channel *c)
+{
+ enum ast_pbx_result res = AST_PBX_SUCCESS;
+
+ if (increase_call_count(c))
+ return AST_PBX_CALL_LIMIT;
+
+ res = __ast_pbx_run(c);
+ decrease_call_count();
+
+ return res;
+}
+
+int ast_active_calls(void)
+{
+ return countcalls;
+}
+
+int pbx_set_autofallthrough(int newval)
+{
+ int oldval;
+ oldval = autofallthrough;
+ if (oldval != newval)
+ autofallthrough = newval;
+ return oldval;
+}
+
+/*
+ * This function locks contexts list by &conlist, search for the right context
+ * structure, leave context list locked and call ast_context_remove_include2
+ * which removes include, unlock contexts list and return ...
+ */
+int ast_context_remove_include(const char *context, const char *include, const char *registrar)
+{
+ struct ast_context *c;
+
+ if (ast_lock_contexts()) return -1;
+
+ /* walk contexts and search for the right one ...*/
+ c = ast_walk_contexts(NULL);
+ while (c) {
+ /* we found one ... */
+ if (!strcmp(ast_get_context_name(c), context)) {
+ int ret;
+ /* remove include from this context ... */
+ ret = ast_context_remove_include2(c, include, registrar);
+
+ ast_unlock_contexts();
+
+ /* ... return results */
+ return ret;
+ }
+ c = ast_walk_contexts(c);
+ }
+
+ /* we can't find the right one context */
+ ast_unlock_contexts();
+ return -1;
+}
+
+/*
+ * When we call this function, &conlock lock must be locked, because when
+ * we giving *con argument, some process can remove/change this context
+ * and after that there can be segfault.
+ *
+ * This function locks given context, removes include, unlock context and
+ * return.
+ */
+int ast_context_remove_include2(struct ast_context *con, const char *include, const char *registrar)
+{
+ struct ast_include *i, *pi = NULL;
+
+ if (ast_mutex_lock(&con->lock)) return -1;
+
+ /* walk includes */
+ i = con->includes;
+ while (i) {
+ /* find our include */
+ if (!strcmp(i->name, include) &&
+ (!registrar || !strcmp(i->registrar, registrar))) {
+ /* remove from list */
+ if (pi)
+ pi->next = i->next;
+ else
+ con->includes = i->next;
+ /* free include and return */
+ free(i);
+ ast_mutex_unlock(&con->lock);
+ return 0;
+ }
+ pi = i;
+ i = i->next;
+ }
+
+ /* we can't find the right include */
+ ast_mutex_unlock(&con->lock);
+ return -1;
+}
+
+/*!
+ * \note This function locks contexts list by &conlist, search for the rigt context
+ * structure, leave context list locked and call ast_context_remove_switch2
+ * which removes switch, unlock contexts list and return ...
+ */
+int ast_context_remove_switch(const char *context, const char *sw, const char *data, const char *registrar)
+{
+ struct ast_context *c;
+
+ if (ast_lock_contexts()) return -1;
+
+ /* walk contexts and search for the right one ...*/
+ c = ast_walk_contexts(NULL);
+ while (c) {
+ /* we found one ... */
+ if (!strcmp(ast_get_context_name(c), context)) {
+ int ret;
+ /* remove switch from this context ... */
+ ret = ast_context_remove_switch2(c, sw, data, registrar);
+
+ ast_unlock_contexts();
+
+ /* ... return results */
+ return ret;
+ }
+ c = ast_walk_contexts(c);
+ }
+
+ /* we can't find the right one context */
+ ast_unlock_contexts();
+ return -1;
+}
+
+/*!
+ * \brief This function locks given context, removes switch, unlock context and
+ * return.
+ * \note When we call this function, &conlock lock must be locked, because when
+ * we giving *con argument, some process can remove/change this context
+ * and after that there can be segfault.
+ *
+ */
+int ast_context_remove_switch2(struct ast_context *con, const char *sw, const char *data, const char *registrar)
+{
+ struct ast_sw *i, *pi = NULL;
+
+ if (ast_mutex_lock(&con->lock)) return -1;
+
+ /* walk switchs */
+ i = con->alts;
+ while (i) {
+ /* find our switch */
+ if (!strcmp(i->name, sw) && !strcmp(i->data, data) &&
+ (!registrar || !strcmp(i->registrar, registrar))) {
+ /* remove from list */
+ if (pi)
+ pi->next = i->next;
+ else
+ con->alts = i->next;
+ /* free switch and return */
+ free(i);
+ ast_mutex_unlock(&con->lock);
+ return 0;
+ }
+ pi = i;
+ i = i->next;
+ }
+
+ /* we can't find the right switch */
+ ast_mutex_unlock(&con->lock);
+ return -1;
+}
+
+/*
+ * \note This functions lock contexts list, search for the right context,
+ * call ast_context_remove_extension2, unlock contexts list and return.
+ * In this function we are using
+ */
+int ast_context_remove_extension(const char *context, const char *extension, int priority, const char *registrar)
+{
+ struct ast_context *c;
+
+ if (ast_lock_contexts()) return -1;
+
+ /* walk contexts ... */
+ c = ast_walk_contexts(NULL);
+ while (c) {
+ /* ... search for the right one ... */
+ if (!strcmp(ast_get_context_name(c), context)) {
+ /* ... remove extension ... */
+ int ret = ast_context_remove_extension2(c, extension, priority,
+ registrar);
+ /* ... unlock contexts list and return */
+ ast_unlock_contexts();
+ return ret;
+ }
+ c = ast_walk_contexts(c);
+ }
+
+ /* we can't find the right context */
+ ast_unlock_contexts();
+ return -1;
+}
+
+/*!
+ * \brief This functionc locks given context, search for the right extension and
+ * fires out all peer in this extensions with given priority. If priority
+ * is set to 0, all peers are removed. After that, unlock context and
+ * return.
+ * \note When do you want to call this function, make sure that &conlock is locked,
+ * because some process can handle with your *con context before you lock
+ * it.
+ *
+ */
+int ast_context_remove_extension2(struct ast_context *con, const char *extension, int priority, const char *registrar)
+{
+ struct ast_exten *exten, *prev_exten = NULL;
+
+ if (ast_mutex_lock(&con->lock)) return -1;
+
+ /* go through all extensions in context and search the right one ... */
+ exten = con->root;
+ while (exten) {
+
+ /* look for right extension */
+ if (!strcmp(exten->exten, extension) &&
+ (!registrar || !strcmp(exten->registrar, registrar))) {
+ struct ast_exten *peer;
+
+ /* should we free all peers in this extension? (priority == 0)? */
+ if (priority == 0) {
+ /* remove this extension from context list */
+ if (prev_exten)
+ prev_exten->next = exten->next;
+ else
+ con->root = exten->next;
+
+ /* fire out all peers */
+ peer = exten;
+ while (peer) {
+ exten = peer->peer;
+
+ if (!peer->priority==PRIORITY_HINT)
+ ast_remove_hint(peer);
+
+ peer->datad(peer->data);
+ free(peer);
+
+ peer = exten;
+ }
+
+ ast_mutex_unlock(&con->lock);
+ return 0;
+ } else {
+ /* remove only extension with exten->priority == priority */
+ struct ast_exten *previous_peer = NULL;
+
+ peer = exten;
+ while (peer) {
+ /* is this our extension? */
+ if (peer->priority == priority &&
+ (!registrar || !strcmp(peer->registrar, registrar) )) {
+ /* we are first priority extension? */
+ if (!previous_peer) {
+ /* exists previous extension here? */
+ if (prev_exten) {
+ /* yes, so we must change next pointer in
+ * previous connection to next peer
+ */
+ if (peer->peer) {
+ prev_exten->next = peer->peer;
+ peer->peer->next = exten->next;
+ } else
+ prev_exten->next = exten->next;
+ } else {
+ /* no previous extension, we are first
+ * extension, so change con->root ...
+ */
+ if (peer->peer)
+ con->root = peer->peer;
+ else
+ con->root = exten->next;
+ }
+ } else {
+ /* we are not first priority in extension */
+ previous_peer->peer = peer->peer;
+ }
+
+ /* now, free whole priority extension */
+ if (peer->priority==PRIORITY_HINT)
+ ast_remove_hint(peer);
+ peer->datad(peer->data);
+ free(peer);
+
+ ast_mutex_unlock(&con->lock);
+ return 0;
+ } else {
+ /* this is not right extension, skip to next peer */
+ previous_peer = peer;
+ peer = peer->peer;
+ }
+ }
+
+ ast_mutex_unlock(&con->lock);
+ return -1;
+ }
+ }
+
+ prev_exten = exten;
+ exten = exten->next;
+ }
+
+ /* we can't find right extension */
+ ast_mutex_unlock(&con->lock);
+ return -1;
+}
+
+
+/*! \brief Dynamically register a new dial plan application */
+int ast_register_application(const char *app, int (*execute)(struct ast_channel *, void *), const char *synopsis, const char *description)
+{
+ struct ast_app *tmp, *prev, *cur;
+ char tmps[80];
+ int length;
+ length = sizeof(struct ast_app);
+ length += strlen(app) + 1;
+ if (ast_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);
+ ast_mutex_unlock(&applock);
+ return -1;
+ }
+ tmp = tmp->next;
+ }
+ tmp = malloc(length);
+ if (tmp) {
+ memset(tmp, 0, length);
+ strcpy(tmp->name, app);
+ tmp->execute = execute;
+ tmp->synopsis = synopsis;
+ tmp->description = description;
+ /* Store in alphabetical order */
+ cur = apps;
+ prev = NULL;
+ while(cur) {
+ if (strcasecmp(tmp->name, cur->name) < 0)
+ break;
+ prev = cur;
+ cur = cur->next;
+ }
+ if (prev) {
+ tmp->next = prev->next;
+ prev->next = tmp;
+ } else {
+ tmp->next = apps;
+ apps = tmp;
+ }
+ } else {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ ast_mutex_unlock(&applock);
+ return -1;
+ }
+ if (option_verbose > 1)
+ ast_verbose( VERBOSE_PREFIX_2 "Registered application '%s'\n", term_color(tmps, tmp->name, COLOR_BRCYAN, 0, sizeof(tmps)));
+ ast_mutex_unlock(&applock);
+ return 0;
+}
+
+int ast_register_switch(struct ast_switch *sw)
+{
+ struct ast_switch *tmp, *prev=NULL;
+ if (ast_mutex_lock(&switchlock)) {
+ ast_log(LOG_ERROR, "Unable to lock switch lock\n");
+ return -1;
+ }
+ tmp = switches;
+ while(tmp) {
+ if (!strcasecmp(tmp->name, sw->name))
+ break;
+ prev = tmp;
+ tmp = tmp->next;
+ }
+ if (tmp) {
+ ast_mutex_unlock(&switchlock);
+ ast_log(LOG_WARNING, "Switch '%s' already found\n", sw->name);
+ return -1;
+ }
+ sw->next = NULL;
+ if (prev)
+ prev->next = sw;
+ else
+ switches = sw;
+ ast_mutex_unlock(&switchlock);
+ return 0;
+}
+
+void ast_unregister_switch(struct ast_switch *sw)
+{
+ struct ast_switch *tmp, *prev=NULL;
+ if (ast_mutex_lock(&switchlock)) {
+ ast_log(LOG_ERROR, "Unable to lock switch lock\n");
+ return;
+ }
+ tmp = switches;
+ while(tmp) {
+ if (tmp == sw) {
+ if (prev)
+ prev->next = tmp->next;
+ else
+ switches = tmp->next;
+ tmp->next = NULL;
+ break;
+ }
+ prev = tmp;
+ tmp = tmp->next;
+ }
+ ast_mutex_unlock(&switchlock);
+}
+
+/*
+ * Help for CLI commands ...
+ */
+static char show_application_help[] =
+"Usage: show application <application> [<application> [<application> [...]]]\n"
+" Describes a particular application.\n";
+
+static char show_functions_help[] =
+"Usage: show functions\n"
+" List builtin functions accessable as $(function args)\n";
+
+static char show_function_help[] =
+"Usage: show function <function>\n"
+" Describe a particular dialplan function.\n";
+
+static char show_applications_help[] =
+"Usage: show applications [{like|describing} <text>]\n"
+" List applications which are currently available.\n"
+" If 'like', <text> will be a substring of the app name\n"
+" If 'describing', <text> will be a substring of the description\n";
+
+static char show_dialplan_help[] =
+"Usage: show dialplan [exten@][context]\n"
+" Show dialplan\n";
+
+static char show_switches_help[] =
+"Usage: show switches\n"
+" Show registered switches\n";
+
+static char show_hints_help[] =
+"Usage: show hints\n"
+" Show registered hints\n";
+
+
+/*
+ * IMPLEMENTATION OF CLI FUNCTIONS IS IN THE SAME ORDER AS COMMANDS HELPS
+ *
+ */
+
+/*
+ * \brief 'show application' CLI command implementation functions ...
+ */
+
+/*
+ * There is a possibility to show informations about more than one
+ * application at one time. You can type 'show application Dial Echo' and
+ * you will see informations about these two applications ...
+ */
+static char *complete_show_application(char *line, char *word,
+ int pos, int state)
+{
+ struct ast_app *a;
+ int which = 0;
+
+ /* try to lock applications list ... */
+ if (ast_mutex_lock(&applock)) {
+ ast_log(LOG_ERROR, "Unable to lock application list\n");
+ return NULL;
+ }
+
+ /* ... walk all applications ... */
+ a = apps;
+ while (a) {
+ /* ... check if word matches this application ... */
+ if (!strncasecmp(word, a->name, strlen(word))) {
+ /* ... if this is right app serve it ... */
+ if (++which > state) {
+ char *ret = strdup(a->name);
+ ast_mutex_unlock(&applock);
+ return ret;
+ }
+ }
+ a = a->next;
+ }
+
+ /* no application match */
+ ast_mutex_unlock(&applock);
+ return NULL;
+}
+
+static int handle_show_application(int fd, int argc, char *argv[])
+{
+ struct ast_app *a;
+ int app, no_registered_app = 1;
+
+ if (argc < 3) return RESULT_SHOWUSAGE;
+
+ /* try to lock applications list ... */
+ if (ast_mutex_lock(&applock)) {
+ ast_log(LOG_ERROR, "Unable to lock application list\n");
+ return -1;
+ }
+
+ /* ... go through all applications ... */
+ a = apps;
+ while (a) {
+ /* ... compare this application name with all arguments given
+ * to 'show application' command ... */
+ for (app = 2; app < argc; app++) {
+ if (!strcasecmp(a->name, argv[app])) {
+ /* Maximum number of characters added by terminal coloring is 22 */
+ char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
+ char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
+ int synopsis_size, description_size;
+
+ no_registered_app = 0;
+
+ if (a->synopsis)
+ synopsis_size = strlen(a->synopsis) + 23;
+ else
+ synopsis_size = strlen("Not available") + 23;
+ synopsis = alloca(synopsis_size);
+
+ if (a->description)
+ description_size = strlen(a->description) + 23;
+ else
+ description_size = strlen("Not available") + 23;
+ description = alloca(description_size);
+
+ if (synopsis && description) {
+ snprintf(info, 64 + AST_MAX_APP, "\n -= Info about application '%s' =- \n\n", a->name);
+ term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
+ term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
+ term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
+ term_color(synopsis,
+ a->synopsis ? a->synopsis : "Not available",
+ COLOR_CYAN, 0, synopsis_size);
+ term_color(description,
+ a->description ? a->description : "Not available",
+ COLOR_CYAN, 0, description_size);
+
+ ast_cli(fd,"%s%s%s\n\n%s%s\n", infotitle, syntitle, synopsis, destitle, description);
+ } else {
+ /* ... one of our applications, show info ...*/
+ ast_cli(fd,"\n -= Info about application '%s' =- \n\n"
+ "[Synopsis]\n %s\n\n"
+ "[Description]\n%s\n",
+ a->name,
+ a->synopsis ? a->synopsis : "Not available",
+ a->description ? a->description : "Not available");
+ }
+ }
+ }
+ a = a->next;
+ }
+
+ ast_mutex_unlock(&applock);
+
+ /* we found at least one app? no? */
+ if (no_registered_app) {
+ ast_cli(fd, "Your application(s) is (are) not registered\n");
+ return RESULT_FAILURE;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+/*! \brief handle_show_hints: CLI support for listing registred dial plan hints */
+static int handle_show_hints(int fd, int argc, char *argv[])
+{
+ struct ast_hint *hint;
+ int num = 0;
+ int watchers;
+ struct ast_state_cb *watcher;
+
+ if (!hints) {
+ ast_cli(fd, "There are no registered dialplan hints\n");
+ return RESULT_SUCCESS;
+ }
+ /* ... we have hints ... */
+ ast_cli(fd, "\n -= Registered Asterisk Dial Plan Hints =-\n");
+ if (ast_mutex_lock(&hintlock)) {
+ ast_log(LOG_ERROR, "Unable to lock hints\n");
+ return -1;
+ }
+ hint = hints;
+ while (hint) {
+ watchers = 0;
+ for (watcher = hint->callbacks; watcher; watcher = watcher->next)
+ watchers++;
+ ast_cli(fd, " %-20.20s: %-20.20s State:%-15.15s Watchers %2d\n",
+ ast_get_extension_name(hint->exten), ast_get_extension_app(hint->exten),
+ ast_extension_state2str(hint->laststate), watchers);
+ num++;
+ hint = hint->next;
+ }
+ ast_cli(fd, "----------------\n");
+ ast_cli(fd, "- %d hints registered\n", num);
+ ast_mutex_unlock(&hintlock);
+ return RESULT_SUCCESS;
+}
+
+/*! \brief handle_show_switches: CLI support for listing registred dial plan switches */
+static int handle_show_switches(int fd, int argc, char *argv[])
+{
+ struct ast_switch *sw;
+ if (!switches) {
+ ast_cli(fd, "There are no registered alternative switches\n");
+ return RESULT_SUCCESS;
+ }
+ /* ... we have applications ... */
+ ast_cli(fd, "\n -= Registered Asterisk Alternative Switches =-\n");
+ if (ast_mutex_lock(&switchlock)) {
+ ast_log(LOG_ERROR, "Unable to lock switches\n");
+ return -1;
+ }
+ sw = switches;
+ while (sw) {
+ ast_cli(fd, "%s: %s\n", sw->name, sw->description);
+ sw = sw->next;
+ }
+ ast_mutex_unlock(&switchlock);
+ return RESULT_SUCCESS;
+}
+
+/*
+ * 'show applications' CLI command implementation functions ...
+ */
+static int handle_show_applications(int fd, int argc, char *argv[])
+{
+ struct ast_app *a;
+ int like=0, describing=0;
+ int total_match = 0; /* Number of matches in like clause */
+ int total_apps = 0; /* Number of apps registered */
+
+ /* try to lock applications list ... */
+ if (ast_mutex_lock(&applock)) {
+ ast_log(LOG_ERROR, "Unable to lock application list\n");
+ return -1;
+ }
+
+ /* ... have we got at least one application (first)? no? */
+ if (!apps) {
+ ast_cli(fd, "There are no registered applications\n");
+ ast_mutex_unlock(&applock);
+ return -1;
+ }
+
+ /* show applications like <keyword> */
+ if ((argc == 4) && (!strcmp(argv[2], "like"))) {
+ like = 1;
+ } else if ((argc > 3) && (!strcmp(argv[2], "describing"))) {
+ describing = 1;
+ }
+
+ /* show applications describing <keyword1> [<keyword2>] [...] */
+ if ((!like) && (!describing)) {
+ ast_cli(fd, " -= Registered Asterisk Applications =-\n");
+ } else {
+ ast_cli(fd, " -= Matching Asterisk Applications =-\n");
+ }
+
+ /* ... go through all applications ... */
+ for (a = apps; a; a = a->next) {
+ /* ... show informations about applications ... */
+ int printapp=0;
+ total_apps++;
+ if (like) {
+ if (strcasestr(a->name, argv[3])) {
+ printapp = 1;
+ total_match++;
+ }
+ } else if (describing) {
+ if (a->description) {
+ /* Match all words on command line */
+ int i;
+ printapp = 1;
+ for (i=3; i<argc; i++) {
+ if (!strcasestr(a->description, argv[i])) {
+ printapp = 0;
+ } else {
+ total_match++;
+ }
+ }
+ }
+ } else {
+ printapp = 1;
+ }
+
+ if (printapp) {
+ ast_cli(fd," %20s: %s\n", a->name, a->synopsis ? a->synopsis : "<Synopsis not available>");
+ }
+ }
+ if ((!like) && (!describing)) {
+ ast_cli(fd, " -= %d Applications Registered =-\n",total_apps);
+ } else {
+ ast_cli(fd, " -= %d Applications Matching =-\n",total_match);
+ }
+
+ /* ... unlock and return */
+ ast_mutex_unlock(&applock);
+
+ return RESULT_SUCCESS;
+}
+
+static char *complete_show_applications(char *line, char *word, int pos, int state)
+{
+ if (pos == 2) {
+ if (ast_strlen_zero(word)) {
+ switch (state) {
+ case 0:
+ return strdup("like");
+ case 1:
+ return strdup("describing");
+ default:
+ return NULL;
+ }
+ } else if (! strncasecmp(word, "like", strlen(word))) {
+ if (state == 0) {
+ return strdup("like");
+ } else {
+ return NULL;
+ }
+ } else if (! strncasecmp(word, "describing", strlen(word))) {
+ if (state == 0) {
+ return strdup("describing");
+ } else {
+ return NULL;
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ * 'show dialplan' CLI command implementation functions ...
+ */
+static char *complete_show_dialplan_context(char *line, char *word, int pos,
+ int state)
+{
+ struct ast_context *c;
+ int which = 0;
+
+ /* we are do completion of [exten@]context on second position only */
+ if (pos != 2) return NULL;
+
+ /* try to lock contexts list ... */
+ if (ast_lock_contexts()) {
+ ast_log(LOG_ERROR, "Unable to lock context list\n");
+ return NULL;
+ }
+
+ /* ... walk through all contexts ... */
+ c = ast_walk_contexts(NULL);
+ while(c) {
+ /* ... word matches context name? yes? ... */
+ if (!strncasecmp(word, ast_get_context_name(c), strlen(word))) {
+ /* ... for serve? ... */
+ if (++which > state) {
+ /* ... yes, serve this context name ... */
+ char *ret = strdup(ast_get_context_name(c));
+ ast_unlock_contexts();
+ return ret;
+ }
+ }
+ c = ast_walk_contexts(c);
+ }
+
+ /* ... unlock and return */
+ ast_unlock_contexts();
+ return NULL;
+}
+
+struct dialplan_counters {
+ int total_context;
+ int total_exten;
+ int total_prio;
+ int context_existence;
+ int extension_existence;
+};
+
+static int show_dialplan_helper(int fd, char *context, char *exten, struct dialplan_counters *dpc, struct ast_include *rinclude, int includecount, char *includes[])
+{
+ struct ast_context *c;
+ int res=0, old_total_exten = dpc->total_exten;
+
+ /* try to lock contexts */
+ if (ast_lock_contexts()) {
+ ast_log(LOG_WARNING, "Failed to lock contexts list\n");
+ return -1;
+ }
+
+ /* walk all contexts ... */
+ for (c = ast_walk_contexts(NULL); c ; c = ast_walk_contexts(c)) {
+ /* show this context? */
+ if (!context ||
+ !strcmp(ast_get_context_name(c), context)) {
+ dpc->context_existence = 1;
+
+ /* try to lock context before walking in ... */
+ if (!ast_lock_context(c)) {
+ struct ast_exten *e;
+ struct ast_include *i;
+ struct ast_ignorepat *ip;
+ struct ast_sw *sw;
+ char buf[256], buf2[256];
+ int context_info_printed = 0;
+
+ /* are we looking for exten too? if yes, we print context
+ * if we our extension only
+ */
+ if (!exten) {
+ dpc->total_context++;
+ ast_cli(fd, "[ Context '%s' created by '%s' ]\n",
+ ast_get_context_name(c), ast_get_context_registrar(c));
+ context_info_printed = 1;
+ }
+
+ /* walk extensions ... */
+ for (e = ast_walk_context_extensions(c, NULL); e; e = ast_walk_context_extensions(c, e)) {
+ struct ast_exten *p;
+ int prio;
+
+ /* looking for extension? is this our extension? */
+ if (exten &&
+ !ast_extension_match(ast_get_extension_name(e), exten))
+ {
+ /* we are looking for extension and it's not our
+ * extension, so skip to next extension */
+ continue;
+ }
+
+ dpc->extension_existence = 1;
+
+ /* may we print context info? */
+ if (!context_info_printed) {
+ dpc->total_context++;
+ if (rinclude) {
+ /* TODO Print more info about rinclude */
+ ast_cli(fd, "[ Included context '%s' created by '%s' ]\n",
+ ast_get_context_name(c),
+ ast_get_context_registrar(c));
+ } else {
+ ast_cli(fd, "[ Context '%s' created by '%s' ]\n",
+ ast_get_context_name(c),
+ ast_get_context_registrar(c));
+ }
+ context_info_printed = 1;
+ }
+ dpc->total_prio++;
+
+ /* write extension name and first peer */
+ bzero(buf, sizeof(buf));
+ snprintf(buf, sizeof(buf), "'%s' =>",
+ ast_get_extension_name(e));
+
+ prio = ast_get_extension_priority(e);
+ if (prio == PRIORITY_HINT) {
+ snprintf(buf2, sizeof(buf2),
+ "hint: %s",
+ ast_get_extension_app(e));
+ } else {
+ snprintf(buf2, sizeof(buf2),
+ "%d. %s(%s)",
+ prio,
+ ast_get_extension_app(e),
+ (char *)ast_get_extension_app_data(e));
+ }
+
+ ast_cli(fd, " %-17s %-45s [%s]\n", buf, buf2,
+ ast_get_extension_registrar(e));
+
+ dpc->total_exten++;
+ /* walk next extension peers */
+ for (p=ast_walk_extension_priorities(e, e); p; p=ast_walk_extension_priorities(e, p)) {
+ dpc->total_prio++;
+ bzero((void *)buf2, sizeof(buf2));
+ bzero((void *)buf, sizeof(buf));
+ if (ast_get_extension_label(p))
+ snprintf(buf, sizeof(buf), " [%s]", ast_get_extension_label(p));
+ prio = ast_get_extension_priority(p);
+ if (prio == PRIORITY_HINT) {
+ snprintf(buf2, sizeof(buf2),
+ "hint: %s",
+ ast_get_extension_app(p));
+ } else {
+ snprintf(buf2, sizeof(buf2),
+ "%d. %s(%s)",
+ prio,
+ ast_get_extension_app(p),
+ (char *)ast_get_extension_app_data(p));
+ }
+
+ ast_cli(fd," %-17s %-45s [%s]\n",
+ buf, buf2,
+ ast_get_extension_registrar(p));
+ }
+ }
+
+ /* walk included and write info ... */
+ for (i = ast_walk_context_includes(c, NULL); i; i = ast_walk_context_includes(c, i)) {
+ bzero(buf, sizeof(buf));
+ snprintf(buf, sizeof(buf), "'%s'",
+ ast_get_include_name(i));
+ if (exten) {
+ /* Check all includes for the requested extension */
+ if (includecount >= AST_PBX_MAX_STACK) {
+ ast_log(LOG_NOTICE, "Maximum include depth exceeded!\n");
+ } else {
+ int dupe=0;
+ int x;
+ for (x=0;x<includecount;x++) {
+ if (!strcasecmp(includes[x], ast_get_include_name(i))) {
+ dupe++;
+ break;
+ }
+ }
+ if (!dupe) {
+ includes[includecount] = (char *)ast_get_include_name(i);
+ show_dialplan_helper(fd, (char *)ast_get_include_name(i), exten, dpc, i, includecount + 1, includes);
+ } else {
+ ast_log(LOG_WARNING, "Avoiding circular include of %s within %s\n", ast_get_include_name(i), context);
+ }
+ }
+ } else {
+ ast_cli(fd, " Include => %-45s [%s]\n",
+ buf, ast_get_include_registrar(i));
+ }
+ }
+
+ /* walk ignore patterns and write info ... */
+ for (ip=ast_walk_context_ignorepats(c, NULL); ip; ip=ast_walk_context_ignorepats(c, ip)) {
+ const char *ipname = ast_get_ignorepat_name(ip);
+ char ignorepat[AST_MAX_EXTENSION];
+ snprintf(buf, sizeof(buf), "'%s'", ipname);
+ snprintf(ignorepat, sizeof(ignorepat), "_%s.", ipname);
+ if ((!exten) || ast_extension_match(ignorepat, exten)) {
+ ast_cli(fd, " Ignore pattern => %-45s [%s]\n",
+ buf, ast_get_ignorepat_registrar(ip));
+ }
+ }
+ if (!rinclude) {
+ for (sw = ast_walk_context_switches(c, NULL); sw; sw = ast_walk_context_switches(c, sw)) {
+ snprintf(buf, sizeof(buf), "'%s/%s'",
+ ast_get_switch_name(sw),
+ ast_get_switch_data(sw));
+ ast_cli(fd, " Alt. Switch => %-45s [%s]\n",
+ buf, ast_get_switch_registrar(sw));
+ }
+ }
+
+ ast_unlock_context(c);
+
+ /* if we print something in context, make an empty line */
+ if (context_info_printed) ast_cli(fd, "\r\n");
+ }
+ }
+ }
+ ast_unlock_contexts();
+
+ if (dpc->total_exten == old_total_exten) {
+ /* Nothing new under the sun */
+ return -1;
+ } else {
+ return res;
+ }
+}
+
+static int handle_show_dialplan(int fd, int argc, char *argv[])
+{
+ char *exten = NULL, *context = NULL;
+ /* Variables used for different counters */
+ struct dialplan_counters counters;
+ char *incstack[AST_PBX_MAX_STACK];
+ memset(&counters, 0, sizeof(counters));
+
+ if (argc != 2 && argc != 3)
+ return RESULT_SHOWUSAGE;
+
+ /* we obtain [exten@]context? if yes, split them ... */
+ if (argc == 3) {
+ char *splitter = ast_strdupa(argv[2]);
+ /* is there a '@' character? */
+ if (splitter && strchr(argv[2], '@')) {
+ /* yes, split into exten & context ... */
+ exten = strsep(&splitter, "@");
+ context = splitter;
+
+ /* check for length and change to NULL if ast_strlen_zero() */
+ if (ast_strlen_zero(exten))
+ exten = NULL;
+ if (ast_strlen_zero(context))
+ context = NULL;
+ show_dialplan_helper(fd, context, exten, &counters, NULL, 0, incstack);
+ } else {
+ /* no '@' char, only context given */
+ context = argv[2];
+ if (ast_strlen_zero(context))
+ context = NULL;
+ show_dialplan_helper(fd, context, exten, &counters, NULL, 0, incstack);
+ }
+ } else {
+ /* Show complete dial plan */
+ show_dialplan_helper(fd, NULL, NULL, &counters, NULL, 0, incstack);
+ }
+
+ /* check for input failure and throw some error messages */
+ if (context && !counters.context_existence) {
+ ast_cli(fd, "There is no existence of '%s' context\n", context);
+ return RESULT_FAILURE;
+ }
+
+ if (exten && !counters.extension_existence) {
+ if (context)
+ ast_cli(fd, "There is no existence of %s@%s extension\n",
+ exten, context);
+ else
+ ast_cli(fd,
+ "There is no existence of '%s' extension in all contexts\n",
+ exten);
+ return RESULT_FAILURE;
+ }
+
+ ast_cli(fd,"-= %d %s (%d %s) in %d %s. =-\n",
+ counters.total_exten, counters.total_exten == 1 ? "extension" : "extensions",
+ counters.total_prio, counters.total_prio == 1 ? "priority" : "priorities",
+ counters.total_context, counters.total_context == 1 ? "context" : "contexts");
+
+ /* everything ok */
+ return RESULT_SUCCESS;
+}
+
+/*
+ * CLI entries for upper commands ...
+ */
+static struct ast_cli_entry pbx_cli[] = {
+ { { "show", "applications", NULL }, handle_show_applications,
+ "Shows registered dialplan applications", show_applications_help, complete_show_applications },
+ { { "show", "functions", NULL }, handle_show_functions,
+ "Shows registered dialplan functions", show_functions_help },
+ { { "show" , "function", NULL }, handle_show_function,
+ "Describe a specific dialplan function", show_function_help, complete_show_function },
+ { { "show", "application", NULL }, handle_show_application,
+ "Describe a specific dialplan application", show_application_help, complete_show_application },
+ { { "show", "dialplan", NULL }, handle_show_dialplan,
+ "Show dialplan", show_dialplan_help, complete_show_dialplan_context },
+ { { "show", "switches", NULL }, handle_show_switches,
+ "Show alternative switches", show_switches_help },
+ { { "show", "hints", NULL }, handle_show_hints,
+ "Show dialplan hints", show_hints_help },
+};
+
+int ast_unregister_application(const char *app)
+{
+ struct ast_app *tmp, *tmpl = NULL;
+ if (ast_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);
+ free(tmp);
+ ast_mutex_unlock(&applock);
+ return 0;
+ }
+ tmpl = tmp;
+ tmp = tmp->next;
+ }
+ ast_mutex_unlock(&applock);
+ return -1;
+}
+
+struct ast_context *ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar)
+{
+ struct ast_context *tmp, **local_contexts;
+ int length;
+ length = sizeof(struct ast_context);
+ length += strlen(name) + 1;
+ if (!extcontexts) {
+ local_contexts = &contexts;
+ ast_mutex_lock(&conlock);
+ } else
+ local_contexts = extcontexts;
+
+ tmp = *local_contexts;
+ while(tmp) {
+ if (!strcasecmp(tmp->name, name)) {
+ ast_mutex_unlock(&conlock);
+ ast_log(LOG_WARNING, "Tried to register context '%s', already in use\n", name);
+ if (!extcontexts)
+ ast_mutex_unlock(&conlock);
+ return NULL;
+ }
+ tmp = tmp->next;
+ }
+ tmp = malloc(length);
+ if (tmp) {
+ memset(tmp, 0, length);
+ ast_mutex_init(&tmp->lock);
+ strcpy(tmp->name, name);
+ tmp->root = NULL;
+ tmp->registrar = registrar;
+ tmp->next = *local_contexts;
+ tmp->includes = NULL;
+ tmp->ignorepats = NULL;
+ *local_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_ERROR, "Out of memory\n");
+
+ if (!extcontexts)
+ ast_mutex_unlock(&conlock);
+ return tmp;
+}
+
+void __ast_context_destroy(struct ast_context *con, const char *registrar);
+
+struct store_hint {
+ char *context;
+ char *exten;
+ struct ast_state_cb *callbacks;
+ int laststate;
+ AST_LIST_ENTRY(store_hint) list;
+ char data[1];
+};
+
+AST_LIST_HEAD(store_hints, store_hint);
+
+void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar)
+{
+ struct ast_context *tmp, *lasttmp = NULL;
+ struct store_hints store;
+ struct store_hint *this;
+ struct ast_hint *hint;
+ struct ast_exten *exten;
+ int length;
+ struct ast_state_cb *thiscb, *prevcb;
+
+ /* preserve all watchers for hints associated with this registrar */
+ AST_LIST_HEAD_INIT(&store);
+ ast_mutex_lock(&hintlock);
+ for (hint = hints; hint; hint = hint->next) {
+ if (hint->callbacks && !strcmp(registrar, hint->exten->parent->registrar)) {
+ length = strlen(hint->exten->exten) + strlen(hint->exten->parent->name) + 2 + sizeof(*this);
+ this = calloc(1, length);
+ if (!this) {
+ ast_log(LOG_WARNING, "Could not allocate memory to preserve hint\n");
+ continue;
+ }
+ this->callbacks = hint->callbacks;
+ hint->callbacks = NULL;
+ this->laststate = hint->laststate;
+ this->context = this->data;
+ strcpy(this->data, hint->exten->parent->name);
+ this->exten = this->data + strlen(this->context) + 1;
+ strcpy(this->exten, hint->exten->exten);
+ AST_LIST_INSERT_HEAD(&store, this, list);
+ }
+ }
+ ast_mutex_unlock(&hintlock);
+
+ tmp = *extcontexts;
+ ast_mutex_lock(&conlock);
+ if (registrar) {
+ __ast_context_destroy(NULL,registrar);
+ while (tmp) {
+ lasttmp = tmp;
+ tmp = tmp->next;
+ }
+ } else {
+ while (tmp) {
+ __ast_context_destroy(tmp,tmp->registrar);
+ lasttmp = tmp;
+ tmp = tmp->next;
+ }
+ }
+ if (lasttmp) {
+ lasttmp->next = contexts;
+ contexts = *extcontexts;
+ *extcontexts = NULL;
+ } else
+ ast_log(LOG_WARNING, "Requested contexts didn't get merged\n");
+ ast_mutex_unlock(&conlock);
+
+ /* restore the watchers for hints that can be found; notify those that
+ cannot be restored
+ */
+ while ((this = AST_LIST_REMOVE_HEAD(&store, list))) {
+ exten = ast_hint_extension(NULL, this->context, this->exten);
+ /* Find the hint in the list of hints */
+ ast_mutex_lock(&hintlock);
+ for (hint = hints; hint; hint = hint->next) {
+ if (hint->exten == exten)
+ break;
+ }
+ if (!exten || !hint) {
+ /* this hint has been removed, notify the watchers */
+ prevcb = NULL;
+ thiscb = this->callbacks;
+ while (thiscb) {
+ prevcb = thiscb;
+ thiscb = thiscb->next;
+ prevcb->callback(this->context, this->exten, AST_EXTENSION_REMOVED, prevcb->data);
+ free(prevcb);
+ }
+ } else {
+ thiscb = this->callbacks;
+ while (thiscb->next)
+ thiscb = thiscb->next;
+ thiscb->next = hint->callbacks;
+ hint->callbacks = this->callbacks;
+ hint->laststate = this->laststate;
+ }
+ ast_mutex_unlock(&hintlock);
+ free(this);
+ }
+
+ return;
+}
+
+/*
+ * errno values
+ * EBUSY - can't lock
+ * ENOENT - no existence of context
+ */
+int ast_context_add_include(const char *context, const char *include, const char *registrar)
+{
+ struct ast_context *c;
+
+ if (ast_lock_contexts()) {
+ errno = EBUSY;
+ return -1;
+ }
+
+ /* walk contexts ... */
+ c = ast_walk_contexts(NULL);
+ while (c) {
+ /* ... search for the right one ... */
+ if (!strcmp(ast_get_context_name(c), context)) {
+ int ret = ast_context_add_include2(c, include, registrar);
+ /* ... unlock contexts list and return */
+ ast_unlock_contexts();
+ return ret;
+ }
+ c = ast_walk_contexts(c);
+ }
+
+ /* we can't find the right context */
+ ast_unlock_contexts();
+ errno = ENOENT;
+ return -1;
+}
+
+#define FIND_NEXT \
+do { \
+ c = info; \
+ while(*c && (*c != '|')) c++; \
+ if (*c) { *c = '\0'; c++; } else c = NULL; \
+} while(0)
+
+static void get_timerange(struct ast_timing *i, char *times)
+{
+ char *e;
+ int x;
+ int s1, s2;
+ int e1, e2;
+ /* int cth, ctm; */
+
+ /* start disabling all times, fill the fields with 0's, as they may contain garbage */
+ memset(i->minmask, 0, sizeof(i->minmask));
+
+ /* Star is all times */
+ if (ast_strlen_zero(times) || !strcmp(times, "*")) {
+ for (x=0; x<24; x++)
+ i->minmask[x] = (1 << 30) - 1;
+ return;
+ }
+ /* Otherwise expect a range */
+ e = strchr(times, '-');
+ if (!e) {
+ ast_log(LOG_WARNING, "Time range is not valid. Assuming no restrictions based on time.\n");
+ return;
+ }
+ *e = '\0';
+ e++;
+ while(*e && !isdigit(*e))
+ e++;
+ if (!*e) {
+ ast_log(LOG_WARNING, "Invalid time range. Assuming no restrictions based on time.\n");
+ return;
+ }
+ if (sscanf(times, "%d:%d", &s1, &s2) != 2) {
+ ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", times);
+ return;
+ }
+ if (sscanf(e, "%d:%d", &e1, &e2) != 2) {
+ ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", e);
+ return;
+ }
+
+#if 1
+ s1 = s1 * 30 + s2/2;
+ if ((s1 < 0) || (s1 >= 24*30)) {
+ ast_log(LOG_WARNING, "%s isn't a valid start time. Assuming no time.\n", times);
+ return;
+ }
+ e1 = e1 * 30 + e2/2;
+ if ((e1 < 0) || (e1 >= 24*30)) {
+ ast_log(LOG_WARNING, "%s isn't a valid end time. Assuming no time.\n", e);
+ return;
+ }
+ /* Go through the time and enable each appropriate bit */
+ for (x=s1;x != e1;x = (x + 1) % (24 * 30)) {
+ i->minmask[x/30] |= (1 << (x % 30));
+ }
+ /* Do the last one */
+ i->minmask[x/30] |= (1 << (x % 30));
+#else
+ for (cth=0; cth<24; cth++) {
+ /* Initialize masks to blank */
+ i->minmask[cth] = 0;
+ for (ctm=0; ctm<30; ctm++) {
+ if (
+ /* First hour with more than one hour */
+ (((cth == s1) && (ctm >= s2)) &&
+ ((cth < e1)))
+ /* Only one hour */
+ || (((cth == s1) && (ctm >= s2)) &&
+ ((cth == e1) && (ctm <= e2)))
+ /* In between first and last hours (more than 2 hours) */
+ || ((cth > s1) &&
+ (cth < e1))
+ /* Last hour with more than one hour */
+ || ((cth > s1) &&
+ ((cth == e1) && (ctm <= e2)))
+ )
+ i->minmask[cth] |= (1 << (ctm / 2));
+ }
+ }
+#endif
+ /* All done */
+ return;
+}
+
+static char *days[] =
+{
+ "sun",
+ "mon",
+ "tue",
+ "wed",
+ "thu",
+ "fri",
+ "sat",
+};
+
+/*! \brief get_dow: Get day of week */
+static unsigned int get_dow(char *dow)
+{
+ char *c;
+ /* The following line is coincidence, really! */
+ int s, e, x;
+ unsigned int mask;
+
+ /* Check for all days */
+ if (ast_strlen_zero(dow) || !strcmp(dow, "*"))
+ return (1 << 7) - 1;
+ /* Get start and ending days */
+ c = strchr(dow, '-');
+ if (c) {
+ *c = '\0';
+ c++;
+ } else
+ c = NULL;
+ /* Find the start */
+ s = 0;
+ while((s < 7) && strcasecmp(dow, days[s])) s++;
+ if (s >= 7) {
+ ast_log(LOG_WARNING, "Invalid day '%s', assuming none\n", dow);
+ return 0;
+ }
+ if (c) {
+ e = 0;
+ while((e < 7) && strcasecmp(c, days[e])) e++;
+ if (e >= 7) {
+ ast_log(LOG_WARNING, "Invalid day '%s', assuming none\n", c);
+ return 0;
+ }
+ } else
+ e = s;
+ mask = 0;
+ for (x=s; x != e; x = (x + 1) % 7) {
+ mask |= (1 << x);
+ }
+ /* One last one */
+ mask |= (1 << x);
+ return mask;
+}
+
+static unsigned int get_day(char *day)
+{
+ char *c;
+ /* The following line is coincidence, really! */
+ int s, e, x;
+ unsigned int mask;
+
+ /* Check for all days */
+ if (ast_strlen_zero(day) || !strcmp(day, "*")) {
+ mask = (1 << 30) + ((1 << 30) - 1);
+ return mask;
+ }
+ /* Get start and ending days */
+ c = strchr(day, '-');
+ if (c) {
+ *c = '\0';
+ c++;
+ }
+ /* Find the start */
+ if (sscanf(day, "%d", &s) != 1) {
+ ast_log(LOG_WARNING, "Invalid day '%s', assuming none\n", day);
+ return 0;
+ }
+ if ((s < 1) || (s > 31)) {
+ ast_log(LOG_WARNING, "Invalid day '%s', assuming none\n", day);
+ return 0;
+ }
+ s--;
+ if (c) {
+ if (sscanf(c, "%d", &e) != 1) {
+ ast_log(LOG_WARNING, "Invalid day '%s', assuming none\n", c);
+ return 0;
+ }
+ if ((e < 1) || (e > 31)) {
+ ast_log(LOG_WARNING, "Invalid day '%s', assuming none\n", c);
+ return 0;
+ }
+ e--;
+ } else
+ e = s;
+ mask = 0;
+ for (x=s; x!=e; x = (x + 1) % 31) {
+ mask |= (1 << x);
+ }
+ mask |= (1 << x);
+ return mask;
+}
+
+static char *months[] =
+{
+ "jan",
+ "feb",
+ "mar",
+ "apr",
+ "may",
+ "jun",
+ "jul",
+ "aug",
+ "sep",
+ "oct",
+ "nov",
+ "dec",
+};
+
+static unsigned int get_month(char *mon)
+{
+ char *c;
+ /* The following line is coincidence, really! */
+ int s, e, x;
+ unsigned int mask;
+
+ /* Check for all days */
+ if (ast_strlen_zero(mon) || !strcmp(mon, "*"))
+ return (1 << 12) - 1;
+ /* Get start and ending days */
+ c = strchr(mon, '-');
+ if (c) {
+ *c = '\0';
+ c++;
+ }
+ /* Find the start */
+ s = 0;
+ while((s < 12) && strcasecmp(mon, months[s])) s++;
+ if (s >= 12) {
+ ast_log(LOG_WARNING, "Invalid month '%s', assuming none\n", mon);
+ return 0;
+ }
+ if (c) {
+ e = 0;
+ while((e < 12) && strcasecmp(mon, months[e])) e++;
+ if (e >= 12) {
+ ast_log(LOG_WARNING, "Invalid month '%s', assuming none\n", c);
+ return 0;
+ }
+ } else
+ e = s;
+ mask = 0;
+ for (x=s; x!=e; x = (x + 1) % 12) {
+ mask |= (1 << x);
+ }
+ /* One last one */
+ mask |= (1 << x);
+ return mask;
+}
+
+int ast_build_timing(struct ast_timing *i, char *info_in)
+{
+ char info_save[256];
+ char *info;
+ char *c;
+
+ /* Check for empty just in case */
+ if (ast_strlen_zero(info_in))
+ return 0;
+ /* make a copy just in case we were passed a static string */
+ ast_copy_string(info_save, info_in, sizeof(info_save));
+ info = info_save;
+ /* Assume everything except time */
+ i->monthmask = (1 << 12) - 1;
+ i->daymask = (1 << 30) - 1 + (1 << 30);
+ i->dowmask = (1 << 7) - 1;
+ /* Avoid using str tok */
+ FIND_NEXT;
+ /* Info has the time range, start with that */
+ get_timerange(i, info);
+ info = c;
+ if (!info)
+ return 1;
+ FIND_NEXT;
+ /* Now check for day of week */
+ i->dowmask = get_dow(info);
+
+ info = c;
+ if (!info)
+ return 1;
+ FIND_NEXT;
+ /* Now check for the day of the month */
+ i->daymask = get_day(info);
+ info = c;
+ if (!info)
+ return 1;
+ FIND_NEXT;
+ /* And finally go for the month */
+ i->monthmask = get_month(info);
+
+ return 1;
+}
+
+int ast_check_timing(struct ast_timing *i)
+{
+ struct tm tm;
+ time_t t;
+
+ time(&t);
+ localtime_r(&t,&tm);
+
+ /* If it's not the right month, return */
+ if (!(i->monthmask & (1 << tm.tm_mon))) {
+ return 0;
+ }
+
+ /* If it's not that time of the month.... */
+ /* Warning, tm_mday has range 1..31! */
+ if (!(i->daymask & (1 << (tm.tm_mday-1))))
+ return 0;
+
+ /* If it's not the right day of the week */
+ if (!(i->dowmask & (1 << tm.tm_wday)))
+ return 0;
+
+ /* Sanity check the hour just to be safe */
+ if ((tm.tm_hour < 0) || (tm.tm_hour > 23)) {
+ ast_log(LOG_WARNING, "Insane time...\n");
+ return 0;
+ }
+
+ /* Now the tough part, we calculate if it fits
+ in the right time based on min/hour */
+ if (!(i->minmask[tm.tm_hour] & (1 << (tm.tm_min / 2))))
+ return 0;
+
+ /* If we got this far, then we're good */
+ return 1;
+}
+
+/*
+ * errno values
+ * ENOMEM - out of memory
+ * EBUSY - can't lock
+ * EEXIST - already included
+ * EINVAL - there is no existence of context for inclusion
+ */
+int ast_context_add_include2(struct ast_context *con, const char *value,
+ const char *registrar)
+{
+ struct ast_include *new_include;
+ char *c;
+ struct ast_include *i, *il = NULL; /* include, include_last */
+ int length;
+ char *p;
+
+ length = sizeof(struct ast_include);
+ length += 2 * (strlen(value) + 1);
+
+ /* allocate new include structure ... */
+ if (!(new_include = malloc(length))) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /* ... fill in this structure ... */
+ memset(new_include, 0, length);
+ p = new_include->stuff;
+ new_include->name = p;
+ strcpy(new_include->name, value);
+ p += strlen(value) + 1;
+ new_include->rname = p;
+ strcpy(new_include->rname, value);
+ c = new_include->rname;
+ /* Strip off timing info */
+ while(*c && (*c != '|'))
+ c++;
+ /* Process if it's there */
+ if (*c) {
+ new_include->hastime = ast_build_timing(&(new_include->timing), c+1);
+ *c = '\0';
+ }
+ new_include->next = NULL;
+ new_include->registrar = registrar;
+
+ /* ... try to lock this context ... */
+ if (ast_mutex_lock(&con->lock)) {
+ free(new_include);
+ errno = EBUSY;
+ return -1;
+ }
+
+ /* ... go to last include and check if context is already included too... */
+ i = con->includes;
+ while (i) {
+ if (!strcasecmp(i->name, new_include->name)) {
+ free(new_include);
+ ast_mutex_unlock(&con->lock);
+ errno = EEXIST;
+ return -1;
+ }
+ il = i;
+ i = i->next;
+ }
+
+ /* ... include new context into context list, unlock, return */
+ if (il)
+ il->next = new_include;
+ else
+ con->includes = new_include;
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Including context '%s' in context '%s'\n", new_include->name, ast_get_context_name(con));
+ ast_mutex_unlock(&con->lock);
+
+ return 0;
+}
+
+/*
+ * errno values
+ * EBUSY - can't lock
+ * ENOENT - no existence of context
+ */
+int ast_context_add_switch(const char *context, const char *sw, const char *data, int eval, const char *registrar)
+{
+ struct ast_context *c;
+
+ if (ast_lock_contexts()) {
+ errno = EBUSY;
+ return -1;
+ }
+
+ /* walk contexts ... */
+ c = ast_walk_contexts(NULL);
+ while (c) {
+ /* ... search for the right one ... */
+ if (!strcmp(ast_get_context_name(c), context)) {
+ int ret = ast_context_add_switch2(c, sw, data, eval, registrar);
+ /* ... unlock contexts list and return */
+ ast_unlock_contexts();
+ return ret;
+ }
+ c = ast_walk_contexts(c);
+ }
+
+ /* we can't find the right context */
+ ast_unlock_contexts();
+ errno = ENOENT;
+ return -1;
+}
+
+/*
+ * errno values
+ * ENOMEM - out of memory
+ * EBUSY - can't lock
+ * EEXIST - already included
+ * EINVAL - there is no existence of context for inclusion
+ */
+int ast_context_add_switch2(struct ast_context *con, const char *value,
+ const char *data, int eval, const char *registrar)
+{
+ struct ast_sw *new_sw;
+ struct ast_sw *i, *il = NULL; /* sw, sw_last */
+ int length;
+ char *p;
+
+ length = sizeof(struct ast_sw);
+ length += strlen(value) + 1;
+ if (data)
+ length += strlen(data);
+ length++;
+ if (eval) {
+ /* Create buffer for evaluation of variables */
+ length += SWITCH_DATA_LENGTH;
+ length++;
+ }
+
+ /* allocate new sw structure ... */
+ if (!(new_sw = malloc(length))) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /* ... fill in this structure ... */
+ memset(new_sw, 0, length);
+ p = new_sw->stuff;
+ new_sw->name = p;
+ strcpy(new_sw->name, value);
+ p += strlen(value) + 1;
+ new_sw->data = p;
+ if (data) {
+ strcpy(new_sw->data, data);
+ p += strlen(data) + 1;
+ } else {
+ strcpy(new_sw->data, "");
+ p++;
+ }
+ if (eval)
+ new_sw->tmpdata = p;
+ new_sw->next = NULL;
+ new_sw->eval = eval;
+ new_sw->registrar = registrar;
+
+ /* ... try to lock this context ... */
+ if (ast_mutex_lock(&con->lock)) {
+ free(new_sw);
+ errno = EBUSY;
+ return -1;
+ }
+
+ /* ... go to last sw and check if context is already swd too... */
+ i = con->alts;
+ while (i) {
+ if (!strcasecmp(i->name, new_sw->name) && !strcasecmp(i->data, new_sw->data)) {
+ free(new_sw);
+ ast_mutex_unlock(&con->lock);
+ errno = EEXIST;
+ return -1;
+ }
+ il = i;
+ i = i->next;
+ }
+
+ /* ... sw new context into context list, unlock, return */
+ if (il)
+ il->next = new_sw;
+ else
+ con->alts = new_sw;
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Including switch '%s/%s' in context '%s'\n", new_sw->name, new_sw->data, ast_get_context_name(con));
+ ast_mutex_unlock(&con->lock);
+
+ return 0;
+}
+
+/*
+ * EBUSY - can't lock
+ * ENOENT - there is not context existence
+ */
+int ast_context_remove_ignorepat(const char *context, const char *ignorepat, const char *registrar)
+{
+ struct ast_context *c;
+
+ if (ast_lock_contexts()) {
+ errno = EBUSY;
+ return -1;
+ }
+
+ c = ast_walk_contexts(NULL);
+ while (c) {
+ if (!strcmp(ast_get_context_name(c), context)) {
+ int ret = ast_context_remove_ignorepat2(c, ignorepat, registrar);
+ ast_unlock_contexts();
+ return ret;
+ }
+ c = ast_walk_contexts(c);
+ }
+
+ ast_unlock_contexts();
+ errno = ENOENT;
+ return -1;
+}
+
+int ast_context_remove_ignorepat2(struct ast_context *con, const char *ignorepat, const char *registrar)
+{
+ struct ast_ignorepat *ip, *ipl = NULL;
+
+ if (ast_mutex_lock(&con->lock)) {
+ errno = EBUSY;
+ return -1;
+ }
+
+ ip = con->ignorepats;
+ while (ip) {
+ if (!strcmp(ip->pattern, ignorepat) &&
+ (!registrar || (registrar == ip->registrar))) {
+ if (ipl) {
+ ipl->next = ip->next;
+ free(ip);
+ } else {
+ con->ignorepats = ip->next;
+ free(ip);
+ }
+ ast_mutex_unlock(&con->lock);
+ return 0;
+ }
+ ipl = ip; ip = ip->next;
+ }
+
+ ast_mutex_unlock(&con->lock);
+ errno = EINVAL;
+ return -1;
+}
+
+/*
+ * EBUSY - can't lock
+ * ENOENT - there is no existence of context
+ */
+int ast_context_add_ignorepat(const char *con, const char *value, const char *registrar)
+{
+ struct ast_context *c;
+
+ if (ast_lock_contexts()) {
+ errno = EBUSY;
+ return -1;
+ }
+
+ c = ast_walk_contexts(NULL);
+ while (c) {
+ if (!strcmp(ast_get_context_name(c), con)) {
+ int ret = ast_context_add_ignorepat2(c, value, registrar);
+ ast_unlock_contexts();
+ return ret;
+ }
+ c = ast_walk_contexts(c);
+ }
+
+ ast_unlock_contexts();
+ errno = ENOENT;
+ return -1;
+}
+
+int ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar)
+{
+ struct ast_ignorepat *ignorepat, *ignorepatc, *ignorepatl = NULL;
+ int length;
+ length = sizeof(struct ast_ignorepat);
+ length += strlen(value) + 1;
+ ignorepat = malloc(length);
+ if (!ignorepat) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ errno = ENOMEM;
+ return -1;
+ }
+ memset(ignorepat, 0, length);
+ strcpy(ignorepat->pattern, value);
+ ignorepat->next = NULL;
+ ignorepat->registrar = registrar;
+ ast_mutex_lock(&con->lock);
+ ignorepatc = con->ignorepats;
+ while(ignorepatc) {
+ ignorepatl = ignorepatc;
+ if (!strcasecmp(ignorepatc->pattern, value)) {
+ /* Already there */
+ ast_mutex_unlock(&con->lock);
+ errno = EEXIST;
+ return -1;
+ }
+ ignorepatc = ignorepatc->next;
+ }
+ if (ignorepatl)
+ ignorepatl->next = ignorepat;
+ else
+ con->ignorepats = ignorepat;
+ ast_mutex_unlock(&con->lock);
+ return 0;
+
+}
+
+int ast_ignore_pattern(const char *context, const char *pattern)
+{
+ struct ast_context *con;
+ struct ast_ignorepat *pat;
+
+ con = ast_context_find(context);
+ if (con) {
+ pat = con->ignorepats;
+ while (pat) {
+ if (ast_extension_match(pat->pattern, pattern))
+ return 1;
+ pat = pat->next;
+ }
+ }
+ return 0;
+}
+
+/*
+ * EBUSY - can't lock
+ * ENOENT - no existence of context
+ *
+ */
+int ast_add_extension(const char *context, int replace, const char *extension, int priority, const char *label, const char *callerid,
+ const char *application, void *data, void (*datad)(void *), const char *registrar)
+{
+ struct ast_context *c;
+
+ if (ast_lock_contexts()) {
+ errno = EBUSY;
+ return -1;
+ }
+
+ c = ast_walk_contexts(NULL);
+ while (c) {
+ if (!strcmp(context, ast_get_context_name(c))) {
+ int ret = ast_add_extension2(c, replace, extension, priority, label, callerid,
+ application, data, datad, registrar);
+ ast_unlock_contexts();
+ return ret;
+ }
+ c = ast_walk_contexts(c);
+ }
+
+ ast_unlock_contexts();
+ errno = ENOENT;
+ return -1;
+}
+
+int ast_explicit_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
+{
+ if (!chan)
+ return -1;
+
+ if (!ast_strlen_zero(context))
+ ast_copy_string(chan->context, context, sizeof(chan->context));
+ if (!ast_strlen_zero(exten))
+ ast_copy_string(chan->exten, exten, sizeof(chan->exten));
+ if (priority > -1) {
+ chan->priority = priority;
+ /* see flag description in channel.h for explanation */
+ if (ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP))
+ chan->priority--;
+ }
+
+ return 0;
+}
+
+int ast_async_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
+{
+ int res = 0;
+
+ ast_mutex_lock(&chan->lock);
+
+ if (chan->pbx) {
+ /* This channel is currently in the PBX */
+ ast_explicit_goto(chan, context, exten, priority);
+ ast_softhangup_nolock(chan, AST_SOFTHANGUP_ASYNCGOTO);
+ } else {
+ /* In order to do it when the channel doesn't really exist within
+ the PBX, we have to make a new channel, masquerade, and start the PBX
+ at the new location */
+ struct ast_channel *tmpchan;
+ tmpchan = ast_channel_alloc(0);
+ if (tmpchan) {
+ snprintf(tmpchan->name, sizeof(tmpchan->name), "AsyncGoto/%s", chan->name);
+ ast_setstate(tmpchan, chan->_state);
+ /* Make formats okay */
+ tmpchan->readformat = chan->readformat;
+ tmpchan->writeformat = chan->writeformat;
+ /* Setup proper location */
+ ast_explicit_goto(tmpchan,
+ (!ast_strlen_zero(context)) ? context : chan->context,
+ (!ast_strlen_zero(exten)) ? exten : chan->exten,
+ priority);
+
+ /* Masquerade into temp channel */
+ ast_channel_masquerade(tmpchan, chan);
+
+ /* Grab the locks and get going */
+ ast_mutex_lock(&tmpchan->lock);
+ ast_do_masquerade(tmpchan);
+ ast_mutex_unlock(&tmpchan->lock);
+ /* Start the PBX going on our stolen channel */
+ if (ast_pbx_start(tmpchan)) {
+ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmpchan->name);
+ ast_hangup(tmpchan);
+ res = -1;
+ }
+ } else {
+ res = -1;
+ }
+ }
+ ast_mutex_unlock(&chan->lock);
+ return res;
+}
+
+int ast_async_goto_by_name(const char *channame, const char *context, const char *exten, int priority)
+{
+ struct ast_channel *chan;
+ int res = -1;
+
+ chan = ast_get_channel_by_name_locked(channame);
+ if (chan) {
+ res = ast_async_goto(chan, context, exten, priority);
+ ast_mutex_unlock(&chan->lock);
+ }
+ return res;
+}
+
+static int ext_strncpy(char *dst, const char *src, int len)
+{
+ int count=0;
+
+ while(*src && (count < len - 1)) {
+ switch(*src) {
+ case ' ':
+ /* otherwise exten => [a-b],1,... doesn't work */
+ /* case '-': */
+ /* Ignore */
+ break;
+ default:
+ *dst = *src;
+ dst++;
+ }
+ src++;
+ count++;
+ }
+ *dst = '\0';
+
+ return count;
+}
+
+static void null_datad(void *foo)
+{
+}
+
+/*
+ * EBUSY - can't lock
+ * EEXIST - extension with the same priority exist and no replace is set
+ *
+ */
+int ast_add_extension2(struct ast_context *con,
+ int replace, const char *extension, int priority, const char *label, const char *callerid,
+ const char *application, void *data, void (*datad)(void *),
+ const char *registrar)
+{
+
+#define LOG do { if (option_debug) {\
+ if (tmp->matchcid) { \
+ ast_log(LOG_DEBUG, "Added extension '%s' priority %d (CID match '%s') to %s\n", tmp->exten, tmp->priority, tmp->cidmatch, con->name); \
+ } else { \
+ ast_log(LOG_DEBUG, "Added extension '%s' priority %d to %s\n", tmp->exten, tmp->priority, con->name); \
+ } \
+ } else if (option_verbose > 2) { \
+ if (tmp->matchcid) { \
+ ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d (CID match '%s')to %s\n", tmp->exten, tmp->priority, tmp->cidmatch, con->name); \
+ } else { \
+ ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d to %s\n", tmp->exten, tmp->priority, con->name); \
+ } \
+ } } while(0)
+
+ /*
+ * 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;
+ int length;
+ char *p;
+ char expand_buf[VAR_BUF_SIZE] = { 0, };
+
+ /* if we are adding a hint, and there are global variables, and the hint
+ contains variable references, then expand them
+ */
+ if ((priority == PRIORITY_HINT) && AST_LIST_FIRST(&globals) && strstr(application, "${")) {
+ pbx_substitute_variables_varshead(&globals, application, expand_buf, sizeof(expand_buf));
+ application = expand_buf;
+ }
+
+ length = sizeof(struct ast_exten);
+ length += strlen(extension) + 1;
+ length += strlen(application) + 1;
+ if (label)
+ length += strlen(label) + 1;
+ if (callerid)
+ length += strlen(callerid) + 1;
+ else
+ length ++;
+
+ /* Be optimistic: Build the extension structure first */
+ if (datad == NULL)
+ datad = null_datad;
+ tmp = malloc(length);
+ if (tmp) {
+ memset(tmp, 0, length);
+ p = tmp->stuff;
+ if (label) {
+ tmp->label = p;
+ strcpy(tmp->label, label);
+ p += strlen(label) + 1;
+ }
+ tmp->exten = p;
+ p += ext_strncpy(tmp->exten, extension, strlen(extension) + 1) + 1;
+ tmp->priority = priority;
+ tmp->cidmatch = p;
+ if (callerid) {
+ p += ext_strncpy(tmp->cidmatch, callerid, strlen(callerid) + 1) + 1;
+ tmp->matchcid = 1;
+ } else {
+ tmp->cidmatch[0] = '\0';
+ tmp->matchcid = 0;
+ p++;
+ }
+ tmp->app = p;
+ strcpy(tmp->app, application);
+ tmp->parent = con;
+ tmp->data = data;
+ tmp->datad = datad;
+ tmp->registrar = registrar;
+ tmp->peer = NULL;
+ tmp->next = NULL;
+ } else {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ errno = ENOMEM;
+ return -1;
+ }
+ if (ast_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);
+ errno = EBUSY;
+ return -1;
+ }
+ e = con->root;
+ while(e) {
+ /* Make sure patterns are always last! */
+ if ((e->exten[0] != '_') && (extension[0] == '_'))
+ res = -1;
+ else if ((e->exten[0] == '_') && (extension[0] != '_'))
+ res = 1;
+ else
+ res= strcmp(e->exten, extension);
+ if (!res) {
+ if (!e->matchcid && !tmp->matchcid)
+ res = 0;
+ else if (tmp->matchcid && !e->matchcid)
+ res = 1;
+ else if (e->matchcid && !tmp->matchcid)
+ res = -1;
+ else
+ res = strcasecmp(e->cidmatch, tmp->cidmatch);
+ }
+ 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;
+ }
+ if (tmp->priority == PRIORITY_HINT)
+ ast_change_hint(e,tmp);
+ /* Destroy the old one */
+ e->datad(e->data);
+ free(e);
+ ast_mutex_unlock(&con->lock);
+ if (tmp->priority == PRIORITY_HINT)
+ ast_change_hint(e, tmp);
+ /* 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);
+ ast_mutex_unlock(&con->lock);
+ errno = EEXIST;
+ 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->next;
+ /* Con->root must always exist or we couldn't get here */
+ tmp->peer = con->root;
+ con->root = tmp;
+ }
+ ast_mutex_unlock(&con->lock);
+
+ /* And immediately return success. */
+ if (tmp->priority == PRIORITY_HINT)
+ ast_add_hint(tmp);
+
+ 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;
+ ast_mutex_unlock(&con->lock);
+ if (tmp->priority == PRIORITY_HINT)
+ ast_add_hint(tmp);
+
+ /* 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;
+ }
+ ast_mutex_unlock(&con->lock);
+ if (tmp->priority == PRIORITY_HINT)
+ ast_add_hint(tmp);
+
+ /* 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;
+ ast_mutex_unlock(&con->lock);
+ if (tmp->priority == PRIORITY_HINT)
+ ast_add_hint(tmp);
+ LOG;
+ return 0;
+}
+
+struct async_stat {
+ pthread_t p;
+ struct ast_channel *chan;
+ char context[AST_MAX_CONTEXT];
+ char exten[AST_MAX_EXTENSION];
+ int priority;
+ int timeout;
+ char app[AST_MAX_EXTENSION];
+ char appdata[1024];
+};
+
+static void *async_wait(void *data)
+{
+ struct async_stat *as = data;
+ struct ast_channel *chan = as->chan;
+ int timeout = as->timeout;
+ int res;
+ struct ast_frame *f;
+ struct ast_app *app;
+
+ while(timeout && (chan->_state != AST_STATE_UP)) {
+ res = ast_waitfor(chan, timeout);
+ if (res < 1)
+ break;
+ if (timeout > -1)
+ timeout = res;
+ f = ast_read(chan);
+ if (!f)
+ break;
+ if (f->frametype == AST_FRAME_CONTROL) {
+ if ((f->subclass == AST_CONTROL_BUSY) ||
+ (f->subclass == AST_CONTROL_CONGESTION) )
+ break;
+ }
+ ast_frfree(f);
+ }
+ if (chan->_state == AST_STATE_UP) {
+ if (!ast_strlen_zero(as->app)) {
+ app = pbx_findapp(as->app);
+ if (app) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Launching %s(%s) on %s\n", as->app, as->appdata, chan->name);
+ pbx_exec(chan, app, as->appdata, 1);
+ } else
+ ast_log(LOG_WARNING, "No such application '%s'\n", as->app);
+ } else {
+ if (!ast_strlen_zero(as->context))
+ ast_copy_string(chan->context, as->context, sizeof(chan->context));
+ if (!ast_strlen_zero(as->exten))
+ ast_copy_string(chan->exten, as->exten, sizeof(chan->exten));
+ if (as->priority > 0)
+ chan->priority = as->priority;
+ /* Run the PBX */
+ if (ast_pbx_run(chan)) {
+ ast_log(LOG_ERROR, "Failed to start PBX on %s\n", chan->name);
+ } else {
+ /* PBX will have taken care of this */
+ chan = NULL;
+ }
+ }
+
+ }
+ free(as);
+ if (chan)
+ ast_hangup(chan);
+ return NULL;
+}
+
+/*! Function to update the cdr after a spool call fails.
+ *
+ * This function updates the cdr for a failed spool call
+ * and takes the channel of the failed call as an argument.
+ *
+ */
+int ast_pbx_outgoing_cdr_failed(void)
+{
+ /* allocate a channel */
+ struct ast_channel *chan = ast_channel_alloc(0);
+ if(!chan) {
+ /* allocation of the channel failed, let some peeps know */
+ ast_log(LOG_WARNING, "Unable to allocate channel structure for CDR record\n");
+ return -1; /* failure */
+ }
+
+ chan->cdr = ast_cdr_alloc(); /* allocate a cdr for the channel */
+
+ if(!chan->cdr) {
+ /* allocation of the cdr failed */
+ ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
+ ast_channel_free(chan); /* free the channel */
+ return -1; /* return failure */
+ }
+
+ /* allocation of the cdr was successful */
+ ast_cdr_init(chan->cdr, chan); /* initilize our channel's cdr */
+ ast_cdr_start(chan->cdr); /* record the start and stop time */
+ ast_cdr_end(chan->cdr);
+ ast_cdr_failed(chan->cdr); /* set the status to failed */
+ ast_cdr_detach(chan->cdr); /* post and free the record */
+ ast_channel_free(chan); /* free the channel */
+
+ return 0; /* success */
+}
+
+int ast_pbx_outgoing_exten(const char *type, int format, void *data, int timeout, const char *context, const char *exten, int priority, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **channel)
+{
+ struct ast_channel *chan;
+ struct async_stat *as;
+ int res = -1, cdr_res = -1;
+ struct outgoing_helper oh;
+ pthread_attr_t attr;
+
+ if (sync) {
+ LOAD_OH(oh);
+ chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
+ if (channel) {
+ *channel = chan;
+ if (chan)
+ ast_mutex_lock(&chan->lock);
+ }
+ if (chan) {
+ if (chan->cdr) { /* check if the channel already has a cdr record, if not give it one */
+ ast_log(LOG_WARNING, "%s already has a call record??\n", chan->name);
+ } else {
+ chan->cdr = ast_cdr_alloc(); /* allocate a cdr for the channel */
+ if (!chan->cdr) {
+ /* allocation of the cdr failed */
+ ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
+ free(chan->pbx);
+ res = -1;
+ goto outgoing_exten_cleanup;
+ }
+ /* allocation of the cdr was successful */
+ ast_cdr_init(chan->cdr, chan); /* initilize our channel's cdr */
+ ast_cdr_start(chan->cdr);
+ }
+ if (chan->_state == AST_STATE_UP) {
+ res = 0;
+ if (option_verbose > 3)
+ ast_verbose(VERBOSE_PREFIX_4 "Channel %s was answered.\n", chan->name);
+
+ if (sync > 1) {
+ if (channel)
+ ast_mutex_unlock(&chan->lock);
+ if (ast_pbx_run(chan)) {
+ ast_log(LOG_ERROR, "Unable to run PBX on %s\n", chan->name);
+ if (channel)
+ *channel = NULL;
+ ast_hangup(chan);
+ res = -1;
+ }
+ } else {
+ if (ast_pbx_start(chan)) {
+ ast_log(LOG_ERROR, "Unable to start PBX on %s\n", chan->name);
+ if (channel)
+ *channel = NULL;
+ ast_hangup(chan);
+ res = -1;
+ }
+ }
+ } else {
+ if (option_verbose > 3)
+ ast_verbose(VERBOSE_PREFIX_4 "Channel %s was never answered.\n", chan->name);
+
+ if(chan->cdr) { /* update the cdr */
+ /* here we update the status of the call, which sould be busy.
+ * if that fails then we set the status to failed */
+ if (ast_cdr_disposition(chan->cdr, chan->hangupcause))
+ ast_cdr_failed(chan->cdr);
+ }
+
+ if (channel)
+ *channel = NULL;
+ ast_hangup(chan);
+ }
+ }
+
+ if(res < 0) { /* the call failed for some reason */
+ if (*reason == 0) { /* if the call failed (not busy or no answer)
+ * update the cdr with the failed message */
+ cdr_res = ast_pbx_outgoing_cdr_failed();
+ if (cdr_res != 0) {
+ res = cdr_res;
+ goto outgoing_exten_cleanup;
+ }
+ }
+
+ /* create a fake channel and execute the "failed" extension (if it exists) within the requested context */
+ /* check if "failed" exists */
+ if (ast_exists_extension(chan, context, "failed", 1, NULL)) {
+ chan = ast_channel_alloc(0);
+ if (chan) {
+ ast_copy_string(chan->name, "OutgoingSpoolFailed", sizeof(chan->name));
+ if (!ast_strlen_zero(context))
+ ast_copy_string(chan->context, context, sizeof(chan->context));
+ ast_copy_string(chan->exten, "failed", sizeof(chan->exten));
+ chan->priority = 1;
+ ast_set_variables(chan, vars);
+ if (account)
+ ast_cdr_setaccount(chan, account);
+ ast_pbx_run(chan);
+ } else
+ ast_log(LOG_WARNING, "Can't allocate the channel structure, skipping execution of extension 'failed'\n");
+ }
+ }
+ } else {
+ as = malloc(sizeof(struct async_stat));
+ if (!as) {
+ res = -1;
+ goto outgoing_exten_cleanup;
+ }
+ memset(as, 0, sizeof(struct async_stat));
+ chan = ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name);
+ if (channel) {
+ *channel = chan;
+ if (chan)
+ ast_mutex_lock(&chan->lock);
+ }
+ if (!chan) {
+ free(as);
+ res = -1;
+ goto outgoing_exten_cleanup;
+ }
+ as->chan = chan;
+ ast_copy_string(as->context, context, sizeof(as->context));
+ ast_copy_string(as->exten, exten, sizeof(as->exten));
+ as->priority = priority;
+ as->timeout = timeout;
+ ast_set_variables(chan, vars);
+ if (account)
+ ast_cdr_setaccount(chan, account);
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (ast_pthread_create(&as->p, &attr, async_wait, as)) {
+ ast_log(LOG_WARNING, "Failed to start async wait\n");
+ free(as);
+ if (channel)
+ *channel = NULL;
+ ast_hangup(chan);
+ res = -1;
+ goto outgoing_exten_cleanup;
+ }
+ res = 0;
+ }
+outgoing_exten_cleanup:
+ ast_variables_destroy(vars);
+ return res;
+}
+
+struct app_tmp {
+ char app[256];
+ char data[256];
+ struct ast_channel *chan;
+ pthread_t t;
+};
+
+static void *ast_pbx_run_app(void *data)
+{
+ struct app_tmp *tmp = data;
+ struct ast_app *app;
+ app = pbx_findapp(tmp->app);
+ if (app) {
+ if (option_verbose > 3)
+ ast_verbose(VERBOSE_PREFIX_4 "Launching %s(%s) on %s\n", tmp->app, tmp->data, tmp->chan->name);
+ pbx_exec(tmp->chan, app, tmp->data, 1);
+ } else
+ ast_log(LOG_WARNING, "No such application '%s'\n", tmp->app);
+ ast_hangup(tmp->chan);
+ free(tmp);
+ return NULL;
+}
+
+int ast_pbx_outgoing_app(const char *type, int format, void *data, int timeout, const char *app, const char *appdata, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel)
+{
+ struct ast_channel *chan;
+ struct async_stat *as;
+ struct app_tmp *tmp;
+ int res = -1, cdr_res = -1;
+ struct outgoing_helper oh;
+ pthread_attr_t attr;
+
+ memset(&oh, 0, sizeof(oh));
+ oh.vars = vars;
+ oh.account = account;
+
+ if (locked_channel)
+ *locked_channel = NULL;
+ if (ast_strlen_zero(app)) {
+ res = -1;
+ goto outgoing_app_cleanup;
+ }
+ if (sync) {
+ chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
+ if (chan) {
+ if (chan->cdr) { /* check if the channel already has a cdr record, if not give it one */
+ ast_log(LOG_WARNING, "%s already has a call record??\n", chan->name);
+ } else {
+ chan->cdr = ast_cdr_alloc(); /* allocate a cdr for the channel */
+ if(!chan->cdr) {
+ /* allocation of the cdr failed */
+ ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
+ free(chan->pbx);
+ res = -1;
+ goto outgoing_app_cleanup;
+ }
+ /* allocation of the cdr was successful */
+ ast_cdr_init(chan->cdr, chan); /* initilize our channel's cdr */
+ ast_cdr_start(chan->cdr);
+ }
+ ast_set_variables(chan, vars);
+ if (account)
+ ast_cdr_setaccount(chan, account);
+ if (chan->_state == AST_STATE_UP) {
+ res = 0;
+ if (option_verbose > 3)
+ ast_verbose(VERBOSE_PREFIX_4 "Channel %s was answered.\n", chan->name);
+ tmp = malloc(sizeof(struct app_tmp));
+ if (tmp) {
+ memset(tmp, 0, sizeof(struct app_tmp));
+ ast_copy_string(tmp->app, app, sizeof(tmp->app));
+ if (appdata)
+ ast_copy_string(tmp->data, appdata, sizeof(tmp->data));
+ tmp->chan = chan;
+ if (sync > 1) {
+ if (locked_channel)
+ ast_mutex_unlock(&chan->lock);
+ ast_pbx_run_app(tmp);
+ } else {
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (locked_channel)
+ ast_mutex_lock(&chan->lock);
+ if (ast_pthread_create(&tmp->t, &attr, ast_pbx_run_app, tmp)) {
+ ast_log(LOG_WARNING, "Unable to spawn execute thread on %s: %s\n", chan->name, strerror(errno));
+ free(tmp);
+ if (locked_channel)
+ ast_mutex_unlock(&chan->lock);
+ ast_hangup(chan);
+ res = -1;
+ } else {
+ if (locked_channel)
+ *locked_channel = chan;
+ }
+ }
+ } else {
+ ast_log(LOG_ERROR, "Out of memory :(\n");
+ res = -1;
+ }
+ } else {
+ if (option_verbose > 3)
+ ast_verbose(VERBOSE_PREFIX_4 "Channel %s was never answered.\n", chan->name);
+ if (chan->cdr) { /* update the cdr */
+ /* here we update the status of the call, which sould be busy.
+ * if that fails then we set the status to failed */
+ if (ast_cdr_disposition(chan->cdr, chan->hangupcause))
+ ast_cdr_failed(chan->cdr);
+ }
+ ast_hangup(chan);
+ }
+ }
+
+ if (res < 0) { /* the call failed for some reason */
+ if (*reason == 0) { /* if the call failed (not busy or no answer)
+ * update the cdr with the failed message */
+ cdr_res = ast_pbx_outgoing_cdr_failed();
+ if (cdr_res != 0) {
+ res = cdr_res;
+ goto outgoing_app_cleanup;
+ }
+ }
+ }
+
+ } else {
+ as = malloc(sizeof(struct async_stat));
+ if (!as) {
+ res = -1;
+ goto outgoing_app_cleanup;
+ }
+ memset(as, 0, sizeof(struct async_stat));
+ chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
+ if (!chan) {
+ free(as);
+ res = -1;
+ goto outgoing_app_cleanup;
+ }
+ as->chan = chan;
+ ast_copy_string(as->app, app, sizeof(as->app));
+ if (appdata)
+ ast_copy_string(as->appdata, appdata, sizeof(as->appdata));
+ as->timeout = timeout;
+ ast_set_variables(chan, vars);
+ if (account)
+ ast_cdr_setaccount(chan, account);
+ /* Start a new thread, and get something handling this channel. */
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (locked_channel)
+ ast_mutex_lock(&chan->lock);
+ if (ast_pthread_create(&as->p, &attr, async_wait, as)) {
+ ast_log(LOG_WARNING, "Failed to start async wait\n");
+ free(as);
+ if (locked_channel)
+ ast_mutex_unlock(&chan->lock);
+ ast_hangup(chan);
+ res = -1;
+ goto outgoing_app_cleanup;
+ } else {
+ if (locked_channel)
+ *locked_channel = chan;
+ }
+ res = 0;
+ }
+outgoing_app_cleanup:
+ ast_variables_destroy(vars);
+ return res;
+}
+
+static void destroy_exten(struct ast_exten *e)
+{
+ if (e->priority == PRIORITY_HINT)
+ ast_remove_hint(e);
+
+ if (e->datad)
+ e->datad(e->data);
+ free(e);
+}
+
+void __ast_context_destroy(struct ast_context *con, const char *registrar)
+{
+ struct ast_context *tmp, *tmpl=NULL;
+ struct ast_include *tmpi, *tmpil= NULL;
+ struct ast_sw *sw, *swl= NULL;
+ struct ast_exten *e, *el, *en;
+ struct ast_ignorepat *ipi, *ipl = NULL;
+
+ ast_mutex_lock(&conlock);
+ tmp = contexts;
+ while(tmp) {
+ if (((tmp->name && con && con->name && !strcasecmp(tmp->name, con->name)) || !con) &&
+ (!registrar || !strcasecmp(registrar, tmp->registrar))) {
+ /* Okay, let's lock the structure to be sure nobody else
+ is searching through it. */
+ if (ast_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. */
+ ast_mutex_unlock(&tmp->lock);
+ for (tmpi = tmp->includes; tmpi; ) {
+ /* Free includes */
+ tmpil = tmpi;
+ tmpi = tmpi->next;
+ free(tmpil);
+ }
+ for (ipi = tmp->ignorepats; ipi; ) {
+ /* Free ignorepats */
+ ipl = ipi;
+ ipi = ipi->next;
+ free(ipl);
+ }
+ for (sw = tmp->alts; sw; ) {
+ /* Free switches */
+ swl = sw;
+ sw = sw->next;
+ free(swl);
+ swl = sw;
+ }
+ for (e = tmp->root; e;) {
+ for (en = e->peer; en;) {
+ el = en;
+ en = en->peer;
+ destroy_exten(el);
+ }
+ el = e;
+ e = e->next;
+ destroy_exten(el);
+ }
+ ast_mutex_destroy(&tmp->lock);
+ free(tmp);
+ if (!con) {
+ /* Might need to get another one -- restart */
+ tmp = contexts;
+ tmpl = NULL;
+ tmpil = NULL;
+ continue;
+ }
+ ast_mutex_unlock(&conlock);
+ return;
+ }
+ tmpl = tmp;
+ tmp = tmp->next;
+ }
+ ast_mutex_unlock(&conlock);
+}
+
+void ast_context_destroy(struct ast_context *con, const char *registrar)
+{
+ __ast_context_destroy(con,registrar);
+}
+
+static void wait_for_hangup(struct ast_channel *chan, void *data)
+{
+ int res;
+ struct ast_frame *f;
+ int waittime;
+
+ if (ast_strlen_zero(data) || (sscanf(data, "%d", &waittime) != 1) || (waittime < 0))
+ waittime = -1;
+ if (waittime > -1) {
+ ast_safe_sleep(chan, waittime * 1000);
+ } else do {
+ res = ast_waitfor(chan, -1);
+ if (res < 0)
+ return;
+ f = ast_read(chan);
+ if (f)
+ ast_frfree(f);
+ } while(f);
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_progress(struct ast_channel *chan, void *data)
+{
+ ast_indicate(chan, AST_CONTROL_PROGRESS);
+ return 0;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_ringing(struct ast_channel *chan, void *data)
+{
+ ast_indicate(chan, AST_CONTROL_RINGING);
+ return 0;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_busy(struct ast_channel *chan, void *data)
+{
+ ast_indicate(chan, AST_CONTROL_BUSY);
+ ast_setstate(chan, AST_STATE_BUSY);
+ wait_for_hangup(chan, data);
+ return -1;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_congestion(struct ast_channel *chan, void *data)
+{
+ ast_indicate(chan, AST_CONTROL_CONGESTION);
+ ast_setstate(chan, AST_STATE_BUSY);
+ wait_for_hangup(chan, data);
+ return -1;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_answer(struct ast_channel *chan, void *data)
+{
+ int delay = 0;
+ int res;
+
+ if (chan->_state == AST_STATE_UP)
+ delay = 0;
+ else if (!ast_strlen_zero(data))
+ delay = atoi(data);
+
+ res = ast_answer(chan);
+ if (res)
+ return res;
+
+ if (delay)
+ res = ast_safe_sleep(chan, delay);
+
+ return res;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_setlanguage(struct ast_channel *chan, void *data)
+{
+ static int deprecation_warning = 0;
+
+ if (!deprecation_warning) {
+ ast_log(LOG_WARNING, "SetLanguage is deprecated, please use Set(LANGUAGE()=language) instead.\n");
+ deprecation_warning = 1;
+ }
+
+ /* Copy the language as specified */
+ if (!ast_strlen_zero(data))
+ ast_copy_string(chan->language, data, sizeof(chan->language));
+
+ return 0;
+}
+
+AST_APP_OPTIONS(resetcdr_opts, {
+ AST_APP_OPTION('w', AST_CDR_FLAG_POSTED),
+ AST_APP_OPTION('a', AST_CDR_FLAG_LOCKED),
+ AST_APP_OPTION('v', AST_CDR_FLAG_KEEP_VARS),
+});
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_resetcdr(struct ast_channel *chan, void *data)
+{
+ char *args;
+ struct ast_flags flags = { 0 };
+
+ if (!ast_strlen_zero(data)) {
+ args = ast_strdupa(data);
+ if (!args) {
+ ast_log(LOG_ERROR, "Out of memory!\n");
+ return -1;
+ }
+ ast_app_parse_options(resetcdr_opts, &flags, NULL, args);
+ }
+
+ ast_cdr_reset(chan->cdr, &flags);
+
+ return 0;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_setaccount(struct ast_channel *chan, void *data)
+{
+ /* Copy the account code as specified */
+ if (data)
+ ast_cdr_setaccount(chan, (char *)data);
+ else
+ ast_cdr_setaccount(chan, "");
+ return 0;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_setamaflags(struct ast_channel *chan, void *data)
+{
+ /* Copy the AMA Flags as specified */
+ if (data)
+ ast_cdr_setamaflags(chan, (char *)data);
+ else
+ ast_cdr_setamaflags(chan, "");
+ return 0;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_hangup(struct ast_channel *chan, void *data)
+{
+ /* Just return non-zero and it will hang up */
+ if (!chan->hangupcause)
+ chan->hangupcause = AST_CAUSE_NORMAL_CLEARING;
+ return -1;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_gotoiftime(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ char *s, *ts;
+ struct ast_timing timing;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "GotoIfTime requires an argument:\n <time range>|<days of week>|<days of month>|<months>?[[context|]extension|]priority\n");
+ return -1;
+ }
+
+ if ((s = ast_strdupa((char *) data))) {
+ ts = s;
+
+ /* Separate the Goto path */
+ strsep(&ts,"?");
+
+ /* struct ast_include include contained garbage here, fixed by zeroing it on get_timerange */
+ if (ast_build_timing(&timing, s) && ast_check_timing(&timing))
+ res = pbx_builtin_goto(chan, (void *)ts);
+ } else {
+ ast_log(LOG_ERROR, "Memory Error!\n");
+ }
+ return res;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_execiftime(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ char *ptr1, *ptr2;
+ struct ast_timing timing;
+ struct ast_app *app;
+ const char *usage = "ExecIfTime requires an argument:\n <time range>|<days of week>|<days of month>|<months>?<appname>[|<appargs>]";
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "%s\n", usage);
+ return -1;
+ }
+
+ ptr1 = ast_strdupa(data);
+
+ if (!ptr1) {
+ ast_log(LOG_ERROR, "Out of Memory!\n");
+ return -1;
+ }
+
+ ptr2 = ptr1;
+ /* Separate the Application data ptr1 is the time spec ptr2 is the app|data */
+ strsep(&ptr2,"?");
+ if(!ast_build_timing(&timing, ptr1)) {
+ ast_log(LOG_WARNING, "Invalid Time Spec: %s\nCorrect usage: %s\n", ptr1, usage);
+ res = -1;
+ }
+
+ if (!res && ast_check_timing(&timing)) {
+ if (!ptr2) {
+ ast_log(LOG_WARNING, "%s\n", usage);
+ }
+
+ /* ptr2 is now the app name
+ we're done with ptr1 now so recycle it and use it to point to the app args */
+ if((ptr1 = strchr(ptr2, '|'))) {
+ *ptr1 = '\0';
+ ptr1++;
+ }
+
+ if ((app = pbx_findapp(ptr2))) {
+ res = pbx_exec(chan, app, ptr1 ? ptr1 : "", 1);
+ } else {
+ ast_log(LOG_WARNING, "Cannot locate application %s\n", ptr2);
+ res = -1;
+ }
+ }
+
+ return res;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_wait(struct ast_channel *chan, void *data)
+{
+ int ms;
+
+ /* Wait for "n" seconds */
+ if (data && atof((char *)data)) {
+ ms = atof((char *)data) * 1000;
+ return ast_safe_sleep(chan, ms);
+ }
+ return 0;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_waitexten(struct ast_channel *chan, void *data)
+{
+ int ms, res, argc;
+ char *args;
+ char *argv[2];
+ char *options = NULL;
+ char *timeout = NULL;
+ struct ast_flags flags = {0};
+ char *opts[1] = { NULL };
+
+ args = ast_strdupa(data);
+
+ if ((argc = ast_app_separate_args(args, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
+ if (argc > 0) {
+ timeout = argv[0];
+ if (argc > 1)
+ options = argv[1];
+ }
+ }
+
+ if (options)
+ ast_app_parse_options(waitexten_opts, &flags, opts, options);
+
+ if (ast_test_flag(&flags, WAITEXTEN_MOH))
+ ast_moh_start(chan, opts[0]);
+
+ /* Wait for "n" seconds */
+ if (timeout && atof((char *)timeout))
+ ms = atof((char *)timeout) * 1000;
+ else if (chan->pbx)
+ ms = chan->pbx->rtimeout * 1000;
+ else
+ ms = 10000;
+ res = ast_waitfordigit(chan, ms);
+ if (!res) {
+ if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 1, chan->cid.cid_num)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Timeout on %s, continuing...\n", chan->name);
+ } else if (ast_exists_extension(chan, chan->context, "t", 1, chan->cid.cid_num)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Timeout on %s, going to 't'\n", chan->name);
+ ast_copy_string(chan->exten, "t", sizeof(chan->exten));
+ chan->priority = 0;
+ } else {
+ ast_log(LOG_WARNING, "Timeout but no rule 't' in context '%s'\n", chan->context);
+ res = -1;
+ }
+ }
+
+ if (ast_test_flag(&flags, WAITEXTEN_MOH))
+ ast_moh_stop(chan);
+
+ return res;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_background(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ int argc;
+ char *parse;
+ char *argv[4];
+ char *options = NULL;
+ char *filename = NULL;
+ char *front = NULL, *back = NULL;
+ char *lang = NULL;
+ char *context = NULL;
+ struct ast_flags flags = {0};
+
+ parse = ast_strdupa(data);
+
+ if ((argc = ast_app_separate_args(parse, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
+ switch (argc) {
+ case 4:
+ context = argv[3];
+ case 3:
+ lang = argv[2];
+ case 2:
+ options = argv[1];
+ case 1:
+ filename = argv[0];
+ break;
+ default:
+ ast_log(LOG_WARNING, "Background requires an argument (filename)\n");
+ break;
+ }
+ }
+
+ if (!lang)
+ lang = chan->language;
+
+ if (!context)
+ context = chan->context;
+
+ if (options) {
+ if (!strcasecmp(options, "skip"))
+ flags.flags = BACKGROUND_SKIP;
+ else if (!strcasecmp(options, "noanswer"))
+ flags.flags = BACKGROUND_NOANSWER;
+ else
+ ast_app_parse_options(background_opts, &flags, NULL, options);
+ }
+
+ /* Answer if need be */
+ if (chan->_state != AST_STATE_UP) {
+ if (ast_test_flag(&flags, BACKGROUND_SKIP)) {
+ return 0;
+ } else if (!ast_test_flag(&flags, BACKGROUND_NOANSWER)) {
+ res = ast_answer(chan);
+ }
+ }
+
+ if (!res) {
+ /* Stop anything playing */
+ ast_stopstream(chan);
+ /* Stream a file */
+ front = filename;
+ while(!res && front) {
+ if((back = strchr(front, '&'))) {
+ *back = '\0';
+ back++;
+ }
+ res = ast_streamfile(chan, front, lang);
+ if (!res) {
+ if (ast_test_flag(&flags, BACKGROUND_PLAYBACK)) {
+ res = ast_waitstream(chan, "");
+ } else {
+ if (ast_test_flag(&flags, BACKGROUND_MATCHEXTEN)) {
+ res = ast_waitstream_exten(chan, context);
+ } else {
+ res = ast_waitstream(chan, AST_DIGIT_ANY);
+ }
+ }
+ ast_stopstream(chan);
+ } else {
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char*)data);
+ res = 0;
+ break;
+ }
+ front = back;
+ }
+ }
+ if (context != chan->context && res) {
+ snprintf(chan->exten, sizeof(chan->exten), "%c", res);
+ ast_copy_string(chan->context, context, sizeof(chan->context));
+ chan->priority = 0;
+ return 0;
+ } else {
+ return res;
+ }
+}
+
+/*! AbsoluteTimeout
+ * \ingroup applications
+ * \todo Remove in 1.3 dev
+ */
+static int pbx_builtin_atimeout(struct ast_channel *chan, void *data)
+{
+ static int deprecation_warning = 0;
+ int x = atoi((char *) data);
+
+ if (!deprecation_warning) {
+ ast_log(LOG_WARNING, "AbsoluteTimeout is deprecated, please use Set(TIMEOUT(absolute)=timeout) instead.\n");
+ deprecation_warning = 1;
+ }
+
+ /* Set the absolute maximum time how long a call can be connected */
+ ast_channel_setwhentohangup(chan,x);
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "Set Absolute Timeout to %d\n", x);
+ return 0;
+}
+
+/*! ResponseTimeout
+ * \ingroup applications
+ * \todo Remove in 1.3 dev
+ */
+static int pbx_builtin_rtimeout(struct ast_channel *chan, void *data)
+{
+ static int deprecation_warning = 0;
+
+ if (!deprecation_warning) {
+ ast_log(LOG_WARNING, "ResponseTimeout is deprecated, please use Set(TIMEOUT(response)=timeout) instead.\n");
+ deprecation_warning = 1;
+ }
+
+ /* If the channel is not in a PBX, return now */
+ if (!chan->pbx)
+ return 0;
+
+ /* 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;
+}
+
+/*! DigitTimeout
+ * \ingroup applications
+ * \todo Remove in 1.3 dev
+ */
+static int pbx_builtin_dtimeout(struct ast_channel *chan, void *data)
+{
+ static int deprecation_warning = 0;
+
+ if (!deprecation_warning) {
+ ast_log(LOG_WARNING, "DigitTimeout is deprecated, please use Set(TIMEOUT(digit)=timeout) instead.\n");
+ deprecation_warning = 1;
+ }
+
+ /* If the channel is not in a PBX, return now */
+ if (!chan->pbx)
+ return 0;
+
+ /* 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;
+}
+
+/*! Goto
+ * \ingroup applications
+ */
+static int pbx_builtin_goto(struct ast_channel *chan, void *data)
+{
+ int res;
+ res = ast_parseable_goto(chan, (const char *) data);
+ if (!res && (option_verbose > 2))
+ ast_verbose( VERBOSE_PREFIX_3 "Goto (%s,%s,%d)\n", chan->context,chan->exten, chan->priority+1);
+ return res;
+}
+
+
+int pbx_builtin_serialize_variables(struct ast_channel *chan, char *buf, size_t size)
+{
+ struct ast_var_t *variables;
+ char *var, *val;
+ int total = 0;
+
+ if (!chan)
+ return 0;
+
+ memset(buf, 0, size);
+
+ AST_LIST_TRAVERSE(&chan->varshead, variables, entries) {
+ if(variables &&
+ (var=ast_var_name(variables)) && (val=ast_var_value(variables)) &&
+ !ast_strlen_zero(var) && !ast_strlen_zero(val)) {
+ if (ast_build_string(&buf, &size, "%s=%s\n", var, val)) {
+ ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
+ break;
+ } else
+ total++;
+ } else
+ break;
+ }
+
+ return total;
+}
+
+char *pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
+{
+ struct ast_var_t *variables;
+ struct varshead *headp;
+
+ if (chan)
+ headp=&chan->varshead;
+ else
+ headp=&globals;
+
+ if (name) {
+ AST_LIST_TRAVERSE(headp,variables,entries) {
+ if (!strcmp(name, ast_var_name(variables)))
+ return ast_var_value(variables);
+ }
+ if (headp != &globals) {
+ /* Check global variables if we haven't already */
+ headp = &globals;
+ AST_LIST_TRAVERSE(headp,variables,entries) {
+ if (!strcmp(name, ast_var_name(variables)))
+ return ast_var_value(variables);
+ }
+ }
+ }
+ return NULL;
+}
+
+void pbx_builtin_pushvar_helper(struct ast_channel *chan, const char *name, const char *value)
+{
+ struct ast_var_t *newvariable;
+ struct varshead *headp;
+
+ if (name[strlen(name)-1] == ')') {
+ ast_log(LOG_WARNING, "Cannot push a value onto a function\n");
+ return ast_func_write(chan, name, value);
+ }
+
+ headp = (chan) ? &chan->varshead : &globals;
+
+ if (value) {
+ if ((option_verbose > 1) && (headp == &globals))
+ ast_verbose(VERBOSE_PREFIX_2 "Setting global variable '%s' to '%s'\n", name, value);
+ newvariable = ast_var_assign(name, value);
+ AST_LIST_INSERT_HEAD(headp, newvariable, entries);
+ }
+}
+
+void pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
+{
+ struct ast_var_t *newvariable;
+ struct varshead *headp;
+ const char *nametail = name;
+
+ if (name[strlen(name)-1] == ')')
+ return ast_func_write(chan, name, value);
+
+ headp = (chan) ? &chan->varshead : &globals;
+
+ /* For comparison purposes, we have to strip leading underscores */
+ if (*nametail == '_') {
+ nametail++;
+ if (*nametail == '_')
+ nametail++;
+ }
+
+ AST_LIST_TRAVERSE (headp, newvariable, entries) {
+ if (strcasecmp(ast_var_name(newvariable), nametail) == 0) {
+ /* there is already such a variable, delete it */
+ AST_LIST_REMOVE(headp, newvariable, entries);
+ ast_var_delete(newvariable);
+ break;
+ }
+ }
+
+ if (value) {
+ if ((option_verbose > 1) && (headp == &globals))
+ ast_verbose(VERBOSE_PREFIX_2 "Setting global variable '%s' to '%s'\n", name, value);
+ newvariable = ast_var_assign(name, value);
+ AST_LIST_INSERT_HEAD(headp, newvariable, entries);
+ }
+}
+
+int pbx_builtin_setvar_old(struct ast_channel *chan, void *data)
+{
+ static int deprecation_warning = 0;
+
+ if (!deprecation_warning) {
+ ast_log(LOG_WARNING, "SetVar is deprecated, please use Set instead.\n");
+ deprecation_warning = 1;
+ }
+
+ return pbx_builtin_setvar(chan, data);
+}
+
+int pbx_builtin_setvar(struct ast_channel *chan, void *data)
+{
+ char *name, *value, *mydata;
+ int argc;
+ char *argv[24]; /* this will only support a maximum of 24 variables being set in a single operation */
+ int global = 0;
+ int x;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Set requires at least one variable name/value pair.\n");
+ return 0;
+ }
+
+ mydata = ast_strdupa(data);
+ argc = ast_app_separate_args(mydata, '|', argv, sizeof(argv) / sizeof(argv[0]));
+
+ /* check for a trailing flags argument */
+ if ((argc > 1) && !strchr(argv[argc-1], '=')) {
+ argc--;
+ if (strchr(argv[argc], 'g'))
+ global = 1;
+ }
+
+ for (x = 0; x < argc; x++) {
+ name = argv[x];
+ if ((value = strchr(name, '='))) {
+ *value = '\0';
+ value++;
+ pbx_builtin_setvar_helper((global) ? NULL : chan, name, value);
+ } else
+ ast_log(LOG_WARNING, "Ignoring entry '%s' with no = (and not last 'options' entry)\n", name);
+ }
+
+ return(0);
+}
+
+int pbx_builtin_importvar(struct ast_channel *chan, void *data)
+{
+ char *name;
+ char *value;
+ char *stringp=NULL;
+ char *channel;
+ struct ast_channel *chan2;
+ char tmp[VAR_BUF_SIZE]="";
+ char *s;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Ignoring, since there is no variable to set\n");
+ return 0;
+ }
+
+ stringp = ast_strdupa(data);
+ name = strsep(&stringp,"=");
+ channel = strsep(&stringp,"|");
+ value = strsep(&stringp,"\0");
+ if (channel && value && name) {
+ chan2 = ast_get_channel_by_name_locked(channel);
+ if (chan2) {
+ s = alloca(strlen(value) + 4);
+ if (s) {
+ sprintf(s, "${%s}", value);
+ pbx_substitute_variables_helper(chan2, s, tmp, sizeof(tmp) - 1);
+ }
+ ast_mutex_unlock(&chan2->lock);
+ }
+ pbx_builtin_setvar_helper(chan, name, tmp);
+ }
+
+ return(0);
+}
+
+static int pbx_builtin_setglobalvar(struct ast_channel *chan, void *data)
+{
+ char *name;
+ char *value;
+ char *stringp = NULL;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Ignoring, since there is no variable to set\n");
+ return 0;
+ }
+
+ stringp = data;
+ name = strsep(&stringp, "=");
+ value = strsep(&stringp, "\0");
+
+ pbx_builtin_setvar_helper(NULL, name, value);
+
+ return(0);
+}
+
+static int pbx_builtin_noop(struct ast_channel *chan, void *data)
+{
+ return 0;
+}
+
+
+void pbx_builtin_clear_globals(void)
+{
+ struct ast_var_t *vardata;
+ while (!AST_LIST_EMPTY(&globals)) {
+ vardata = AST_LIST_REMOVE_HEAD(&globals, entries);
+ ast_var_delete(vardata);
+ }
+}
+
+static int pbx_checkcondition(char *condition)
+{
+ if (condition) {
+ if (*condition == '\0') {
+ /* Empty strings are false */
+ return 0;
+ } else if (*condition >= '0' && *condition <= '9') {
+ /* Numbers are evaluated for truth */
+ return atoi(condition);
+ } else {
+ /* Strings are true */
+ return 1;
+ }
+ } else {
+ /* NULL is also false */
+ return 0;
+ }
+}
+
+static int pbx_builtin_gotoif(struct ast_channel *chan, void *data)
+{
+ char *condition, *branch1, *branch2, *branch;
+ char *s;
+ int rc;
+ char *stringp=NULL;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Ignoring, since there is no variable to check\n");
+ return 0;
+ }
+
+ s = ast_strdupa(data);
+ stringp = s;
+ condition = strsep(&stringp,"?");
+ branch1 = strsep(&stringp,":");
+ branch2 = strsep(&stringp,"");
+ branch = pbx_checkcondition(condition) ? branch1 : branch2;
+
+ if (ast_strlen_zero(branch)) {
+ ast_log(LOG_DEBUG, "Not taking any branch\n");
+ return 0;
+ }
+
+ rc = pbx_builtin_goto(chan, branch);
+
+ return rc;
+}
+
+static int pbx_builtin_saynumber(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ char tmp[256];
+ char *number = (char *) NULL;
+ char *options = (char *) NULL;
+
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "SayNumber requires an argument (number)\n");
+ return -1;
+ }
+ ast_copy_string(tmp, (char *) data, sizeof(tmp));
+ number=tmp;
+ strsep(&number, "|");
+ options = strsep(&number, "|");
+ if (options) {
+ if ( strcasecmp(options, "f") && strcasecmp(options,"m") &&
+ strcasecmp(options, "c") && strcasecmp(options, "n") ) {
+ ast_log(LOG_WARNING, "SayNumber gender option is either 'f', 'm', 'c' or 'n'\n");
+ return -1;
+ }
+ }
+ return res = ast_say_number(chan, atoi((char *) tmp), "", chan->language, options);
+}
+
+static int pbx_builtin_saydigits(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+
+ if (data)
+ res = ast_say_digit_str(chan, (char *)data, "", chan->language);
+ return res;
+}
+
+static int pbx_builtin_saycharacters(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+
+ if (data)
+ res = ast_say_character_str(chan, (char *)data, "", chan->language);
+ return res;
+}
+
+static int pbx_builtin_sayphonetic(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+
+ if (data)
+ res = ast_say_phonetic_str(chan, (char *)data, "", chan->language);
+ return res;
+}
+
+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");
+ }
+ AST_LIST_HEAD_INIT_NOLOCK(&globals);
+ ast_cli_register_multiple(pbx_cli, sizeof(pbx_cli) / sizeof(pbx_cli[0]));
+
+ /* Register builtin applications */
+ 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, builtins[x].synopsis, builtins[x].description)) {
+ ast_log(LOG_ERROR, "Unable to register builtin application '%s'\n", builtins[x].name);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Lock context list functions ...
+ */
+int ast_lock_contexts()
+{
+ return ast_mutex_lock(&conlock);
+}
+
+int ast_unlock_contexts()
+{
+ return ast_mutex_unlock(&conlock);
+}
+
+/*
+ * Lock context ...
+ */
+int ast_lock_context(struct ast_context *con)
+{
+ return ast_mutex_lock(&con->lock);
+}
+
+int ast_unlock_context(struct ast_context *con)
+{
+ return ast_mutex_unlock(&con->lock);
+}
+
+/*
+ * Name functions ...
+ */
+const char *ast_get_context_name(struct ast_context *con)
+{
+ return con ? con->name : NULL;
+}
+
+const char *ast_get_extension_name(struct ast_exten *exten)
+{
+ return exten ? exten->exten : NULL;
+}
+
+const char *ast_get_extension_label(struct ast_exten *exten)
+{
+ return exten ? exten->label : NULL;
+}
+
+const char *ast_get_include_name(struct ast_include *inc)
+{
+ return inc ? inc->name : NULL;
+}
+
+const char *ast_get_ignorepat_name(struct ast_ignorepat *ip)
+{
+ return ip ? ip->pattern : NULL;
+}
+
+int ast_get_extension_priority(struct ast_exten *exten)
+{
+ return exten ? exten->priority : -1;
+}
+
+/*
+ * Registrar info functions ...
+ */
+const char *ast_get_context_registrar(struct ast_context *c)
+{
+ return c ? c->registrar : NULL;
+}
+
+const char *ast_get_extension_registrar(struct ast_exten *e)
+{
+ return e ? e->registrar : NULL;
+}
+
+const char *ast_get_include_registrar(struct ast_include *i)
+{
+ return i ? i->registrar : NULL;
+}
+
+const char *ast_get_ignorepat_registrar(struct ast_ignorepat *ip)
+{
+ return ip ? ip->registrar : NULL;
+}
+
+int ast_get_extension_matchcid(struct ast_exten *e)
+{
+ return e ? e->matchcid : 0;
+}
+
+const char *ast_get_extension_cidmatch(struct ast_exten *e)
+{
+ return e ? e->cidmatch : NULL;
+}
+
+const char *ast_get_extension_app(struct ast_exten *e)
+{
+ return e ? e->app : NULL;
+}
+
+void *ast_get_extension_app_data(struct ast_exten *e)
+{
+ return e ? e->data : NULL;
+}
+
+const char *ast_get_switch_name(struct ast_sw *sw)
+{
+ return sw ? sw->name : NULL;
+}
+
+const char *ast_get_switch_data(struct ast_sw *sw)
+{
+ return sw ? sw->data : NULL;
+}
+
+const char *ast_get_switch_registrar(struct ast_sw *sw)
+{
+ return sw ? sw->registrar : NULL;
+}
+
+/*
+ * Walking functions ...
+ */
+struct ast_context *ast_walk_contexts(struct ast_context *con)
+{
+ if (!con)
+ return contexts;
+ else
+ return con->next;
+}
+
+struct ast_exten *ast_walk_context_extensions(struct ast_context *con,
+ struct ast_exten *exten)
+{
+ if (!exten)
+ return con ? con->root : NULL;
+ else
+ return exten->next;
+}
+
+struct ast_sw *ast_walk_context_switches(struct ast_context *con,
+ struct ast_sw *sw)
+{
+ if (!sw)
+ return con ? con->alts : NULL;
+ else
+ return sw->next;
+}
+
+struct ast_exten *ast_walk_extension_priorities(struct ast_exten *exten,
+ struct ast_exten *priority)
+{
+ if (!priority)
+ return exten;
+ else
+ return priority->peer;
+}
+
+struct ast_include *ast_walk_context_includes(struct ast_context *con,
+ struct ast_include *inc)
+{
+ if (!inc)
+ return con ? con->includes : NULL;
+ else
+ return inc->next;
+}
+
+struct ast_ignorepat *ast_walk_context_ignorepats(struct ast_context *con,
+ struct ast_ignorepat *ip)
+{
+ if (!ip)
+ return con ? con->ignorepats : NULL;
+ else
+ return ip->next;
+}
+
+int ast_context_verify_includes(struct ast_context *con)
+{
+ struct ast_include *inc;
+ int res = 0;
+
+ for (inc = ast_walk_context_includes(con, NULL); inc; inc = ast_walk_context_includes(con, inc))
+ if (!ast_context_find(inc->rname)) {
+ res = -1;
+ ast_log(LOG_WARNING, "Context '%s' tries includes nonexistent context '%s'\n",
+ ast_get_context_name(con), inc->rname);
+ }
+ return res;
+}
+
+
+static int __ast_goto_if_exists(struct ast_channel *chan, char *context, char *exten, int priority, int async)
+{
+ int (*goto_func)(struct ast_channel *chan, const char *context, const char *exten, int priority);
+
+ if (!chan)
+ return -2;
+
+ goto_func = (async) ? ast_async_goto : ast_explicit_goto;
+ if (ast_exists_extension(chan, context ? context : chan->context,
+ exten ? exten : chan->exten, priority,
+ chan->cid.cid_num))
+ return goto_func(chan, context ? context : chan->context,
+ exten ? exten : chan->exten, priority);
+ else
+ return -3;
+}
+
+int ast_goto_if_exists(struct ast_channel *chan, char* context, char *exten, int priority) {
+ return __ast_goto_if_exists(chan, context, exten, priority, 0);
+}
+
+int ast_async_goto_if_exists(struct ast_channel *chan, char* context, char *exten, int priority) {
+ return __ast_goto_if_exists(chan, context, exten, priority, 1);
+}
+
+int ast_parseable_goto(struct ast_channel *chan, const char *goto_string)
+{
+ char *s;
+ char *exten, *pri, *context;
+ char *stringp=NULL;
+ int ipri;
+ int mode = 0;
+
+ if (ast_strlen_zero(goto_string)) {
+ ast_log(LOG_WARNING, "Goto requires an argument (optional context|optional extension|priority)\n");
+ return -1;
+ }
+ s = ast_strdupa(goto_string);
+ stringp=s;
+ context = strsep(&stringp, "|");
+ exten = strsep(&stringp, "|");
+ if (!exten) {
+ /* Only a priority in this one */
+ pri = context;
+ exten = NULL;
+ context = NULL;
+ } else {
+ pri = strsep(&stringp, "|");
+ if (!pri) {
+ /* Only an extension and priority in this one */
+ pri = exten;
+ exten = context;
+ context = NULL;
+ }
+ }
+ if (*pri == '+') {
+ mode = 1;
+ pri++;
+ } else if (*pri == '-') {
+ mode = -1;
+ pri++;
+ }
+ if (sscanf(pri, "%d", &ipri) != 1) {
+ if ((ipri = ast_findlabel_extension(chan, context ? context : chan->context, (exten && strcasecmp(exten, "BYEXTENSION")) ? exten : chan->exten,
+ pri, chan->cid.cid_num)) < 1) {
+ ast_log(LOG_WARNING, "Priority '%s' must be a number > 0, or valid label\n", pri);
+ return -1;
+ } else
+ mode = 0;
+ }
+ /* At this point we have a priority and maybe an extension and a context */
+
+ if (exten && !strcasecmp(exten, "BYEXTENSION"))
+ exten = NULL;
+
+ if (mode)
+ ipri = chan->priority + (ipri * mode);
+
+ ast_explicit_goto(chan, context, exten, ipri);
+ ast_cdr_update(chan);
+ return 0;
+
+}