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