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