Version 0.3.0 from FTP
[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\n. 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                         default:
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 digit)
125 {
126         /* Read in the first three digits..  "digit" is the first digit, already read */
127         char ext[NUMDIGITS + 1];
128         struct ast_variable *v;
129         int res;
130         int found=0;
131         char *start, *pos, *conv,*stringp=NULL;
132         char fn[256];
133         memset(ext, 0, sizeof(ext));
134         ext[0] = digit;
135         res = 0;
136         if (ast_readstring(chan, ext + 1, NUMDIGITS - 1, 3000, 3000, "#") < 0) res = -1;
137         if (!res) {
138                 /* Search for all names which start with those digits */
139                 v = ast_variable_browse(cfg, context);
140                 while(v && !res) {
141                         /* Find all candidate extensions */
142                         while(v) {
143                                 /* Find a candidate extension */
144                                 start = strdup(v->value);
145                                 if (start) {
146                                         stringp=start;
147                                         strsep(&stringp, ",");
148                                         pos = strsep(&stringp, ",");
149                                         if (pos) {
150                                                 /* Grab the last name */
151                                                 if (strrchr(pos, ' '))
152                                                         pos = strrchr(pos, ' ') + 1;
153                                                 conv = convert(pos);
154                                                 if (conv) {
155                                                         if (!strcmp(conv, ext)) {
156                                                                 /* Match! */
157                                                                 found++;
158                                                                 free(conv);
159                                                                 free(start);
160                                                                 break;
161                                                         }
162                                                         free(conv);
163                                                 }
164                                         }
165                                         free(start);
166                                 }
167                                 v = v->next;
168                         }
169                         if (v) {
170                                 /* We have a match -- play a greeting if they have it */
171                                 snprintf(fn, sizeof(fn), "%s/vm/%s/greet", (char *)ast_config_AST_SPOOL_DIR, v->name);
172                                 if (ast_fileexists(fn, NULL, chan->language) > 0) {
173                                         res = ast_streamfile(chan, fn, chan->language);
174                                         if (!res)
175                                                 res = ast_waitstream(chan, AST_DIGIT_ANY);
176                                         ast_stopstream(chan);
177                                 } else {
178                                         res = ast_say_digit_str(chan, v->name, AST_DIGIT_ANY, chan->language);
179                                 }
180 ahem:
181                                 if (!res)
182                                         res = ast_streamfile(chan, "dir-instr", chan->language);
183                                 if (!res)
184                                         res = ast_waitstream(chan, AST_DIGIT_ANY);
185                                 if (!res)
186                                         res = ast_waitfordigit(chan, 3000);
187                                 ast_stopstream(chan);
188                                 if (res > -1) {
189                                         if (res == '1') {
190                                                 strncpy(chan->exten, v->name, sizeof(chan->exten)-1);
191                                                 chan->priority = 0;
192                                                 strncpy(chan->context, context, sizeof(chan->context)-1);
193                                                 res = 0;
194                                                 break;
195                                         } else if (res == '*') {
196                                                 res = 0;
197                                                 v = v->next;
198                                         } else {
199                                                 res = 0;
200                                                 goto ahem;
201                                         }
202                                 }
203                         } else {
204                                 if (found) 
205                                         res = ast_streamfile(chan, "dir-nomore", chan->language);
206                                 else
207                                         res = ast_streamfile(chan, "dir-nomatch", chan->language);
208                                 if (!res)
209                                         res = 1;
210                                 return res;
211                         }
212                 }
213                 
214         }
215         return res;
216 }
217
218 static int directory_exec(struct ast_channel *chan, void *data)
219 {
220         int res = 0;
221         struct localuser *u;
222         struct ast_config *cfg;
223         if (!data) {
224                 ast_log(LOG_WARNING, "directory requires an argument (context)\n");
225                 return -1;
226         }
227         cfg = ast_load(DIRECTORY_CONFIG);
228         if (!cfg) {
229                 ast_log(LOG_WARNING, "Unable to open directory configuration %s\n", DIRECTORY_CONFIG);
230                 return -1;
231         }
232         LOCAL_USER_ADD(u);
233 top:
234         if (!res)
235                 res = ast_streamfile(chan, "dir-intro", chan->language);
236         if (!res)
237                 res = ast_waitstream(chan, AST_DIGIT_ANY);
238         ast_stopstream(chan);
239         if (!res)
240                 res = ast_waitfordigit(chan, 5000);
241         if (res > 0) {
242                 res = do_directory(chan, cfg, (char *)data, res);
243                 if (res > 0) {
244                         res = ast_waitstream(chan, AST_DIGIT_ANY);
245                         ast_stopstream(chan);
246                         if (res >= 0) {
247                                 goto top;
248                         }
249                 }
250         }
251         ast_destroy(cfg);
252         LOCAL_USER_REMOVE(u);
253         return res;
254 }
255
256 int unload_module(void)
257 {
258         STANDARD_HANGUP_LOCALUSERS;
259         return ast_unregister_application(app);
260 }
261
262 int load_module(void)
263 {
264         return ast_register_application(app, directory_exec, synopsis, descrip);
265 }
266
267 char *description(void)
268 {
269         return tdesc;
270 }
271
272 int usecount(void)
273 {
274         int res;
275         STANDARD_USECOUNT(res);
276         return res;
277 }
278
279 char *key()
280 {
281         return ASTERISK_GPL_KEY;
282 }