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