00c19be6448ed93cc88334938748490fe3a05ec5
[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         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                                 snprintf(fn, sizeof(fn), "%s/vm/%s/greet", (char *)ast_config_AST_SPOOL_DIR, v->name);
176                                 if (ast_fileexists(fn, NULL, chan->language) > 0) {
177                                         res = ast_streamfile(chan, fn, chan->language);
178                                         if (!res)
179                                                 res = ast_waitstream(chan, AST_DIGIT_ANY);
180                                         ast_stopstream(chan);
181                                 } else {
182                                         res = ast_say_digit_str(chan, v->name, AST_DIGIT_ANY, chan->language);
183                                 }
184 ahem:
185                                 if (!res)
186                                         res = ast_streamfile(chan, "dir-instr", chan->language);
187                                 if (!res)
188                                         res = ast_waitstream(chan, AST_DIGIT_ANY);
189                                 if (!res)
190                                         res = ast_waitfordigit(chan, 3000);
191                                 ast_stopstream(chan);
192                                 if (res > -1) {
193                                         if (res == '1') {
194                                                 strncpy(chan->exten, v->name, sizeof(chan->exten)-1);
195                                                 chan->priority = 0;
196                                                 strncpy(chan->context, context, sizeof(chan->context)-1);
197                                                 res = 0;
198                                                 break;
199                                         } else if (res == '*') {
200                                                 res = 0;
201                                                 v = v->next;
202                                         } else {
203                                                 res = 0;
204                                                 goto ahem;
205                                         }
206                                 }
207                         } else {
208                                 if (found) 
209                                         res = ast_streamfile(chan, "dir-nomore", chan->language);
210                                 else
211                                         res = ast_streamfile(chan, "dir-nomatch", chan->language);
212                                 if (!res)
213                                         res = 1;
214                                 return res;
215                         }
216                 }
217                 
218         }
219         return res;
220 }
221
222 static int directory_exec(struct ast_channel *chan, void *data)
223 {
224         int res = 0;
225         struct localuser *u;
226         struct ast_config *cfg;
227         if (!data) {
228                 ast_log(LOG_WARNING, "directory requires an argument (context)\n");
229                 return -1;
230         }
231         cfg = ast_load(DIRECTORY_CONFIG);
232         if (!cfg) {
233                 ast_log(LOG_WARNING, "Unable to open directory configuration %s\n", DIRECTORY_CONFIG);
234                 return -1;
235         }
236         LOCAL_USER_ADD(u);
237 top:
238         if (!res)
239                 res = ast_streamfile(chan, "dir-intro", chan->language);
240         if (!res)
241                 res = ast_waitstream(chan, AST_DIGIT_ANY);
242         ast_stopstream(chan);
243         if (!res)
244                 res = ast_waitfordigit(chan, 5000);
245         if (res > 0) {
246                 res = do_directory(chan, cfg, (char *)data, res);
247                 if (res > 0) {
248                         res = ast_waitstream(chan, AST_DIGIT_ANY);
249                         ast_stopstream(chan);
250                         if (res >= 0) {
251                                 goto top;
252                         }
253                 }
254         }
255         ast_destroy(cfg);
256         LOCAL_USER_REMOVE(u);
257         return res;
258 }
259
260 int unload_module(void)
261 {
262         STANDARD_HANGUP_LOCALUSERS;
263         return ast_unregister_application(app);
264 }
265
266 int load_module(void)
267 {
268         return ast_register_application(app, directory_exec, synopsis, descrip);
269 }
270
271 char *description(void)
272 {
273         return tdesc;
274 }
275
276 int usecount(void)
277 {
278         int res;
279         STANDARD_USECOUNT(res);
280         return res;
281 }
282
283 char *key()
284 {
285         return ASTERISK_GPL_KEY;
286 }