Add new AMI action for app_voicemail
[asterisk/asterisk.git] / apps / app_chanisavail.c
old mode 100755 (executable)
new mode 100644 (file)
index a777773..af4b616
 /*
- * Asterisk -- A telephony toolkit for Linux.
+* Asterisk -- An open source telephony toolkit.
+*
+* Copyright (C) 1999 - 2005, Digium, Inc.
+*
+* Mark Spencer <markster@digium.com>
+* James Golovich <james@gnuinter.net>
+*
+* 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
  *
- * Check if Channel is Available
- * 
- * Copyright (C) 2003, Digium
+ * \brief Check if Channel is Available
  *
- * Mark Spencer <markster@digium.com>
- * James Golovich <james@gnuinter.net>
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License
- *
- */
-
-#include <asterisk/lock.h>
-#include <asterisk/file.h>
-#include <asterisk/logger.h>
-#include <asterisk/channel.h>
-#include <asterisk/pbx.h>
-#include <asterisk/module.h>
-#include <asterisk/app.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
-#include <sys/ioctl.h>
-
-#include <pthread.h>
-
-static char *tdesc = "Check if channel is available";
-
-static char *app = "ChanIsAvail";
+ * \author Mark Spencer <markster@digium.com>
+ * \author James Golovich <james@gnuinter.net>
 
-static char *synopsis = "Check if channel is available";
+ * \ingroup applications
+ */
 
-static char *descrip = 
-"  ChanIsAvail(Technology/resource[&Technology2/resource2...]): \n"
-"Checks is any of the requested channels are available.  If none\n"
-"of the requested channels are available the new priority will be\n"
-"n+101 (unless such a priority does not exist or on error, in which\n"
-"case ChanIsAvail will return -1).  If any of the requested channels\n"
-"are available, the next priority will be n+1, the channel variable\n"
-"${AVAILCHAN} will be set to the name of the available channel and\n"
-"the ChanIsAvail app will return 0.  ${AVAILORIGCHAN} is\n"
-"the canonical channel name that was used to create the channel.\n";
+/*** MODULEINFO
+       <support_level>extended</support_level>
+ ***/
 
-STANDARD_LOCAL_USER;
+#include "asterisk.h"
 
-LOCAL_USER_DECL;
+#include <sys/ioctl.h>
 
