Trivial portability fix
[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(vm-context[|dial-context]): Presents the user with a directory\n"
36 "of extensions from which they  may  select  by name. The  list  of  names \n"
37 "and  extensions  is discovered from  voicemail.conf. The  vm-context  argument\n"
38 "is required, and specifies  the  context  of voicemail.conf to use.  The\n"
39 "dial-context is the context to use for dialing the users, and defaults to\n"
40 "the vm-context if unspecified. Returns 0 unless the user hangs up. It  also\n"
41 "sets up the channel on exit to enter the extension the user selected.\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 *dialcontext, 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, dialcontext, v->name, 1, chan->callerid)) {
204                                                         strncpy(chan->exten, v->name, sizeof(chan->exten)-1);
205                                                         chan->priority = 0;
206                                                         strncpy(chan->context, dialcontext, 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         char *context, *dialcontext;
242         if (!data) {
243                 ast_log(LOG_WARNING, "directory requires an argument (context)\n");
244                 return -1;
245         }
246         cfg = ast_load(DIRECTORY_CONFIG);
247         if (!cfg) {
248                 ast_log(LOG_WARNING, "Unable to open directory configuration %s\n", DIRECTORY_CONFIG);
249                 return -1;
250         }
251         LOCAL_USER_ADD(u);
252 top:
253         context = ast_strdupa(data);
254         dialcontext = strchr(context, '|');
255         if (dialcontext) {
256                 *dialcontext = '\0';
257                 dialcontext++;
258         } else
259                 dialcontext = context;
260         if (chan->_state != AST_STATE_UP) 
261                 res = ast_answer(chan);
262         if (!res)
263                 res = ast_streamfile(chan, "dir-intro", chan->language);
264         if (!res)
265                 res = ast_waitstream(chan, AST_DIGIT_ANY);
266         ast_stopstream(chan);
267         if (!res)
268                 res = ast_waitfordigit(chan, 5000);
269         if (res > 0) {
270                 res = do_directory(chan, cfg, context, dialcontext, res);
271                 if (res > 0) {
272                         res = ast_waitstream(chan, AST_DIGIT_ANY);
273                         ast_stopstream(chan);
274                         if (res >= 0) {
275                                 goto top;
276                         }
277                 }
278         }
279         ast_destroy(cfg);
280         LOCAL_USER_REMOVE(u);
281         return res;
282 }
283
284 int unload_module(void)
285 {
286         STANDARD_HANGUP_LOCALUSERS;
287         return ast_unregister_application(app);
288 }
289
290 int load_module(void)
291 {
292         return ast_register_application(app, directory_exec, synopsis, descrip);
293 }
294
295 char *description(void)
296 {
297         return tdesc;
298 }
299
300 int usecount(void)
301 {
302         int res;
303         STANDARD_USECOUNT(res);
304         return res;
305 }
306
307 char *key()
308 {
309         return ASTERISK_GPL_KEY;
310 }