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