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