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: 36377 $")
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 #ifdef USE_ODBC_STORAGE
00046 #include <errno.h>
00047 #include <sys/mman.h>
00048 #include "asterisk/res_odbc.h"
00049
00050 static char odbc_database[80] = "asterisk";
00051 static char odbc_table[80] = "voicemessages";
00052 static char vmfmts[80] = "wav";
00053 #endif
00054
00055 static char *tdesc = "Extension Directory";
00056 static char *app = "Directory";
00057
00058 static char *synopsis = "Provide directory of voicemail extensions";
00059 static char *descrip =
00060 " Directory(vm-context[|dial-context[|options]]): This application will present\n"
00061 "the calling channel with a directory of extensions from which they can search\n"
00062 "by name. The list of names and corresponding extensions is retrieved from the\n"
00063 "voicemail configuration file, voicemail.conf.\n"
00064 " This applicaiton will immediate exit if one of the following DTMF digits are\n"
00065 "received and the extension to jump to exists:\n"
00066 " 0 - Jump to the 'o' extension, if it exists.\n"
00067 " * - Jump to the 'a' extension, if it exists.\n\n"
00068 " Parameters:\n"
00069 " vm-context - This is the context within voicemail.conf to use for the\n"
00070 " Directory.\n"
00071 " dial-context - This is the dialplan context to use when looking for an\n"
00072 " extension that the user has selected, or when jumping to the\n"
00073 " 'o' or 'a' extension.\n\n"
00074 " Options:\n"
00075 " f - Allow the caller to enter the first name of a user in the directory\n"
00076 " instead of using the last name.\n";
00077
00078
00079
00080
00081 #define VOICEMAIL_CONFIG "voicemail.conf"
00082
00083
00084 #define NUMDIGITS 3
00085
00086 STANDARD_LOCAL_USER;
00087
00088 LOCAL_USER_DECL;
00089
00090 #ifdef USE_ODBC_STORAGE
00091 static void retrieve_file(char *dir)
00092 {
00093 int x = 0;
00094 int res;
00095 int fd=-1;
00096 size_t fdlen = 0;
00097 void *fdm=NULL;
00098 SQLHSTMT stmt;
00099 char sql[256];
00100 char fmt[80]="";
00101 char *c;
00102 SQLLEN colsize;
00103 char full_fn[256];
00104
00105 odbc_obj *obj;
00106 obj = fetch_odbc_obj(odbc_database, 0);
00107 if (obj) {
00108 do {
00109 ast_copy_string(fmt, vmfmts, sizeof(fmt));
00110 c = strchr(fmt, '|');
00111 if (c)
00112 *c = '\0';
00113 if (!strcasecmp(fmt, "wav49"))
00114 strcpy(fmt, "WAV");
00115 snprintf(full_fn, sizeof(full_fn), "%s.%s", dir, fmt);
00116 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00117 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00118 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
00119 break;
00120 }
00121 snprintf(sql, sizeof(sql), "SELECT recording FROM %s WHERE dir=? AND msgnum=-1", odbc_table);
00122 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
00123 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00124 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
00125 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00126 break;
00127 }
00128 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
00129 res = odbc_smart_execute(obj, stmt);
00130 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00131 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
00132 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00133 break;
00134 }
00135 res = SQLFetch(stmt);
00136 if (res == SQL_NO_DATA) {
00137 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00138 break;
00139 } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00140 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
00141 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00142 break;
00143 }
00144 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, 0770);
00145 if (fd < 0) {
00146 ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
00147 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00148 break;
00149 }
00150
00151 res = SQLGetData(stmt, 1, SQL_BINARY, NULL, 0, &colsize);
00152 fdlen = colsize;
00153 if (fd > -1) {
00154 char tmp[1]="";
00155 lseek(fd, fdlen - 1, SEEK_SET);
00156 if (write(fd, tmp, 1) != 1) {
00157 close(fd);
00158 fd = -1;
00159 break;
00160 }
00161 if (fd > -1)
00162 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
00163 }
00164 if (fdm) {
00165 memset(fdm, 0, fdlen);
00166 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, fdlen, &colsize);
00167 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00168 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
00169 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00170 break;
00171 }
00172 }
00173 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00174 } while (0);
00175 } else
00176 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
00177 if (fdm)
00178 munmap(fdm, fdlen);
00179 if (fd > -1)
00180 close(fd);
00181 return;
00182 }
00183 #endif
00184
00185 static char *convert(char *lastname)
00186 {
00187 char *tmp;
00188 int lcount = 0;
00189 tmp = malloc(NUMDIGITS + 1);
00190 if (tmp) {
00191 while((*lastname > 32) && lcount < NUMDIGITS) {
00192 switch(toupper(*lastname)) {
00193 case '1':
00194 tmp[lcount++] = '1';
00195 break;
00196 case '2':
00197 case 'A':
00198 case 'B':
00199 case 'C':
00200 tmp[lcount++] = '2';
00201 break;
00202 case '3':
00203 case 'D':
00204 case 'E':
00205 case 'F':
00206 tmp[lcount++] = '3';
00207 break;
00208 case '4':
00209 case 'G':
00210 case 'H':
00211 case 'I':
00212 tmp[lcount++] = '4';
00213 break;
00214 case '5':
00215 case 'J':
00216 case 'K':
00217 case 'L':
00218 tmp[lcount++] = '5';
00219 break;
00220 case '6':
00221 case 'M':
00222 case 'N':
00223 case 'O':
00224 tmp[lcount++] = '6';
00225 break;
00226 case '7':
00227 case 'P':
00228 case 'Q':
00229 case 'R':
00230 case 'S':
00231 tmp[lcount++] = '7';
00232 break;
00233 case '8':
00234 case 'T':
00235 case 'U':
00236 case 'V':
00237 tmp[lcount++] = '8';
00238 break;
00239 case '9':
00240 case 'W':
00241 case 'X':
00242 case 'Y':
00243 case 'Z':
00244 tmp[lcount++] = '9';
00245 break;
00246 }
00247 lastname++;
00248 }
00249 tmp[lcount] = '\0';
00250 }
00251 return tmp;
00252 }
00253
00254
00255
00256
00257
00258
00259 static int play_mailbox_owner(struct ast_channel *chan, char *context, char *dialcontext, char *ext, char *name) {
00260 int res = 0;
00261 int loop = 3;
00262 char fn[256];
00263 char fn2[256];
00264
00265
00266 snprintf(fn, sizeof(fn), "%s/voicemail/%s/%s/greet",
00267 (char *)ast_config_AST_SPOOL_DIR, context, ext);
00268 #ifdef USE_ODBC_STORAGE
00269 retrieve_file(fn);
00270 #endif
00271
00272
00273 snprintf(fn2, sizeof(fn2), "%s/vm/%s/greet",
00274 (char *)ast_config_AST_SPOOL_DIR, ext);
00275 #ifdef USE_ODBC_STORAGE
00276 retrieve_file(fn2);
00277 #endif
00278
00279 if (ast_fileexists(fn, NULL, chan->language) > 0) {
00280 res = ast_streamfile(chan, fn, chan->language);
00281 if (!res) {
00282 res = ast_waitstream(chan, AST_DIGIT_ANY);
00283 }
00284 ast_stopstream(chan);
00285 } else if (ast_fileexists(fn2, NULL, chan->language) > 0) {
00286 res = ast_streamfile(chan, fn2, chan->language);
00287 if (!res) {
00288 res = ast_waitstream(chan, AST_DIGIT_ANY);
00289 }
00290 ast_stopstream(chan);
00291 } else {
00292 res = ast_say_character_str(chan, !ast_strlen_zero(name) ? name : ext,
00293 AST_DIGIT_ANY, chan->language);
00294 }
00295 #ifdef USE_ODBC_STORAGE
00296 ast_filedelete(fn, NULL);
00297 ast_filedelete(fn2, NULL);
00298 #endif
00299
00300 while (loop) {
00301 if (!res) {
00302 res = ast_streamfile(chan, "dir-instr", chan->language);
00303 }
00304 if (!res) {
00305 res = ast_waitstream(chan, AST_DIGIT_ANY);
00306 }
00307 if (!res) {
00308 res = ast_waitfordigit(chan, 3000);
00309 }
00310 ast_stopstream(chan);
00311
00312 if (res > -1) {
00313 switch (res) {
00314 case '1':
00315
00316 loop = 0;
00317 if (ast_goto_if_exists(chan, dialcontext, ext, 1)) {
00318 ast_log(LOG_WARNING,
00319 "Can't find extension '%s' in context '%s'. "
00320 "Did you pass the wrong context to Directory?\n",
00321 ext, dialcontext);
00322 res = -1;
00323 }
00324 break;
00325
00326 case '*':
00327
00328 loop = 0;
00329 break;
00330
00331 default:
00332
00333 res = 0;
00334 loop--;
00335 break;
00336 }
00337 }
00338 else {
00339
00340 loop = 0;
00341 }
00342 }
00343
00344 return(res);
00345 }
00346
00347 static struct ast_config *realtime_directory(char *context)
00348 {
00349 struct ast_config *cfg;
00350 struct ast_config *rtdata;
00351 struct ast_category *cat;
00352 struct ast_variable *var;
00353 char *mailbox;
00354 char *fullname;
00355 char *hidefromdir;
00356 char tmp[100];
00357
00358
00359 cfg = ast_config_load(VOICEMAIL_CONFIG);
00360
00361 if (!cfg) {
00362
00363 ast_log(LOG_WARNING, "Loading config failed.\n");
00364 return NULL;
00365 }
00366
00367
00368
00369 rtdata = ast_load_realtime_multientry("voicemail", "mailbox LIKE", "%", "context", context, NULL);
00370
00371
00372 if (!rtdata)
00373 return cfg;
00374
00375
00376 cat = ast_category_get(cfg, context);
00377 if (!cat) {
00378 cat = ast_category_new(context);
00379 if (!cat) {
00380 ast_log(LOG_WARNING, "Out of memory\n");
00381 ast_config_destroy(cfg);
00382 return NULL;
00383 }
00384 ast_category_append(cfg, cat);
00385 }
00386
00387 mailbox = ast_category_browse(rtdata, NULL);
00388 while (mailbox) {
00389 fullname = ast_variable_retrieve(rtdata, mailbox, "fullname");
00390 hidefromdir = ast_variable_retrieve(rtdata, mailbox, "hidefromdir");
00391 snprintf(tmp, sizeof(tmp), "no-password,%s,hidefromdir=%s",
00392 fullname ? fullname : "",
00393 hidefromdir ? hidefromdir : "no");
00394 var = ast_variable_new(mailbox, tmp);
00395 if (var)
00396 ast_variable_append(cat, var);
00397 else
00398 ast_log(LOG_WARNING, "Out of memory adding mailbox '%s'\n", mailbox);
00399 mailbox = ast_category_browse(rtdata, mailbox);
00400 }
00401 ast_config_destroy(rtdata);
00402
00403 return cfg;
00404 }
00405
00406 static int do_directory(struct ast_channel *chan, struct ast_config *cfg, char *context, char *dialcontext, char digit, int last)
00407 {
00408
00409 char ext[NUMDIGITS + 1];
00410 char name[80] = "";
00411 struct ast_variable *v;
00412 int res;
00413 int found=0;
00414 int lastuserchoice = 0;
00415 char *start, *pos, *conv,*stringp=NULL;
00416
00417 if (ast_strlen_zero(context)) {
00418 ast_log(LOG_WARNING,
00419 "Directory must be called with an argument "
00420 "(context in which to interpret extensions)\n");
00421 return -1;
00422 }
00423 if (digit == '0') {
00424 if (!ast_goto_if_exists(chan, chan->context, "o", 1) ||
00425 (!ast_strlen_zero(chan->macrocontext) &&
00426 !ast_goto_if_exists(chan, chan->macrocontext, "o", 1))) {
00427 return 0;
00428 } else {
00429 ast_log(LOG_WARNING, "Can't find extension 'o' in current context. "
00430 "Not Exiting the Directory!\n");
00431 res = 0;
00432 }
00433 }
00434 if (digit == '*') {
00435 if (!ast_goto_if_exists(chan, chan->context, "a", 1) ||
00436 (!ast_strlen_zero(chan->macrocontext) &&
00437 !ast_goto_if_exists(chan, chan->macrocontext, "a", 1))) {
00438 return 0;
00439 } else {
00440 ast_log(LOG_WARNING, "Can't find extension 'a' in current context. "
00441 "Not Exiting the Directory!\n");
00442 res = 0;
00443 }
00444 }
00445 memset(ext, 0, sizeof(ext));
00446 ext[0] = digit;
00447 res = 0;
00448 if (ast_readstring(chan, ext + 1, NUMDIGITS - 1, 3000, 3000, "#") < 0) res = -1;
00449 if (!res) {
00450
00451 v = ast_variable_browse(cfg, context);
00452 while(v && !res) {
00453
00454 while(v) {
00455
00456 start = strdup(v->value);
00457 if (start && !strcasestr(start, "hidefromdir=yes")) {
00458 stringp=start;
00459 strsep(&stringp, ",");
00460 pos = strsep(&stringp, ",");
00461 if (pos) {
00462 ast_copy_string(name, pos, sizeof(name));
00463
00464 if (last && strrchr(pos,' '))
00465 pos = strrchr(pos, ' ') + 1;
00466 conv = convert(pos);
00467 if (conv) {
00468 if (!strcmp(conv, ext)) {
00469
00470 found++;
00471 free(conv);
00472 free(start);
00473 break;
00474 }
00475 free(conv);
00476 }
00477 }
00478 free(start);
00479 }
00480 v = v->next;
00481 }
00482
00483 if (v) {
00484
00485 res = play_mailbox_owner(chan, context, dialcontext, v->name, name);
00486 switch (res) {
00487 case -1:
00488
00489
00490
00491 lastuserchoice = 0;
00492 break;
00493 case '1':
00494
00495
00496
00497
00498 lastuserchoice = res;
00499 break;
00500 case '*':
00501
00502 lastuserchoice = res;
00503 res = 0;
00504 break;
00505 default:
00506 break;
00507 }
00508 v = v->next;
00509 }
00510 }
00511
00512 if (lastuserchoice != '1') {
00513 if (found)
00514 res = ast_streamfile(chan, "dir-nomore", chan->language);
00515 else
00516 res = ast_streamfile(chan, "dir-nomatch", chan->language);
00517 if (!res)
00518 res = 1;
00519 return res;
00520 }
00521 return 0;
00522 }
00523 return res;
00524 }
00525
00526 static int directory_exec(struct ast_channel *chan, void *data)
00527 {
00528 int res = 0;
00529 struct localuser *u;
00530 struct ast_config *cfg;
00531 int last = 1;
00532 char *context, *dialcontext, *dirintro, *options;
00533
00534 if (ast_strlen_zero(data)) {
00535 ast_log(LOG_WARNING, "Directory requires an argument (context[,dialcontext])\n");
00536 return -1;
00537 }
00538
00539 LOCAL_USER_ADD(u);
00540
00541 context = ast_strdupa(data);
00542 dialcontext = strchr(context, '|');
00543 if (dialcontext) {
00544 *dialcontext = '\0';
00545 dialcontext++;
00546 options = strchr(dialcontext, '|');
00547 if (options) {
00548 *options = '\0';
00549 options++;
00550 if (strchr(options, 'f'))
00551 last = 0;
00552 }
00553 } else
00554 dialcontext = context;
00555
00556 cfg = realtime_directory(context);
00557 if (!cfg) {
00558 LOCAL_USER_REMOVE(u);
00559 return -1;
00560 }
00561
00562 dirintro = ast_variable_retrieve(cfg, context, "directoryintro");
00563 if (ast_strlen_zero(dirintro))
00564 dirintro = ast_variable_retrieve(cfg, "general", "directoryintro");
00565 if (ast_strlen_zero(dirintro)) {
00566 if (last)
00567 dirintro = "dir-intro";
00568 else
00569 dirintro = "dir-intro-fn";
00570 }
00571
00572 if (chan->_state != AST_STATE_UP)
00573 res = ast_answer(chan);
00574
00575 for (;;) {
00576 if (!res)
00577 res = ast_streamfile(chan, dirintro, chan->language);
00578 if (!res)
00579 res = ast_waitstream(chan, AST_DIGIT_ANY);
00580 ast_stopstream(chan);
00581 if (!res)
00582 res = ast_waitfordigit(chan, 5000);
00583 if (res > 0) {
00584 res = do_directory(chan, cfg, context, dialcontext, res, last);
00585 if (res > 0) {
00586 res = ast_waitstream(chan, AST_DIGIT_ANY);
00587 ast_stopstream(chan);
00588 if (res >= 0) {
00589 continue;
00590 }
00591 }
00592 }
00593 break;
00594 }
00595 ast_config_destroy(cfg);
00596 LOCAL_USER_REMOVE(u);
00597 return res;
00598 }
00599
00600 int unload_module(void)
00601 {
00602 int res;
00603
00604 res = ast_unregister_application(app);
00605
00606 STANDARD_HANGUP_LOCALUSERS;
00607
00608 return res;
00609 }
00610
00611 int load_module(void)
00612 {
00613 #ifdef USE_ODBC_STORAGE
00614 struct ast_config *cfg = ast_config_load(VOICEMAIL_CONFIG);
00615 char *tmp;
00616
00617 if (cfg) {
00618 if ((tmp = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
00619 ast_copy_string(odbc_database, tmp, sizeof(odbc_database));
00620 }
00621 if ((tmp = ast_variable_retrieve(cfg, "general", "odbctable"))) {
00622 ast_copy_string(odbc_table, tmp, sizeof(odbc_table));
00623 }
00624 if ((tmp = ast_variable_retrieve(cfg, "general", "format"))) {
00625 ast_copy_string(vmfmts, tmp, sizeof(vmfmts));
00626 }
00627 ast_config_destroy(cfg);
00628 } else
00629 ast_log(LOG_WARNING, "Unable to load " VOICEMAIL_CONFIG " - ODBC defaults will be used\n");
00630 #endif
00631
00632 return ast_register_application(app, directory_exec, synopsis, descrip);
00633 }
00634
00635 char *description(void)
00636 {
00637 return tdesc;
00638 }
00639
00640 int usecount(void)
00641 {
00642 int res;
00643 STANDARD_USECOUNT(res);
00644 return res;
00645 }
00646
00647 char *key()
00648 {
00649 return ASTERISK_GPL_KEY;
00650 }