app_directory.c 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. /*
  2. * Asterisk -- A telephony toolkit for Linux.
  3. *
  4. * Provide a directory of extensions
  5. *
  6. * Copyright (C) 1999, Mark Spencer
  7. *
  8. * Mark Spencer <markster@linux-support.net>
  9. *
  10. * This program is free software, distributed under the terms of
  11. * the GNU General Public License
  12. */
  13. #include <asterisk/lock.h>
  14. #include <asterisk/file.h>
  15. #include <asterisk/logger.h>
  16. #include <asterisk/channel.h>
  17. #include <asterisk/pbx.h>
  18. #include <asterisk/module.h>
  19. #include <asterisk/config.h>
  20. #include <asterisk/say.h>
  21. #include <asterisk/utils.h>
  22. #include <string.h>
  23. #include <ctype.h>
  24. #include <stdlib.h>
  25. #include <stdio.h>
  26. #include "../asterisk.h"
  27. #include "../astconf.h"
  28. static char *tdesc = "Extension Directory";
  29. static char *app = "Directory";
  30. static char *synopsis = "Provide directory of voicemail extensions";
  31. static char *descrip =
  32. " Directory(vm-context[|dial-context[|options]]): Presents the user with a directory\n"
  33. "of extensions from which they may select by name. The list of names \n"
  34. "and extensions is discovered from voicemail.conf. The vm-context argument\n"
  35. "is required, and specifies the context of voicemail.conf to use. The\n"
  36. "dial-context is the context to use for dialing the users, and defaults to\n"
  37. "the vm-context if unspecified. The 'f' option causes the directory to match\n"
  38. "based on the first name in voicemail.conf instead of the last name.\n"
  39. "Returns 0 unless the user hangs up. It also sets up the channel on exit\n"
  40. "to enter the extension the user selected.\n";
  41. /* For simplicity, I'm keeping the format compatible with the voicemail config,
  42. but i'm open to suggestions for isolating it */
  43. #define DIRECTORY_CONFIG "voicemail.conf"
  44. /* How many digits to read in */
  45. #define NUMDIGITS 3
  46. STANDARD_LOCAL_USER;
  47. LOCAL_USER_DECL;
  48. static char *convert(char *lastname)
  49. {
  50. char *tmp;
  51. int lcount = 0;
  52. tmp = malloc(NUMDIGITS + 1);
  53. if (tmp) {
  54. while((*lastname > 32) && lcount < NUMDIGITS) {
  55. switch(toupper(*lastname)) {
  56. case '1':
  57. tmp[lcount++] = '1';
  58. break;
  59. case '2':
  60. case 'A':
  61. case 'B':
  62. case 'C':
  63. tmp[lcount++] = '2';
  64. break;
  65. case '3':
  66. case 'D':
  67. case 'E':
  68. case 'F':
  69. tmp[lcount++] = '3';
  70. break;
  71. case '4':
  72. case 'G':
  73. case 'H':
  74. case 'I':
  75. tmp[lcount++] = '4';
  76. break;
  77. case '5':
  78. case 'J':
  79. case 'K':
  80. case 'L':
  81. tmp[lcount++] = '5';
  82. break;
  83. case '6':
  84. case 'M':
  85. case 'N':
  86. case 'O':
  87. tmp[lcount++] = '6';
  88. break;
  89. case '7':
  90. case 'P':
  91. case 'Q':
  92. case 'R':
  93. case 'S':
  94. tmp[lcount++] = '7';
  95. break;
  96. case '8':
  97. case 'T':
  98. case 'U':
  99. case 'V':
  100. tmp[lcount++] = '8';
  101. break;
  102. case '9':
  103. case 'W':
  104. case 'X':
  105. case 'Y':
  106. case 'Z':
  107. tmp[lcount++] = '9';
  108. break;
  109. }
  110. lastname++;
  111. }
  112. tmp[lcount] = '\0';
  113. }
  114. return tmp;
  115. }
  116. /* play name of mailbox owner.
  117. * returns: -1 for bad or missing extension
  118. * '1' for selected entry from directory
  119. * '*' for skipped entry from directory
  120. */
  121. static int play_mailbox_owner(struct ast_channel *chan, char *context, char *dialcontext, char *ext, char *name) {
  122. int res = 0;
  123. int loop = 3;
  124. char fn[256];
  125. char fn2[256];
  126. /* Check for the VoiceMail2 greeting first */
  127. snprintf(fn, sizeof(fn), "%s/voicemail/%s/%s/greet",
  128. (char *)ast_config_AST_SPOOL_DIR, context, ext);
  129. /* Otherwise, check for an old-style Voicemail greeting */
  130. snprintf(fn2, sizeof(fn2), "%s/vm/%s/greet",
  131. (char *)ast_config_AST_SPOOL_DIR, ext);
  132. if (ast_fileexists(fn, NULL, chan->language) > 0) {
  133. res = ast_streamfile(chan, fn, chan->language);
  134. if (!res) {
  135. res = ast_waitstream(chan, AST_DIGIT_ANY);
  136. }
  137. ast_stopstream(chan);
  138. } else if (ast_fileexists(fn2, NULL, chan->language) > 0) {
  139. res = ast_streamfile(chan, fn2, chan->language);
  140. if (!res) {
  141. res = ast_waitstream(chan, AST_DIGIT_ANY);
  142. }
  143. ast_stopstream(chan);
  144. } else {
  145. res = ast_say_character_str(chan, !ast_strlen_zero(name) ? name : ext,
  146. AST_DIGIT_ANY, chan->language);
  147. }
  148. while (loop) {
  149. if (!res) {
  150. res = ast_streamfile(chan, "dir-instr", chan->language);
  151. }
  152. if (!res) {
  153. res = ast_waitstream(chan, AST_DIGIT_ANY);
  154. }
  155. if (!res) {
  156. res = ast_waitfordigit(chan, 3000);
  157. }
  158. ast_stopstream(chan);
  159. if (res > -1) {
  160. switch (res) {
  161. case '1':
  162. /* Name selected */
  163. loop = 0;
  164. if (ast_exists_extension(chan,dialcontext,ext,1,chan->callerid)) {
  165. strncpy(chan->exten, ext, sizeof(chan->exten)-1);
  166. chan->priority = 0;
  167. strncpy(chan->context, dialcontext, sizeof(chan->context)-1);
  168. } else {
  169. ast_log(LOG_WARNING,
  170. "Can't find extension '%s' in context '%s'. "
  171. "Did you pass the wrong context to Directory?\n",
  172. ext, dialcontext);
  173. res = -1;
  174. }
  175. break;
  176. case '*':
  177. /* Skip to next match in list */
  178. loop = 0;
  179. break;
  180. default:
  181. /* Not '1', or '*', so decrement number of tries */
  182. res = 0;
  183. loop--;
  184. break;
  185. } /* end switch */
  186. } /* end if */
  187. else {
  188. /* User hungup, so jump out now */
  189. loop = 0;
  190. }
  191. } /* end while */
  192. return(res);
  193. }
  194. static int do_directory(struct ast_channel *chan, struct ast_config *cfg, char *context, char *dialcontext, char digit, int last)
  195. {
  196. /* Read in the first three digits.. "digit" is the first digit, already read */
  197. char ext[NUMDIGITS + 1];
  198. char name[80] = "";
  199. struct ast_variable *v;
  200. int res;
  201. int found=0;
  202. int lastuserchoice = 0;
  203. char *start, *pos, *conv,*stringp=NULL;
  204. if (!context || ast_strlen_zero(context)) {
  205. ast_log(LOG_WARNING,
  206. "Directory must be called with an argument "
  207. "(context in which to interpret extensions)\n");
  208. return -1;
  209. }
  210. memset(ext, 0, sizeof(ext));
  211. ext[0] = digit;
  212. res = 0;
  213. if (ast_readstring(chan, ext + 1, NUMDIGITS - 1, 3000, 3000, "#") < 0) res = -1;
  214. if (!res) {
  215. /* Search for all names which start with those digits */
  216. v = ast_variable_browse(cfg, context);
  217. while(v && !res) {
  218. /* Find all candidate extensions */
  219. while(v) {
  220. /* Find a candidate extension */
  221. start = strdup(v->value);
  222. if (start) {
  223. stringp=start;
  224. strsep(&stringp, ",");
  225. pos = strsep(&stringp, ",");
  226. if (pos) {
  227. strncpy(name, pos, sizeof(name) - 1);
  228. /* Grab the last name */
  229. if (last && strrchr(pos,' '))
  230. pos = strrchr(pos, ' ') + 1;
  231. conv = convert(pos);
  232. if (conv) {
  233. if (!strcmp(conv, ext)) {
  234. /* Match! */
  235. found++;
  236. free(conv);
  237. free(start);
  238. break;
  239. }
  240. free(conv);
  241. }
  242. }
  243. free(start);
  244. }
  245. v = v->next;
  246. }
  247. if (v) {
  248. /* We have a match -- play a greeting if they have it */
  249. res = play_mailbox_owner(chan, context, dialcontext, v->name, name);
  250. switch (res) {
  251. case -1:
  252. /* user pressed '1' but extension does not exist, or
  253. * user hungup
  254. */
  255. lastuserchoice = 0;
  256. break;
  257. case '1':
  258. /* user pressed '1' and extensions exists */
  259. lastuserchoice = res;
  260. strncpy(chan->context, dialcontext, sizeof(chan->context) - 1);
  261. strncpy(chan->exten, v->name, sizeof(chan->exten) - 1);
  262. chan->priority = 0;
  263. break;
  264. case '*':
  265. /* user pressed '*' to skip something found */
  266. lastuserchoice = res;
  267. res = 0;
  268. break;
  269. default:
  270. break;
  271. }
  272. v = v->next;
  273. }
  274. }
  275. if (lastuserchoice != '1') {
  276. if (found)
  277. res = ast_streamfile(chan, "dir-nomore", chan->language);
  278. else
  279. res = ast_streamfile(chan, "dir-nomatch", chan->language);
  280. if (!res)
  281. res = 1;
  282. return res;
  283. }
  284. return 0;
  285. }
  286. return res;
  287. }
  288. static int directory_exec(struct ast_channel *chan, void *data)
  289. {
  290. int res = 0;
  291. struct localuser *u;
  292. struct ast_config *cfg;
  293. int last = 1;
  294. char *context, *dialcontext, *dirintro, *options;
  295. if (!data) {
  296. ast_log(LOG_WARNING, "directory requires an argument (context[,dialcontext])\n");
  297. return -1;
  298. }
  299. cfg = ast_load(DIRECTORY_CONFIG);
  300. if (!cfg) {
  301. ast_log(LOG_WARNING, "Unable to open directory configuration %s\n", DIRECTORY_CONFIG);
  302. return -1;
  303. }
  304. LOCAL_USER_ADD(u);
  305. top:
  306. context = ast_strdupa(data);
  307. dialcontext = strchr(context, '|');
  308. if (dialcontext) {
  309. *dialcontext = '\0';
  310. dialcontext++;
  311. options = strchr(dialcontext, '|');
  312. if (options) {
  313. *options = '\0';
  314. options++;
  315. if (strchr(options, 'f'))
  316. last = 0;
  317. }
  318. } else
  319. dialcontext = context;
  320. dirintro = ast_variable_retrieve(cfg, context, "directoryintro");
  321. if (!dirintro || ast_strlen_zero(dirintro))
  322. dirintro = ast_variable_retrieve(cfg, "general", "directoryintro");
  323. if (!dirintro || ast_strlen_zero(dirintro)) {
  324. if (last)
  325. dirintro = "dir-intro";
  326. else
  327. dirintro = "dir-intro-fn";
  328. }
  329. if (chan->_state != AST_STATE_UP)
  330. res = ast_answer(chan);
  331. if (!res)
  332. res = ast_streamfile(chan, dirintro, chan->language);
  333. if (!res)
  334. res = ast_waitstream(chan, AST_DIGIT_ANY);
  335. ast_stopstream(chan);
  336. if (!res)
  337. res = ast_waitfordigit(chan, 5000);
  338. if (res > 0) {
  339. res = do_directory(chan, cfg, context, dialcontext, res, last);
  340. if (res > 0) {
  341. res = ast_waitstream(chan, AST_DIGIT_ANY);
  342. ast_stopstream(chan);
  343. if (res >= 0) {
  344. goto top;
  345. }
  346. }
  347. }
  348. ast_destroy(cfg);
  349. LOCAL_USER_REMOVE(u);
  350. return res;
  351. }
  352. int unload_module(void)
  353. {
  354. STANDARD_HANGUP_LOCALUSERS;
  355. return ast_unregister_application(app);
  356. }
  357. int load_module(void)
  358. {
  359. return ast_register_application(app, directory_exec, synopsis, descrip);
  360. }
  361. char *description(void)
  362. {
  363. return tdesc;
  364. }
  365. int usecount(void)
  366. {
  367. int res;
  368. STANDARD_USECOUNT(res);
  369. return res;
  370. }
  371. char *key()
  372. {
  373. return ASTERISK_GPL_KEY;
  374. }