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: 165322 $")
00031
00032 #include <ctype.h>
00033
00034 #include "asterisk/paths.h"
00035 #include "asterisk/file.h"
00036 #include "asterisk/pbx.h"
00037 #include "asterisk/module.h"
00038 #include "asterisk/say.h"
00039 #include "asterisk/app.h"
00040
00041 #ifdef ODBC_STORAGE
00042 #include <sys/mman.h>
00043 #include "asterisk/res_odbc.h"
00044
00045 static char odbc_database[80] = "asterisk";
00046 static char odbc_table[80] = "voicemessages";
00047 static char vmfmts[80] = "wav";
00048 #endif
00049
00050 static char *app = "Directory";
00051
00052 static char *synopsis = "Provide directory of voicemail extensions";
00053 static char *descrip =
00054 " Directory(vm-context[,dial-context[,options]]): This application will present\n"
00055 "the calling channel with a directory of extensions from which they can search\n"
00056 "by name. The list of names and corresponding extensions is retrieved from the\n"
00057 "voicemail configuration file, voicemail.conf.\n"
00058 " This application will immediately exit if one of the following DTMF digits are\n"
00059 "received and the extension to jump to exists:\n"
00060 " 0 - Jump to the 'o' extension, if it exists.\n"
00061 " * - Jump to the 'a' extension, if it exists.\n\n"
00062 " Parameters:\n"
00063 " vm-context - This is the context within voicemail.conf to use for the\n"
00064 " Directory.\n"
00065 " dial-context - This is the dialplan context to use when looking for an\n"
00066 " extension that the user has selected, or when jumping to the\n"
00067 " 'o' or 'a' extension.\n\n"
00068 " Options:\n"
00069 " e - In addition to the name, also read the extension number to the\n"
00070 " caller before presenting dialing options.\n"
00071 " f - Allow the caller to enter the first name of a user in the directory\n"
00072 " instead of using the last name.\n"
00073 " m - Instead of reading each name sequentially and asking for confirmation,\n"
00074 " create a menu of up to 8 names.\n";
00075
00076
00077
00078
00079 #define VOICEMAIL_CONFIG "voicemail.conf"
00080
00081
00082 #define NUMDIGITS 3
00083
00084 enum {
00085 OPT_LISTBYFIRSTNAME = (1 << 0),
00086 OPT_SAYEXTENSION = (1 << 1),
00087 OPT_FROMVOICEMAIL = (1 << 2),
00088 OPT_SELECTFROMMENU = (1 << 3),
00089 } directory_option_flags;
00090
00091 struct directory_item {
00092 char exten[AST_MAX_EXTENSION + 1];
00093 char name[AST_MAX_EXTENSION + 1];
00094 char key[50];
00095
00096 AST_LIST_ENTRY(directory_item) entry;
00097 };
00098
00099 AST_APP_OPTIONS(directory_app_options, {
00100 AST_APP_OPTION('f', OPT_LISTBYFIRSTNAME),
00101 AST_APP_OPTION('e', OPT_SAYEXTENSION),
00102 AST_APP_OPTION('v', OPT_FROMVOICEMAIL),
00103 AST_APP_OPTION('m', OPT_SELECTFROMMENU),
00104 });
00105
00106 #ifdef ODBC_STORAGE
00107 struct generic_prepare_struct {
00108 const char *sql;
00109 const char *param;
00110 };
00111
00112 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
00113 {
00114 struct generic_prepare_struct *gps = data;
00115 SQLHSTMT stmt;
00116 int res;
00117
00118 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00119 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00120 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
00121 return NULL;
00122 }
00123
00124 res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
00125 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00126 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", (char *)gps->sql);
00127 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00128 return NULL;
00129 }
00130
00131 if (!ast_strlen_zero(gps->param))
00132 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->param), 0, (void *)gps->param, 0, NULL);
00133
00134 return stmt;
00135 }
00136
00137 static void retrieve_file(char *dir)
00138 {
00139 int x = 0;
00140 int res;
00141 int fd=-1;
00142 size_t fdlen = 0;
00143 void *fdm = MAP_FAILED;
00144 SQLHSTMT stmt;
00145 char sql[256];
00146 char fmt[80]="", empty[10] = "";
00147 char *c;
00148 SQLLEN colsize;
00149 char full_fn[256];
00150 struct odbc_obj *obj;
00151 struct generic_prepare_struct gps = { .sql = sql, .param = dir };
00152
00153 obj = ast_odbc_request_obj(odbc_database, 1);
00154 if (obj) {
00155 do {
00156 ast_copy_string(fmt, vmfmts, sizeof(fmt));
00157 c = strchr(fmt, '|');
00158 if (c)
00159 *c = '\0';
00160 if (!strcasecmp(fmt, "wav49"))
00161 strcpy(fmt, "WAV");
00162 snprintf(full_fn, sizeof(full_fn), "%s.%s", dir, fmt);
00163 snprintf(sql, sizeof(sql), "SELECT recording FROM %s WHERE dir=? AND msgnum=-1", odbc_table);
00164 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
00165
00166 if (!stmt) {
00167 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
00168 break;
00169 }
00170 res = SQLFetch(stmt);
00171 if (res == SQL_NO_DATA) {
00172 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00173 break;
00174 } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00175 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
00176 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00177 break;
00178 }
00179 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, AST_FILE_MODE);
00180 if (fd < 0) {
00181 ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
00182 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00183 break;
00184 }
00185
00186 res = SQLGetData(stmt, 1, SQL_BINARY, empty, 0, &colsize);
00187 fdlen = colsize;
00188 if (fd > -1) {
00189 char tmp[1]="";
00190 lseek(fd, fdlen - 1, SEEK_SET);
00191 if (write(fd, tmp, 1) != 1) {
00192 close(fd);
00193 fd = -1;
00194 break;
00195 }
00196 if (fd > -1)
00197 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
00198 }
00199 if (fdm != MAP_FAILED) {
00200 memset(fdm, 0, fdlen);
00201 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, fdlen, &colsize);
00202 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00203 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
00204 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00205 break;
00206 }
00207 }
00208 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00209 } while (0);
00210 ast_odbc_release_obj(obj);
00211 } else
00212 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
00213 if (fdm != MAP_FAILED)
00214 munmap(fdm, fdlen);
00215 if (fd > -1)
00216 close(fd);
00217 return;
00218 }
00219 #endif
00220
00221 static int compare(const char *text, const char *template)
00222 {
00223 char digit;
00224
00225 if (ast_strlen_zero(text)) {
00226 return -1;
00227 }
00228
00229 while (*template) {
00230 digit = toupper(*text++);
00231 switch (digit) {
00232 case 0:
00233 return -1;
00234 case '1':
00235 digit = '1';
00236 break;
00237 case '2':
00238 case 'A':
00239 case 'B':
00240 case 'C':
00241 digit = '2';
00242 break;
00243 case '3':
00244 case 'D':
00245 case 'E':
00246 case 'F':
00247 digit = '3';
00248 break;
00249 case '4':
00250 case 'G':
00251 case 'H':
00252 case 'I':
00253 digit = '4';
00254 break;
00255 case '5':
00256 case 'J':
00257 case 'K':
00258 case 'L':
00259 digit = '5';
00260 break;
00261 case '6':
00262 case 'M':
00263 case 'N':
00264 case 'O':
00265 digit = '6';
00266 break;
00267 case '7':
00268 case 'P':
00269 case 'Q':
00270 case 'R':
00271 case 'S':
00272 digit = '7';
00273 break;
00274 case '8':
00275 case 'T':
00276 case 'U':
00277 case 'V':
00278 digit = '8';
00279 break;
00280 case '9':
00281 case 'W':
00282 case 'X':
00283 case 'Y':
00284 case 'Z':
00285 digit = '9';
00286 break;
00287
00288 default:
00289 if (digit > ' ')
00290 return -1;
00291 continue;
00292 }
00293
00294 if (*template++ != digit)
00295 return -1;
00296 }
00297
00298 return 0;
00299 }
00300
00301
00302
00303
00304
00305
00306 static int play_mailbox_owner(struct ast_channel *chan, const char *context,
00307 const char *ext, const char *name, struct ast_flags *flags)
00308 {
00309 int res = 0;
00310 char fn[256];
00311
00312
00313 snprintf(fn, sizeof(fn), "%s/voicemail/%s/%s/greet",
00314 ast_config_AST_SPOOL_DIR, context, ext);
00315 #ifdef ODBC_STORAGE
00316 retrieve_file(fn);
00317 #endif
00318
00319 if (ast_fileexists(fn, NULL, chan->language) <= 0) {
00320
00321 snprintf(fn, sizeof(fn), "%s/vm/%s/greet",
00322 ast_config_AST_SPOOL_DIR, ext);
00323 }
00324 #ifdef ODBC_STORAGE
00325 retrieve_file(fn);
00326 #endif
00327
00328 if (ast_fileexists(fn, NULL, chan->language) > 0) {
00329 res = ast_stream_and_wait(chan, fn, AST_DIGIT_ANY);
00330 ast_stopstream(chan);
00331
00332 if (ast_test_flag(flags, OPT_SAYEXTENSION)) {
00333 ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
00334 res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, chan->language);
00335 }
00336 } else {
00337 res = ast_say_character_str(chan, S_OR(name, ext), AST_DIGIT_ANY, chan->language);
00338 if (!ast_strlen_zero(name) && ast_test_flag(flags, OPT_SAYEXTENSION)) {
00339 ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
00340 res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, chan->language);
00341 }
00342 }
00343 #ifdef ODBC_STORAGE
00344 ast_filedelete(fn, NULL);
00345 #endif
00346
00347 return res;
00348 }
00349
00350 static int select_entry(struct ast_channel *chan, const char *context, const char *dialcontext, const struct directory_item *item, struct ast_flags *flags)
00351 {
00352 ast_debug(1, "Selecting '%s' - %s@%s\n", item->name, item->exten, dialcontext);
00353
00354 if (ast_test_flag(flags, OPT_FROMVOICEMAIL)) {
00355
00356 ast_copy_string(chan->exten, item->exten, sizeof(chan->exten));
00357 } else if (ast_goto_if_exists(chan, dialcontext, item->exten, 1)) {
00358 ast_log(LOG_WARNING,
00359 "Can't find extension '%s' in context '%s'. "
00360 "Did you pass the wrong context to Directory?\n",
00361 item->exten, dialcontext);
00362 return -1;
00363 }
00364
00365 return 0;
00366 }
00367
00368 static int select_item_seq(struct ast_channel *chan, struct directory_item **items, int count, const char *context, const char *dialcontext, struct ast_flags *flags)
00369 {
00370 struct directory_item *item, **ptr;
00371 int i, res, loop;
00372
00373 for (ptr = items, i = 0; i < count; i++, ptr++) {
00374 item = *ptr;
00375
00376 for (loop = 3 ; loop > 0; loop--) {
00377 res = play_mailbox_owner(chan, context, item->exten, item->name, flags);
00378
00379 if (!res)
00380 res = ast_stream_and_wait(chan, "dir-instr", AST_DIGIT_ANY);
00381 if (!res)
00382 res = ast_waitfordigit(chan, 3000);
00383 ast_stopstream(chan);
00384
00385 if (res == '1') {
00386 return select_entry(chan, context, dialcontext, item, flags) ? -1 : 1;
00387 } else if (res == '*') {
00388
00389 break;
00390 }
00391
00392 if (res < 0)
00393 return -1;
00394
00395 res = 0;
00396 }
00397 }
00398
00399
00400 return 0;
00401 }
00402
00403 static int select_item_menu(struct ast_channel *chan, struct directory_item **items, int count, const char *context, const char *dialcontext, struct ast_flags *flags)
00404 {
00405 struct directory_item **block, *item;
00406 int i, limit, res = 0;
00407 char buf[9];
00408
00409 for (block = items; count; block += limit, count -= limit) {
00410 limit = count;
00411 if (limit > 8)
00412 limit = 8;
00413
00414 for (i = 0; i < limit && !res; i++) {
00415 item = block[i];
00416
00417 snprintf(buf, sizeof(buf), "digits/%d", i + 1);
00418
00419 res = ast_streamfile(chan, "dir-multi1", chan->language);
00420 if (!res)
00421 res = ast_waitstream(chan, AST_DIGIT_ANY);
00422 if (!res)
00423 res = ast_streamfile(chan, buf, chan->language);
00424 if (!res)
00425 res = ast_waitstream(chan, AST_DIGIT_ANY);
00426 if (!res)
00427 res = ast_streamfile(chan, "dir-multi2", chan->language);
00428 if (!res)
00429 res = ast_waitstream(chan, AST_DIGIT_ANY);
00430 if (!res)
00431 res = play_mailbox_owner(chan, context, item->exten, item->name, flags);
00432 if (!res)
00433 res = ast_waitstream(chan, AST_DIGIT_ANY);
00434 if (!res)
00435 res = ast_waitfordigit(chan, 800);
00436 }
00437
00438
00439 if (!res && count > limit) {
00440 res = ast_streamfile(chan, "dir-multi9", chan->language);
00441 if (!res)
00442 res = ast_waitstream(chan, AST_DIGIT_ANY);
00443 }
00444
00445 if (!res) {
00446 res = ast_waitfordigit(chan, 3000);
00447 }
00448
00449 if (res && res > '0' && res < '1' + limit) {
00450 return select_entry(chan, context, dialcontext, block[res - '1'], flags) ? -1 : 1;
00451 }
00452
00453 if (res < 0)
00454 return -1;
00455
00456 res = 0;
00457 }
00458
00459
00460 return 0;
00461 }
00462
00463 static struct ast_config *realtime_directory(char *context)
00464 {
00465 struct ast_config *cfg;
00466 struct ast_config *rtdata;
00467 struct ast_category *cat;
00468 struct ast_variable *var;
00469 char *mailbox;
00470 const char *fullname;
00471 const char *hidefromdir;
00472 char tmp[100];
00473 struct ast_flags config_flags = { 0 };
00474
00475
00476 cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
00477
00478 if (!cfg) {
00479
00480 ast_log(LOG_WARNING, "Loading config failed.\n");
00481 return NULL;
00482 }
00483
00484
00485
00486 rtdata = ast_load_realtime_multientry("voicemail", "mailbox LIKE", "%", "context", context, NULL);
00487
00488
00489 if (!rtdata)
00490 return cfg;
00491
00492
00493 cat = ast_category_get(cfg, context);
00494 if (!cat) {
00495 cat = ast_category_new(context, "", 99999);
00496 if (!cat) {
00497 ast_log(LOG_WARNING, "Out of memory\n");
00498 ast_config_destroy(cfg);
00499 if (rtdata) {
00500 ast_config_destroy(rtdata);
00501 }
00502 return NULL;
00503 }
00504 ast_category_append(cfg, cat);
00505 }
00506
00507 mailbox = NULL;
00508 while ( (mailbox = ast_category_browse(rtdata, mailbox)) ) {
00509 fullname = ast_variable_retrieve(rtdata, mailbox, "fullname");
00510 hidefromdir = ast_variable_retrieve(rtdata, mailbox, "hidefromdir");
00511 snprintf(tmp, sizeof(tmp), "no-password,%s,hidefromdir=%s",
00512 fullname ? fullname : "",
00513 hidefromdir ? hidefromdir : "no");
00514 var = ast_variable_new(mailbox, tmp, "");
00515 if (var)
00516 ast_variable_append(cat, var);
00517 else
00518 ast_log(LOG_WARNING, "Out of memory adding mailbox '%s'\n", mailbox);
00519 }
00520 ast_config_destroy(rtdata);
00521
00522 return cfg;
00523 }
00524
00525 static int check_match(struct directory_item **result, const char *item_fullname, const char *item_ext, const char *pattern_ext, int use_first_name)
00526 {
00527 struct directory_item *item;
00528 const char *key = NULL;
00529 int namelen;
00530
00531
00532
00533 if (!use_first_name)
00534 key = strchr(item_fullname, ' ');
00535
00536 if (key)
00537 key++;
00538 else
00539 key = item_fullname;
00540
00541 if (compare(key, pattern_ext))
00542 return 0;
00543
00544
00545 item = ast_calloc(1, sizeof(*item));
00546 if (!item)
00547 return -1;
00548 ast_copy_string(item->name, item_fullname, sizeof(item->name));
00549 ast_copy_string(item->exten, item_ext, sizeof(item->exten));
00550
00551 ast_copy_string(item->key, key, sizeof(item->key));
00552 if (key != item_fullname) {
00553
00554 namelen = key - item_fullname - 1;
00555 if (namelen > sizeof(item->key) - strlen(item->key) - 1)
00556 namelen = sizeof(item->key) - strlen(item->key) - 1;
00557 strncat(item->key, item_fullname, namelen);
00558 }
00559
00560 *result = item;
00561 return 1;
00562 }
00563
00564 typedef AST_LIST_HEAD_NOLOCK(, directory_item) itemlist;
00565
00566 static int search_directory(const char *context, struct ast_config *vmcfg, struct ast_config *ucfg, const char *ext, int use_first_name, itemlist *alist)
00567 {
00568 struct ast_variable *v;
00569 char buf[AST_MAX_EXTENSION + 1], *pos, *bufptr, *cat;
00570 struct directory_item *item;
00571 int res;
00572
00573 ast_debug(2, "Pattern: %s\n", ext);
00574
00575 for (v = ast_variable_browse(vmcfg, context); v; v = v->next) {
00576
00577
00578 if (strcasestr(v->value, "hidefromdir=yes"))
00579 continue;
00580
00581 ast_copy_string(buf, v->value, sizeof(buf));
00582 bufptr = buf;
00583
00584
00585 strsep(&bufptr, ",");
00586 pos = strsep(&bufptr, ",");
00587
00588 res = check_match(&item, S_OR(pos, ""), v->name, ext, use_first_name);
00589 if (!res)
00590 continue;
00591 else if (res < 0)
00592 return -1;
00593
00594 AST_LIST_INSERT_TAIL(alist, item, entry);
00595 }
00596
00597
00598 if (ucfg) {
00599 for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
00600 const char *pos;
00601 if (!strcasecmp(cat, "general"))
00602 continue;
00603 if (!ast_true(ast_config_option(ucfg, cat, "hasdirectory")))
00604 continue;
00605
00606
00607 pos = ast_variable_retrieve(ucfg, cat, "fullname");
00608 if (!pos)
00609 continue;
00610
00611 res = check_match(&item, pos, cat, ext, use_first_name);
00612 if (!res)
00613 continue;
00614 else if (res < 0)
00615 return -1;
00616
00617 AST_LIST_INSERT_TAIL(alist, item, entry);
00618 }
00619 }
00620
00621 return 0;
00622 }
00623
00624 static void sort_items(struct directory_item **sorted, int count)
00625 {
00626 int reordered, i;
00627 struct directory_item **ptr, *tmp;
00628
00629 if (count < 2)
00630 return;
00631
00632
00633 do {
00634 reordered = 0;
00635 for (ptr = sorted, i = 0; i < count - 1; i++, ptr++) {
00636 if (strcasecmp(ptr[0]->key, ptr[1]->key) > 0) {
00637 tmp = ptr[0];
00638 ptr[0] = ptr[1];
00639 ptr[1] = tmp;
00640 reordered++;
00641 }
00642 }
00643 } while (reordered);
00644 }
00645
00646 static int goto_exten(struct ast_channel *chan, const char *dialcontext, char *ext)
00647 {
00648 if (!ast_goto_if_exists(chan, dialcontext, ext, 1) ||
00649 (!ast_strlen_zero(chan->macrocontext) &&
00650 !ast_goto_if_exists(chan, chan->macrocontext, ext, 1))) {
00651 return 0;
00652 } else {
00653 ast_log(LOG_WARNING, "Can't find extension '%s' in current context. "
00654 "Not Exiting the Directory!\n", ext);
00655 return -1;
00656 }
00657 }
00658
00659 static int do_directory(struct ast_channel *chan, struct ast_config *vmcfg, struct ast_config *ucfg, char *context, char *dialcontext, char digit, struct ast_flags *flags)
00660 {
00661
00662 int res = 0;
00663 itemlist alist = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
00664 struct directory_item *item, **ptr, **sorted = NULL;
00665 int count, i;
00666 char ext[NUMDIGITS + 1] = "";
00667
00668 if (ast_strlen_zero(context)) {
00669 ast_log(LOG_WARNING,
00670 "Directory must be called with an argument "
00671 "(context in which to interpret extensions)\n");
00672 return -1;
00673 }
00674
00675 if (digit == '0' && !goto_exten(chan, dialcontext, "o")) {
00676 return 0;
00677 }
00678
00679 if (digit == '*' && !goto_exten(chan, dialcontext, "a")) {
00680 return 0;
00681 }
00682
00683 ext[0] = digit;
00684 if (ast_readstring(chan, ext + 1, NUMDIGITS - 1, 3000, 3000, "#") < 0)
00685 return -1;
00686
00687 res = search_directory(context, vmcfg, ucfg, ext, ast_test_flag(flags, OPT_LISTBYFIRSTNAME), &alist);
00688 if (res)
00689 goto exit;
00690
00691
00692 count = 0;
00693 AST_LIST_TRAVERSE(&alist, item, entry) {
00694 count++;
00695 }
00696
00697 if (count < 1) {
00698 res = ast_streamfile(chan, "dir-nomatch", chan->language);
00699 goto exit;
00700 }
00701
00702
00703
00704 sorted = ast_calloc(count, sizeof(*sorted));
00705
00706 ptr = sorted;
00707 AST_LIST_TRAVERSE(&alist, item, entry) {
00708 *ptr++ = item;
00709 }
00710
00711
00712 sort_items(sorted, count);
00713
00714 if (option_debug) {
00715 ast_debug(2, "Listing matching entries:\n");
00716 for (ptr = sorted, i = 0; i < count; i++, ptr++) {
00717 ast_log(LOG_DEBUG, "%s: %s\n", ptr[0]->exten, ptr[0]->name);
00718 }
00719 }
00720
00721 if (ast_test_flag(flags, OPT_SELECTFROMMENU)) {
00722
00723 res = select_item_menu(chan, sorted, count, context, dialcontext, flags);
00724 } else {
00725
00726 res = select_item_seq(chan, sorted, count, context, dialcontext, flags);
00727 }
00728
00729 if (!res) {
00730 res = ast_streamfile(chan, "dir-nomore", chan->language);
00731 }
00732
00733 exit:
00734 if (sorted)
00735 ast_free(sorted);
00736
00737 while ((item = AST_LIST_REMOVE_HEAD(&alist, entry)))
00738 ast_free(item);
00739
00740 return res;
00741 }
00742
00743 static int directory_exec(struct ast_channel *chan, void *data)
00744 {
00745 int res = 0;
00746 struct ast_config *cfg, *ucfg;
00747 const char *dirintro;
00748 char *parse, *opts[0];
00749 struct ast_flags flags = { 0 };
00750 struct ast_flags config_flags = { 0 };
00751 AST_DECLARE_APP_ARGS(args,
00752 AST_APP_ARG(vmcontext);
00753 AST_APP_ARG(dialcontext);
00754 AST_APP_ARG(options);
00755 );
00756
00757 if (ast_strlen_zero(data)) {
00758 ast_log(LOG_WARNING, "Directory requires an argument (context[,dialcontext])\n");
00759 return -1;
00760 }
00761
00762 parse = ast_strdupa(data);
00763
00764 AST_STANDARD_APP_ARGS(args, parse);
00765
00766 if (args.options && ast_app_parse_options(directory_app_options, &flags, opts, args.options))
00767 return -1;
00768
00769 if (ast_strlen_zero(args.dialcontext))
00770 args.dialcontext = args.vmcontext;
00771
00772 cfg = realtime_directory(args.vmcontext);
00773 if (!cfg) {
00774 ast_log(LOG_ERROR, "Unable to read the configuration data!\n");
00775 return -1;
00776 }
00777
00778 ucfg = ast_config_load("users.conf", config_flags);
00779
00780 dirintro = ast_variable_retrieve(cfg, args.vmcontext, "directoryintro");
00781 if (ast_strlen_zero(dirintro))
00782 dirintro = ast_variable_retrieve(cfg, "general", "directoryintro");
00783 if (ast_strlen_zero(dirintro))
00784 dirintro = ast_test_flag(&flags, OPT_LISTBYFIRSTNAME) ? "dir-intro-fn" : "dir-intro";
00785
00786 if (chan->_state != AST_STATE_UP)
00787 res = ast_answer(chan);
00788
00789 for (;;) {
00790 if (!res)
00791 res = ast_stream_and_wait(chan, dirintro, AST_DIGIT_ANY);
00792 ast_stopstream(chan);
00793 if (!res)
00794 res = ast_waitfordigit(chan, 5000);
00795
00796 if (res <= 0)
00797 break;
00798
00799 res = do_directory(chan, cfg, ucfg, args.vmcontext, args.dialcontext, res, &flags);
00800 if (res)
00801 break;
00802
00803 res = ast_waitstream(chan, AST_DIGIT_ANY);
00804 ast_stopstream(chan);
00805
00806 if (res)
00807 break;
00808 }
00809
00810 if (ucfg)
00811 ast_config_destroy(ucfg);
00812 ast_config_destroy(cfg);
00813
00814 return res < 0 ? -1 : 0;
00815 }
00816
00817 static int unload_module(void)
00818 {
00819 int res;
00820 res = ast_unregister_application(app);
00821 return res;
00822 }
00823
00824 static int load_module(void)
00825 {
00826 #ifdef ODBC_STORAGE
00827 struct ast_flags config_flags = { 0 };
00828 struct ast_config *cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
00829 const char *tmp;
00830
00831 if (cfg) {
00832 if ((tmp = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
00833 ast_copy_string(odbc_database, tmp, sizeof(odbc_database));
00834 }
00835 if ((tmp = ast_variable_retrieve(cfg, "general", "odbctable"))) {
00836 ast_copy_string(odbc_table, tmp, sizeof(odbc_table));
00837 }
00838 if ((tmp = ast_variable_retrieve(cfg, "general", "format"))) {
00839 ast_copy_string(vmfmts, tmp, sizeof(vmfmts));
00840 }
00841 ast_config_destroy(cfg);
00842 } else
00843 ast_log(LOG_WARNING, "Unable to load " VOICEMAIL_CONFIG " - ODBC defaults will be used\n");
00844 #endif
00845
00846 return ast_register_application(app, directory_exec, synopsis, descrip);
00847 }
00848
00849 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Directory");