Mon Jun 27 16:50:53 2011

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

Generated on Mon Jun 27 16:50:53 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7