Wed Oct 28 11:45:38 2009

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

Generated on Wed Oct 28 11:45:38 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6