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 #include <string.h>
00027 #include <ctype.h>
00028 #include <stdlib.h>
00029 #include <stdio.h>
00030
00031 #include "asterisk.h"
00032
00033 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 7221 $")
00034
00035 #include "asterisk/lock.h"
00036 #include "asterisk/file.h"
00037 #include "asterisk/logger.h"
00038 #include "asterisk/channel.h"
00039 #include "asterisk/pbx.h"
00040 #include "asterisk/module.h"
00041 #include "asterisk/config.h"
00042 #include "asterisk/say.h"
00043 #include "asterisk/utils.h"
00044
00045 static char *tdesc = "Extension Directory";
00046 static char *app = "Directory";
00047
00048 static char *synopsis = "Provide directory of voicemail extensions";
00049 static char *descrip =
00050 " Directory(vm-context[|dial-context[|options]]): This application will present\n"
00051 "the calling channel with a directory of extensions from which they can search\n"
00052 "by name. The list of names and corresponding extensions is retrieved from the\n"
00053 "voicemail configuration file, voicemail.conf.\n"
00054 " This applicaiton will immediate exit if one of the following DTMF digits are\n"
00055 "received and the extension to jump to exists:\n"
00056 " 0 - Jump to the 'o' extension, if it exists.\n"
00057 " * - Jump to the 'a' extension, if it exists.\n\n"
00058 " Parameters:\n"
00059 " vm-context - This is the context within voicemail.conf to use for the\n"
00060 " Directory.\n"
00061 " dial-context - This is the dialplan context to use when looking for an\n"
00062 " extension that the user has selected, or when jumping to the\n"
00063 " 'o' or 'a' extension.\n\n"
00064 " Options:\n"
00065 " f - Allow the caller to enter the first name of a user in the directory\n"
00066 " instead of using the last name.\n";
00067
00068
00069
00070
00071 #define VOICEMAIL_CONFIG "voicemail.conf"
00072
00073
00074 #define NUMDIGITS 3
00075
00076 STANDARD_LOCAL_USER;
00077
00078 LOCAL_USER_DECL;
00079
00080 static char *convert(char *lastname)
00081 {
00082 char *tmp;
00083 int lcount = 0;
00084 tmp = malloc(NUMDIGITS + 1);
00085 if (tmp) {
00086 while((*lastname > 32) && lcount < NUMDIGITS) {
00087 switch(toupper(*lastname)) {
00088 case '1':
00089 tmp[lcount++] = '1';
00090 break;
00091 case '2':
00092 case 'A':
00093 case 'B':
00094 case 'C':
00095 tmp[lcount++] = '2';
00096 break;
00097 case '3':
00098 case 'D':
00099 case 'E':
00100 case 'F':
00101 tmp[lcount++] = '3';
00102 break;
00103 case '4':
00104 case 'G':
00105 case 'H':
00106 case 'I':
00107 tmp[lcount++] = '4';
00108 break;
00109 case '5':
00110 case 'J':
00111 case 'K':
00112 case 'L':
00113 tmp[lcount++] = '5';
00114 break;
00115 case '6':
00116 case 'M':
00117 case 'N':
00118 case 'O':
00119 tmp[lcount++] = '6';
00120 break;
00121 case '7':
00122 case 'P':
00123 case 'Q':
00124 case 'R':
00125 case 'S':
00126 tmp[lcount++] = '7';
00127 break;
00128 case '8':
00129 case 'T':
00130 case 'U':
00131 case 'V':
00132 tmp[lcount++] = '8';
00133 break;
00134 case '9':
00135 case 'W':
00136 case 'X':
00137 case 'Y':
00138 case 'Z':
00139 tmp[lcount++] = '9';
00140 break;
00141 }
00142 lastname++;
00143 }
00144 tmp[lcount] = '\0';
00145 }
00146 return tmp;
00147 }
00148
00149
00150
00151
00152
00153
00154 static int play_mailbox_owner(struct ast_channel *chan, char *context, char *dialcontext, char *ext, char *name) {
00155 int res = 0;
00156 int loop = 3;
00157 char fn[256];
00158 char fn2[256];
00159
00160
00161 snprintf(fn, sizeof(fn), "%s/voicemail/%s/%s/greet",
00162 (char *)ast_config_AST_SPOOL_DIR, context, ext);
00163
00164
00165 snprintf(fn2, sizeof(fn2), "%s/vm/%s/greet",
00166 (char *)ast_config_AST_SPOOL_DIR, ext);
00167
00168 if (ast_fileexists(fn, NULL, chan->language) > 0) {
00169 res = ast_streamfile(chan, fn, chan->language);
00170 if (!res) {
00171 res = ast_waitstream(chan, AST_DIGIT_ANY);
00172 }
00173 ast_stopstream(chan);
00174 } else if (ast_fileexists(fn2, NULL, chan->language) > 0) {
00175 res = ast_streamfile(chan, fn2, chan->language);
00176 if (!res) {
00177 res = ast_waitstream(chan, AST_DIGIT_ANY);
00178 }
00179 ast_stopstream(chan);
00180 } else {
00181 res = ast_say_character_str(chan, !ast_strlen_zero(name) ? name : ext,
00182 AST_DIGIT_ANY, chan->language);
00183 }
00184
00185 while (loop) {
00186 if (!res) {
00187 res = ast_streamfile(chan, "dir-instr", chan->language);
00188 }
00189 if (!res) {
00190 res = ast_waitstream(chan, AST_DIGIT_ANY);
00191 }
00192 if (!res) {
00193 res = ast_waitfordigit(chan, 3000);
00194 }
00195 ast_stopstream(chan);
00196
00197 if (res > -1) {
00198 switch (res) {
00199 case '1':
00200
00201 loop = 0;
00202 if (ast_goto_if_exists(chan, dialcontext, ext, 1)) {
00203 ast_log(LOG_WARNING,
00204 "Can't find extension '%s' in context '%s'. "
00205 "Did you pass the wrong context to Directory?\n",
00206 ext, dialcontext);
00207 res = -1;
00208 }
00209 break;
00210
00211 case '*':
00212
00213 loop = 0;
00214 break;
00215
00216 default:
00217
00218 res = 0;
00219 loop--;
00220 break;
00221 }
00222 }
00223 else {
00224
00225 loop = 0;
00226 }
00227 }
00228
00229 return(res);
00230 }
00231
00232 static struct ast_config *realtime_directory(char *context)
00233 {
00234 struct ast_config *cfg;
00235 struct ast_config *rtdata;
00236 struct ast_category *cat;
00237 struct ast_variable *var;
00238 char *mailbox;
00239 char *fullname;
00240 char *hidefromdir;
00241 char tmp[100];
00242
00243
00244 cfg = ast_config_load(VOICEMAIL_CONFIG);
00245
00246 if (!cfg) {
00247
00248 ast_log(LOG_WARNING, "Loading config failed.\n");
00249 return NULL;
00250 }
00251
00252
00253
00254 rtdata = ast_load_realtime_multientry("voicemail", "mailbox LIKE", "%", "context", context, NULL);
00255
00256
00257 if (!rtdata)
00258 return cfg;
00259
00260
00261 cat = ast_category_get(cfg, context);
00262 if (!cat) {
00263 cat = ast_category_new(context);
00264 if (!cat) {
00265 ast_log(LOG_WARNING, "Out of memory\n");
00266 ast_config_destroy(cfg);
00267 return NULL;
00268 }
00269 ast_category_append(cfg, cat);
00270 }
00271
00272 mailbox = ast_category_browse(rtdata, NULL);
00273 while (mailbox) {
00274 fullname = ast_variable_retrieve(rtdata, mailbox, "fullname");
00275 hidefromdir = ast_variable_retrieve(rtdata, mailbox, "hidefromdir");
00276 snprintf(tmp, sizeof(tmp), "no-password,%s,hidefromdir=%s",
00277 fullname ? fullname : "",
00278 hidefromdir ? hidefromdir : "no");
00279 var = ast_variable_new(mailbox, tmp);
00280 if (var)
00281 ast_variable_append(cat, var);
00282 else
00283 ast_log(LOG_WARNING, "Out of memory adding mailbox '%s'\n", mailbox);
00284 mailbox = ast_category_browse(rtdata, mailbox);
00285 }
00286 ast_config_destroy(rtdata);
00287
00288 return cfg;
00289 }
00290
00291 static int do_directory(struct ast_channel *chan, struct ast_config *cfg, char *context, char *dialcontext, char digit, int last)
00292 {
00293
00294 char ext[NUMDIGITS + 1];
00295 char name[80] = "";
00296 struct ast_variable *v;
00297 int res;
00298 int found=0;
00299 int lastuserchoice = 0;
00300 char *start, *pos, *conv,*stringp=NULL;
00301
00302 if (ast_strlen_zero(context)) {
00303 ast_log(LOG_WARNING,
00304 "Directory must be called with an argument "
00305 "(context in which to interpret extensions)\n");
00306 return -1;
00307 }
00308 if (digit == '0') {
00309 if (!ast_goto_if_exists(chan, chan->context, "o", 1) ||
00310 (!ast_strlen_zero(chan->macrocontext) &&
00311 !ast_goto_if_exists(chan, chan->macrocontext, "o", 1))) {
00312 return 0;
00313 } else {
00314 ast_log(LOG_WARNING, "Can't find extension 'o' in current context. "
00315 "Not Exiting the Directory!\n");
00316 res = 0;
00317 }
00318 }
00319 if (digit == '*') {
00320 if (!ast_goto_if_exists(chan, chan->context, "a", 1) ||
00321 (!ast_strlen_zero(chan->macrocontext) &&
00322 !ast_goto_if_exists(chan, chan->macrocontext, "a", 1))) {
00323 return 0;
00324 } else {
00325 ast_log(LOG_WARNING, "Can't find extension 'a' in current context. "
00326 "Not Exiting the Directory!\n");
00327 res = 0;
00328 }
00329 }
00330 memset(ext, 0, sizeof(ext));
00331 ext[0] = digit;
00332 res = 0;
00333 if (ast_readstring(chan, ext + 1, NUMDIGITS - 1, 3000, 3000, "#") < 0) res = -1;
00334 if (!res) {
00335
00336 v = ast_variable_browse(cfg, context);
00337 while(v && !res) {
00338
00339 while(v) {
00340
00341 start = strdup(v->value);
00342 if (start && !strcasestr(start, "hidefromdir=yes")) {
00343 stringp=start;
00344 strsep(&stringp, ",");
00345 pos = strsep(&stringp, ",");
00346 if (pos) {
00347 ast_copy_string(name, pos, sizeof(name));
00348
00349 if (last && strrchr(pos,' '))
00350 pos = strrchr(pos, ' ') + 1;
00351 conv = convert(pos);
00352 if (conv) {
00353 if (!strcmp(conv, ext)) {
00354
00355 found++;
00356 free(conv);
00357 free(start);
00358 break;
00359 }
00360 free(conv);
00361 }
00362 }
00363 free(start);
00364 }
00365 v = v->next;
00366 }
00367
00368 if (v) {
00369
00370 res = play_mailbox_owner(chan, context, dialcontext, v->name, name);
00371 switch (res) {
00372 case -1:
00373
00374
00375
00376 lastuserchoice = 0;
00377 break;
00378 case '1':
00379
00380
00381
00382
00383 lastuserchoice = res;
00384 break;
00385 case '*':
00386
00387 lastuserchoice = res;
00388 res = 0;
00389 break;
00390 default:
00391 break;
00392 }
00393 v = v->next;
00394 }
00395 }
00396
00397 if (lastuserchoice != '1') {
00398 if (found)
00399 res = ast_streamfile(chan, "dir-nomore", chan->language);
00400 else
00401 res = ast_streamfile(chan, "dir-nomatch", chan->language);
00402 if (!res)
00403 res = 1;
00404 return res;
00405 }
00406 return 0;
00407 }
00408 return res;
00409 }
00410
00411 static int directory_exec(struct ast_channel *chan, void *data)
00412 {
00413 int res = 0;
00414 struct localuser *u;
00415 struct ast_config *cfg;
00416 int last = 1;
00417 char *context, *dialcontext, *dirintro, *options;
00418
00419 if (ast_strlen_zero(data)) {
00420 ast_log(LOG_WARNING, "Directory requires an argument (context[,dialcontext])\n");
00421 return -1;
00422 }
00423
00424 LOCAL_USER_ADD(u);
00425
00426 context = ast_strdupa(data);
00427 dialcontext = strchr(context, '|');
00428 if (dialcontext) {
00429 *dialcontext = '\0';
00430 dialcontext++;
00431 options = strchr(dialcontext, '|');
00432 if (options) {
00433 *options = '\0';
00434 options++;
00435 if (strchr(options, 'f'))
00436 last = 0;
00437 }
00438 } else
00439 dialcontext = context;
00440
00441 cfg = realtime_directory(context);
00442 if (!cfg) {
00443 LOCAL_USER_REMOVE(u);
00444 return -1;
00445 }
00446
00447 dirintro = ast_variable_retrieve(cfg, context, "directoryintro");
00448 if (ast_strlen_zero(dirintro))
00449 dirintro = ast_variable_retrieve(cfg, "general", "directoryintro");
00450 if (ast_strlen_zero(dirintro)) {
00451 if (last)
00452 dirintro = "dir-intro";
00453 else
00454 dirintro = "dir-intro-fn";
00455 }
00456
00457 if (chan->_state != AST_STATE_UP)
00458 res = ast_answer(chan);
00459
00460 for (;;) {
00461 if (!res)
00462 res = ast_streamfile(chan, dirintro, chan->language);
00463 if (!res)
00464 res = ast_waitstream(chan, AST_DIGIT_ANY);
00465 ast_stopstream(chan);
00466 if (!res)
00467 res = ast_waitfordigit(chan, 5000);
00468 if (res > 0) {
00469 res = do_directory(chan, cfg, context, dialcontext, res, last);
00470 if (res > 0) {
00471 res = ast_waitstream(chan, AST_DIGIT_ANY);
00472 ast_stopstream(chan);
00473 if (res >= 0) {
00474 continue;
00475 }
00476 }
00477 }
00478 break;
00479 }
00480 ast_config_destroy(cfg);
00481 LOCAL_USER_REMOVE(u);
00482 return res;
00483 }
00484
00485 int unload_module(void)
00486 {
00487 int res;
00488
00489 res = ast_unregister_application(app);
00490
00491 STANDARD_HANGUP_LOCALUSERS;
00492
00493 return res;
00494 }
00495
00496 int load_module(void)
00497 {
00498 return ast_register_application(app, directory_exec, synopsis, descrip);
00499 }
00500
00501 char *description(void)
00502 {
00503 return tdesc;
00504 }
00505
00506 int usecount(void)
00507 {
00508 int res;
00509 STANDARD_USECOUNT(res);
00510 return res;
00511 }
00512
00513 char *key()
00514 {
00515 return ASTERISK_GPL_KEY;
00516 }