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