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: 140490 $")
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
00130 return res;
00131 }
00132
00133 static int dialgroup_refreshdb(struct ast_channel *chan, const char *cdialgroup)
00134 {
00135 int len = 500, res = 0;
00136 char *buf = NULL;
00137 char *dialgroup = ast_strdupa(cdialgroup);
00138
00139 do {
00140 len *= 2;
00141 buf = ast_realloc(buf, len);
00142
00143 if ((res = dialgroup_read(chan, "", dialgroup, buf, len)) < 0) {
00144 ast_free(buf);
00145 return -1;
00146 }
00147 } while (res == 1);
00148
00149 if (ast_strlen_zero(buf)) {
00150 ast_db_del("dialgroup", cdialgroup);
00151 } else {
00152 ast_db_put("dialgroup", cdialgroup, buf);
00153 }
00154 ast_free(buf);
00155 return 0;
00156 }
00157
00158 static int dialgroup_write(struct ast_channel *chan, const char *cmd, char *data, const char *cvalue)
00159 {
00160 struct group *grhead;
00161 struct group_entry *entry;
00162 int j, needrefresh = 1;
00163 AST_DECLARE_APP_ARGS(args,
00164 AST_APP_ARG(group);
00165 AST_APP_ARG(op);
00166 );
00167 AST_DECLARE_APP_ARGS(inter,
00168 AST_APP_ARG(faces)[100];
00169 );
00170 char *value = ast_strdupa(cvalue);
00171
00172 AST_STANDARD_APP_ARGS(args, data);
00173 AST_NONSTANDARD_APP_ARGS(inter, value, '&');
00174
00175 if (!(grhead = ao2_find(group_container, args.group, 0))) {
00176
00177 grhead = ao2_alloc(sizeof(*grhead), group_destroy);
00178 if (!grhead)
00179 return -1;
00180 grhead->entries = ao2_container_alloc(37, entry_hash_fn, entry_cmp_fn);
00181 if (!grhead->entries) {
00182 ao2_ref(grhead, -1);
00183 return -1;
00184 }
00185 ast_copy_string(grhead->name, args.group, sizeof(grhead->name));
00186 ao2_link(group_container, grhead);
00187 }
00188
00189 if (ast_strlen_zero(args.op)) {
00190
00191 args.op = "add";
00192
00193
00194 ao2_ref(grhead->entries, -1);
00195 if (!(grhead->entries = ao2_container_alloc(37, entry_hash_fn, entry_cmp_fn))) {
00196 ao2_unlink(group_container, grhead);
00197 ao2_ref(grhead, -1);
00198 return -1;
00199 }
00200 }
00201
00202 if (strcasecmp(args.op, "add") == 0) {
00203 for (j = 0; j < inter.argc; j++) {
00204 if ((entry = ao2_alloc(sizeof(*entry), NULL))) {
00205 ast_copy_string(entry->name, inter.faces[j], sizeof(entry->name));
00206 ao2_link(grhead->entries, entry);
00207 ao2_ref(entry, -1);
00208 } else {
00209 ast_log(LOG_WARNING, "Unable to add '%s' to dialgroup '%s'\n", inter.faces[j], grhead->name);
00210 }
00211 }
00212 } else if (strncasecmp(args.op, "del", 3) == 0) {
00213 for (j = 0; j < inter.argc; j++) {
00214 if ((entry = ao2_find(grhead->entries, inter.faces[j], OBJ_UNLINK))) {
00215 ao2_ref(entry, -1);
00216 } else {
00217 ast_log(LOG_WARNING, "Interface '%s' not found in dialgroup '%s'\n", inter.faces[j], grhead->name);
00218 }
00219 }
00220 } else {
00221 ast_log(LOG_ERROR, "Unrecognized operation: %s\n", args.op);
00222 needrefresh = 0;
00223 }
00224 ao2_ref(grhead, -1);
00225
00226 if (needrefresh) {
00227 dialgroup_refreshdb(chan, args.group);
00228 }
00229
00230 return 0;
00231 }
00232
00233 static struct ast_custom_function dialgroup_function = {
00234 .name = "DIALGROUP",
00235 .synopsis = "Manages a group of users for dialing",
00236 .syntax = "DIALGROUP(<group>[,op])",
00237 .desc =
00238 " DIALGROUP presents an interface meant to be used in concert with the Dial\n"
00239 "application, by presenting a list of channels which should be dialled when\n"
00240 "referenced.\n"
00241 " When DIALGROUP is read from, the argument is interpreted as the particular\n"
00242 "group for which a dial should be attempted. When DIALGROUP is written to\n"
00243 "with no arguments, the entire list is replaced with the argument specified.\n"
00244 "Other operations are as follows:\n"
00245 " add - add a channel name or interface (write-only)\n"
00246 " del - remove a channel name or interface (write-only)\n\n"
00247 "Functionality is similar to a queue, except that when no interfaces are\n"
00248 "available, execution may continue in the dialplan. This is useful when\n"
00249 "you want certain people to be the first to answer any calls, with immediate\n"
00250 "fallback to a queue when the front line people are busy or unavailable, but\n"
00251 "you still want front line people to log in and out of that group, just like\n"
00252 "a queue.\n",
00253 .read = dialgroup_read,
00254 .write = dialgroup_write,
00255 };
00256
00257 static int unload_module(void)
00258 {
00259 int res = ast_custom_function_unregister(&dialgroup_function);
00260 ao2_ref(group_container, -1);
00261 return res;
00262 }
00263
00264 static int load_module(void)
00265 {
00266 struct ast_db_entry *dbtree, *tmp;
00267 char groupname[AST_MAX_EXTENSION], *ptr;
00268
00269 if ((group_container = ao2_container_alloc(37, group_hash_fn, group_cmp_fn))) {
00270
00271 if ((dbtree = ast_db_gettree("dialgroup", NULL))) {
00272 for (tmp = dbtree; tmp; tmp = tmp->next) {
00273 ast_copy_string(groupname, tmp->key, sizeof(groupname));
00274 if ((ptr = strrchr(groupname, '/'))) {
00275 ptr++;
00276 dialgroup_write(NULL, "", ptr, tmp->data);
00277 }
00278 }
00279 ast_db_freetree(dbtree);
00280 }
00281 return ast_custom_function_register(&dialgroup_function);
00282 } else {
00283 return AST_MODULE_LOAD_DECLINE;
00284 }
00285 }
00286
00287 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialgroup dialplan function");