-static int chanavail_exec(struct ast_channel *chan, void *data)
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+#include "asterisk/devicestate.h"
+
+static const char app[] = "ChanIsAvail";
+
+/*** DOCUMENTATION
+       <application name="ChanIsAvail" language="en_US">
+               <synopsis>
+                       Check channel availability
+               </synopsis>
+               <syntax>
+                       <parameter name="Technology/Resource" required="true" argsep="&amp;">
+                               <argument name="Technology2/Resource2" multiple="true">
+                                       <para>Optional extra devices to check</para>
+                                       <para>If you need more than one enter them as
+                                       Technology2/Resource2&amp;Technology3/Resource3&amp;.....</para>
+                               </argument>
+                               <para>Specification of the device(s) to check.  These must be in the format of
+                               <literal>Technology/Resource</literal>, where <replaceable>Technology</replaceable>
+                               represents a particular channel driver, and <replaceable>Resource</replaceable>
+                               represents a resource available to that particular channel driver.</para>
+                       </parameter>
+                       <parameter name="options" required="false">
+                               <optionlist>
+                                       <option name="a">
+                                               <para>Check for all available channels, not only the first one</para>
+                                       </option>
+                                       <option name="s">
+                                               <para>Consider the channel unavailable if the channel is in use at all</para>
+                                       </option>
+                                       <option name="t" implies="s">
+                                               <para>Simply checks if specified channels exist in the channel list</para>
+                                       </option>
+                               </optionlist>
+                       </parameter>
+               </syntax>
+               <description>
+                       <para>This application will check to see if any of the specified channels are available.</para>
+                       <para>This application sets the following channel variables:</para>
+                       <variablelist>
+                               <variable name="AVAILCHAN">
+                                       <para>The name of the available channel, if one exists</para>
+                               </variable>
+                               <variable name="AVAILORIGCHAN">
+                                       <para>The canonical channel name that was used to create the channel</para>
+                               </variable>
+                               <variable name="AVAILSTATUS">
+                                       <para>The device state for the device</para>
+                               </variable>
+                               <variable name="AVAILCAUSECODE">
+                                       <para>The cause code returned when requesting the channel</para>
+                               </variable>     
+                       </variablelist>
+               </description>
+       </application>
+ ***/
+
+static int chanavail_exec(struct ast_channel *chan, const char *data)
 {
-       int res=-1;
-       struct localuser *u;
-       char info[512], tmp[512], *peers, *tech, *number, *rest, *cur;
+       int inuse=-1, option_state=0, string_compare=0, option_all_avail=0;
+       int status;
+       char *info, tmp[512], trychan[512], *peers, *tech, *number, *rest, *cur;
+       struct ast_str *tmp_availchan = ast_str_alloca(2048);
+       struct ast_str *tmp_availorig = ast_str_alloca(2048);
+       struct ast_str *tmp_availstat = ast_str_alloca(2048);
+       struct ast_str *tmp_availcause = ast_str_alloca(2048);
        struct ast_channel *tempchan;
+       AST_DECLARE_APP_ARGS(args,
+               AST_APP_ARG(reqchans);
+               AST_APP_ARG(options);
+       );
 
-       if (!data) {
-               ast_log(LOG_WARNING, "ChanIsAvail requires an argument (Zap/1&Zap/2)\n");
+       if (ast_strlen_zero(data)) {
+               ast_log(LOG_WARNING, "ChanIsAvail requires an argument (DAHDI/1&DAHDI/2)\n");
                return -1;
        }
-       LOCAL_USER_ADD(u);
 
-       strncpy(info, (char *)data, sizeof(info)-1);
-       peers = info;
+       info = ast_strdupa(data);
+
+       AST_STANDARD_APP_ARGS(args, info);
+
+       if (args.options) {
+               if (strchr(args.options, 'a')) {
+                       option_all_avail = 1;
+               }
+               if (strchr(args.options, 's')) {
+                       option_state = 1;
+               }
+               if (strchr(args.options, 't')) {
+                       string_compare = 1;
+               }
+       }
+       peers = args.reqchans;
        if (peers) {
                cur = peers;
                do {
@@ -82,57 +150,64 @@ static int chanavail_exec(struct ast_channel *chan, void *data)
                        }
                        *number = '\0';
                        number++;
-                       if ((tempchan = ast_request(tech, chan->nativeformats, number))) {
-                                       pbx_builtin_setvar_helper(chan, "AVAILCHAN", tempchan->name);
-                                       /* Store the originally used channel too */
+
+                       status = AST_DEVICE_UNKNOWN;
+
+                       if (string_compare) {
+                               /* ast_parse_device_state checks for "SIP/1234" as a channel name.
+                                  ast_device_state will ask the SIP driver for the channel state. */
+
+                               snprintf(trychan, sizeof(trychan), "%s/%s",cur,number);
+                               status = inuse = ast_parse_device_state(trychan);
+                       } else if (option_state) {
+                               /* If the pbx says in use then don't bother trying further.
+                                  This is to permit testing if someone's on a call, even if the
+                                  channel can permit more calls (ie callwaiting, sip calls, etc).  */
+
+                               snprintf(trychan, sizeof(trychan), "%s/%s",cur,number);
+                               status = inuse = ast_device_state(trychan);
+                       }
+                       snprintf(tmp, sizeof(tmp), "%d", status);
+                       ast_str_append(&tmp_availstat, 0, "%s%s", ast_str_strlen(tmp_availstat) ? "&" : "", tmp);
+                       if ((inuse <= 1) && (tempchan = ast_request(tech, ast_channel_nativeformats(chan), NULL, chan, number, &status))) {
+                                       ast_str_append(&tmp_availchan, 0, "%s%s", ast_str_strlen(tmp_availchan) ? "&" : "", ast_channel_name(tempchan));
+                                       
                                        snprintf(tmp, sizeof(tmp), "%s/%s", tech, number);
-                                       pbx_builtin_setvar_helper(chan, "AVAILORIGCHAN", tmp);
+                                       ast_str_append(&tmp_availorig, 0, "%s%s", ast_str_strlen(tmp_availorig) ? "&" : "", tmp);
+
+                                       snprintf(tmp, sizeof(tmp), "%d", status);
+                                       ast_str_append(&tmp_availcause, 0, "%s%s", ast_str_strlen(tmp_availcause) ? "&" : "", tmp);
+
                                        ast_hangup(tempchan);
                                        tempchan = NULL;
-                                       res = 1;
-                                       break;
+
+                                       if (!option_all_avail) {
+                                               break;
+                                       }
                        }
                        cur = rest;
                } while (cur);
        }
 
-       if (res < 1) {
-               pbx_builtin_setvar_helper(chan, "AVAILCHAN", "");
-               pbx_builtin_setvar_helper(chan, "AVAILORIGCHAN", "");
-               if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
-                       chan->priority+=100;
-               else
-                       return -1;
-       }
+       pbx_builtin_setvar_helper(chan, "AVAILCHAN", ast_str_buffer(tmp_availchan));
+       /* Store the originally used channel too */
+       pbx_builtin_setvar_helper(chan, "AVAILORIGCHAN", ast_str_buffer(tmp_availorig));
+       pbx_builtin_setvar_helper(chan, "AVAILSTATUS", ast_str_buffer(tmp_availstat));
+       pbx_builtin_setvar_helper(chan, "AVAILCAUSECODE", ast_str_buffer(tmp_availcause));
 
-       LOCAL_USER_REMOVE(u);
        return 0;
 }
 
-int unload_module(void)
+static int unload_module(void)
 {
-       STANDARD_HANGUP_LOCALUSERS;
        return ast_unregister_application(app);
 }
 
-int load_module(void)
+static int load_module(void)
 {
-       return ast_register_application(app, chanavail_exec, synopsis, descrip);
+       return ast_register_application_xml(app, chanavail_exec) ?
+               AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS;
 }
 
-char *description(void)
-{
-       return tdesc;
-}
+AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Check channel availability");
 
-int usecount(void)
-{
-       int res;
-       STANDARD_USECOUNT(res);
-       return res;
-}
-
-char *key()
-{
-       return ASTERISK_GPL_KEY;
-}