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