Tue Aug 20 16:34:33 2013

Asterisk developer's documentation


func_dialgroup.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2007, Tilghman Lesher
00005  *
00006  * Tilghman Lesher <func_dialgroup__200709@the-tilghman.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Dial group dialplan function
00022  *
00023  * \author Tilghman Lesher <func_dialgroup__200709@the-tilghman.com>
00024  *
00025  * \ingroup functions
00026  */
00027 
00028 /*** MODULEINFO
00029    <support_level>core</support_level>
00030  ***/
00031 
00032 #include "asterisk.h"
00033 
00034 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 366880 $")
00035 
00036 #include <sys/stat.h>
00037 
00038 #include "asterisk/module.h"
00039 #include "asterisk/channel.h"
00040 #include "asterisk/pbx.h"
00041 #include "asterisk/utils.h"
00042 #include "asterisk/app.h"
00043 #include "asterisk/astobj2.h"
00044 #include "asterisk/astdb.h"
00045 
00046 /*** DOCUMENTATION
00047    <function name="DIALGROUP" language="en_US">
00048       <synopsis>
00049          Manages a group of users for dialing.
00050       </synopsis>
00051       <syntax>
00052          <parameter name="group" required="true" />
00053          <parameter name="op">
00054             <para>The operation name, possible values are:</para>
00055             <para><literal>add</literal> - add a channel name or interface (write-only)</para>
00056             <para><literal>del</literal> - remove a channel name or interface (write-only)</para>
00057          </parameter>
00058       </syntax>
00059       <description>
00060          <para>Presents an interface meant to be used in concert with the Dial
00061          application, by presenting a list of channels which should be dialled when
00062          referenced.</para>
00063          <para>When DIALGROUP is read from, the argument is interpreted as the particular
00064          <replaceable>group</replaceable> for which a dial should be attempted.  When DIALGROUP is written to
00065          with no arguments, the entire list is replaced with the argument specified.</para>
00066          <para>Functionality is similar to a queue, except that when no interfaces are
00067          available, execution may continue in the dialplan.  This is useful when
00068          you want certain people to be the first to answer any calls, with immediate
00069          fallback to a queue when the front line people are busy or unavailable, but
00070          you still want front line people to log in and out of that group, just like
00071          a queue.</para>
00072          <para>Example:</para>
00073          <para>exten => 1,1,Set(DIALGROUP(mygroup,add)=SIP/10)</para>
00074          <para>exten => 1,n,Set(DIALGROUP(mygroup,add)=SIP/20)</para>
00075          <para>exten => 1,n,Dial(${DIALGROUP(mygroup)})</para>
00076       </description>
00077    </function>
00078  ***/
00079 
00080 static struct ao2_container *group_container = NULL;
00081 
00082 struct group_entry {
00083    char name[AST_CHANNEL_NAME];
00084 };
00085 
00086 struct group {
00087    char name[AST_MAX_EXTENSION];
00088    struct ao2_container *entries;
00089 };
00090 
00091 static void group_destroy(void *vgroup)
00092 {
00093    struct group *group = vgroup;
00094    ao2_ref(group->entries, -1);
00095 }
00096 
00097 static int group_hash_fn(const void *obj, const int flags)
00098 {
00099    const struct group *g = obj;
00100    return ast_str_hash(g->name);
00101 }
00102 
00103 static int group_cmp_fn(void *obj1, void *name2, int flags)
00104 {
00105    struct group *g1 = obj1, *g2 = name2;
00106    char *name = name2;
00107    if (flags & OBJ_POINTER)
00108       return strcmp(g1->name, g2->name) ? 0 : CMP_MATCH | CMP_STOP;
00109    else
00110       return strcmp(g1->name, name) ? 0 : CMP_MATCH | CMP_STOP;
00111 }
00112 
00113 static int entry_hash_fn(const void *obj, const int flags)
00114 {
00115    const struct group_entry *e = obj;
00116    return ast_str_hash(e->name);
00117 }
00118 
00119 static int entry_cmp_fn(void *obj1, void *name2, int flags)
00120 {
00121    struct group_entry *e1 = obj1, *e2 = name2;
00122    char *name = name2;
00123    if (flags & OBJ_POINTER)
00124       return strcmp(e1->name, e2->name) ? 0 : CMP_MATCH | CMP_STOP;
00125    else
00126       return strcmp(e1->name, name) ? 0 : CMP_MATCH | CMP_STOP;
00127 }
00128 
00129 static int dialgroup_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00130 {
00131    struct ao2_iterator i;
00132    struct group *grhead = ao2_find(group_container, data, 0);
00133    struct group_entry *entry;
00134    size_t bufused = 0;
00135    int trunc_warning = 0;
00136    int res = 0;
00137 
00138    if (!grhead) {
00139       if (!ast_strlen_zero(cmd)) {
00140          ast_log(LOG_WARNING, "No such dialgroup '%s'\n", data);
00141       }
00142       return -1;
00143    }
00144 
00145    buf[0] = '\0';
00146 
00147    i = ao2_iterator_init(grhead->entries, 0);
00148    while ((entry = ao2_iterator_next(&i))) {
00149       int tmp = strlen(entry->name);
00150       /* Ensure that we copy only complete names, not partials */
00151       if (len - bufused > tmp + 2) {
00152          if (bufused != 0)
00153             buf[bufused++] = '&';
00154          ast_copy_string(buf + bufused, entry->name, len - bufused);
00155          bufused += tmp;
00156       } else if (trunc_warning++ == 0) {
00157          if (!ast_strlen_zero(cmd)) {
00158             ast_log(LOG_WARNING, "Dialgroup '%s' is too large.  Truncating list.\n", data);
00159          } else {
00160             res = 1;
00161             ao2_ref(entry, -1);
00162             break;
00163          }
00164       }
00165       ao2_ref(entry, -1);
00166    }
00167    ao2_iterator_destroy(&i);
00168    ao2_ref(grhead, -1);
00169 
00170    return res;
00171 }
00172 
00173 static int dialgroup_refreshdb(struct ast_channel *chan, const char *cdialgroup)
00174 {
00175    int len = 500, res = 0;
00176    char *buf = NULL;
00177    char *dialgroup = ast_strdupa(cdialgroup);
00178 
00179    do {
00180       len *= 2;
00181       buf = ast_realloc(buf, len);
00182 
00183       if ((res = dialgroup_read(chan, "", dialgroup, buf, len)) < 0) {
00184          ast_free(buf);
00185          return -1;
00186       }
00187    } while (res == 1);
00188 
00189    if (ast_strlen_zero(buf)) {
00190       ast_db_del("dialgroup", cdialgroup);
00191    } else {
00192       ast_db_put("dialgroup", cdialgroup, buf);
00193    }
00194    ast_free(buf);
00195    return 0;
00196 }
00197 
00198 static int dialgroup_write(struct ast_channel *chan, const char *cmd, char *data, const char *cvalue)
00199 {
00200    struct group *grhead;
00201    struct group_entry *entry;
00202    int j, needrefresh = 1;
00203    AST_DECLARE_APP_ARGS(args,
00204       AST_APP_ARG(group);
00205       AST_APP_ARG(op);
00206    );
00207    AST_DECLARE_APP_ARGS(inter,
00208       AST_APP_ARG(faces)[100];
00209    );
00210    char *value = ast_strdupa(cvalue);
00211 
00212    AST_STANDARD_APP_ARGS(args, data);
00213    AST_NONSTANDARD_APP_ARGS(inter, value, '&');
00214 
00215    if (!(grhead = ao2_find(group_container, args.group, 0))) {
00216       /* Create group */
00217       grhead = ao2_alloc(sizeof(*grhead), group_destroy);
00218       if (!grhead)
00219          return -1;
00220       grhead->entries = ao2_container_alloc(37, entry_hash_fn, entry_cmp_fn);
00221       if (!grhead->entries) {
00222          ao2_ref(grhead, -1);
00223          return -1;
00224       }
00225       ast_copy_string(grhead->name, args.group, sizeof(grhead->name));
00226       ao2_link(group_container, grhead);
00227    }
00228 
00229    if (ast_strlen_zero(args.op)) {
00230       /* Wholesale replacement of the group */
00231       args.op = "add";
00232 
00233       /* Remove all existing */
00234       ao2_ref(grhead->entries, -1);
00235       if (!(grhead->entries = ao2_container_alloc(37, entry_hash_fn, entry_cmp_fn))) {
00236          ao2_unlink(group_container, grhead);
00237          ao2_ref(grhead, -1);
00238          return -1;
00239       }
00240    }
00241 
00242    if (strcasecmp(args.op, "add") == 0) {
00243       for (j = 0; j < inter.argc; j++) {
00244          /* Eliminate duplicates */
00245          if ((entry = ao2_find(grhead->entries, inter.faces[j], 0))) {
00246             ao2_ref(entry, -1);
00247             continue;
00248          }
00249          if ((entry = ao2_alloc(sizeof(*entry), NULL))) {
00250             ast_copy_string(entry->name, inter.faces[j], sizeof(entry->name));
00251             ao2_link(grhead->entries, entry);
00252             ao2_ref(entry, -1);
00253          } else {
00254             ast_log(LOG_WARNING, "Unable to add '%s' to dialgroup '%s'\n", inter.faces[j], grhead->name);
00255          }
00256       }
00257    } else if (strncasecmp(args.op, "del", 3) == 0) {
00258       for (j = 0; j < inter.argc; j++) {
00259          if ((entry = ao2_find(grhead->entries, inter.faces[j], OBJ_UNLINK))) {
00260             ao2_ref(entry, -1);
00261          } else {
00262             ast_log(LOG_WARNING, "Interface '%s' not found in dialgroup '%s'\n", inter.faces[j], grhead->name);
00263          }
00264       }
00265    } else {
00266       ast_log(LOG_ERROR, "Unrecognized operation: %s\n", args.op);
00267       needrefresh = 0;
00268    }
00269    ao2_ref(grhead, -1);
00270 
00271    if (needrefresh) {
00272       dialgroup_refreshdb(chan, args.group);
00273    }
00274 
00275    return 0;
00276 }
00277 
00278 static struct ast_custom_function dialgroup_function = {
00279    .name = "DIALGROUP",
00280    .read = dialgroup_read,
00281    .write = dialgroup_write,
00282 };
00283 
00284 static int unload_module(void)
00285 {
00286    int res = ast_custom_function_unregister(&dialgroup_function);
00287    ao2_ref(group_container, -1);
00288    return res;
00289 }
00290 
00291 static int load_module(void)
00292 {
00293    struct ast_db_entry *dbtree, *tmp;
00294    char groupname[AST_MAX_EXTENSION], *ptr;
00295 
00296    if ((group_container = ao2_container_alloc(37, group_hash_fn, group_cmp_fn))) {
00297       /* Refresh groups from astdb */
00298       if ((dbtree = ast_db_gettree("dialgroup", NULL))) {
00299          for (tmp = dbtree; tmp; tmp = tmp->next) {
00300             ast_copy_string(groupname, tmp->key, sizeof(groupname));
00301             if ((ptr = strrchr(groupname, '/'))) {
00302                ptr++;
00303                dialgroup_write(NULL, "", ptr, tmp->data);
00304             }
00305          }
00306          ast_db_freetree(dbtree);
00307       }
00308       return ast_custom_function_register(&dialgroup_function);
00309    } else {
00310       return AST_MODULE_LOAD_DECLINE;
00311    }
00312 }
00313 
00314 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialgroup dialplan function");

Generated on 20 Aug 2013 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1