00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include "asterisk.h"
00029
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 222186 $")
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 | CMP_STOP;
00071 else
00072 return strcmp(g1->name, name) ? 0 : CMP_MATCH | CMP_STOP;
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 | CMP_STOP;
00087 else
00088 return strcmp(e1->name, name) ? 0 : CMP_MATCH | CMP_STOP;
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
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
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
00192 args.op = "add";
00193
00194
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
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");