Remove pthread.h from source. We should be using asterisk/lock.h everywhere instead...
[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 <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 /* play name of mailbox owner.
125  * returns:  -1 for bad or missing extension
126  *           '1' for selected entry from directory
127  *           '*' for skipped entry from directory
128  */
129 static int play_mailbox_owner(struct ast_channel *chan, char *context, char *dialcontext, char *ext, char *name) {
130         int res = 0;
131         int loop = -1;
132         char fn[256];
133         char fn2[256];
134
135         /* Check for the VoiceMail2 greeting first */
136         snprintf(fn, sizeof(fn), "%s/voicemail/%s/%s/greet",
137                 (char *)ast_config_AST_SPOOL_DIR, context, ext);
138
139         /* Otherwise, check for an old-style Voicemail greeting */
140         snprintf(fn2, sizeof(fn2), "%s/vm/%s/greet",
141                 (char *)ast_config_AST_SPOOL_DIR, ext);
142
143         if (ast_fileexists(fn, NULL, chan->language) > 0) {
144                 res = ast_streamfile(chan, fn, chan->language);
145                 if (!res) {
146                         res = ast_waitstream(chan, AST_DIGIT_ANY);
147                 }
148                 ast_stopstream(chan);
149         } else if (ast_fileexists(fn2, NULL, chan->language) > 0) {
150                 res = ast_streamfile(chan, fn2, chan->language);
151                 if (!res) {
152                         res = ast_waitstream(chan, AST_DIGIT_ANY);
153                 }
154                 ast_stopstream(chan);
155         } else {
156                 res = ast_say_character_str(chan, !ast_strlen_zero(name) ? name : ext,
157                                         AST_DIGIT_ANY, chan->language);
158         }
159
160         while (loop) {
161                 if (!res) {
162                         res = ast_streamfile(chan, "dir-instr", chan->language);
163                 }
164                 if (!res) {
165                         res = ast_waitstream(chan, AST_DIGIT_ANY);
166                 }
167                 if (!res) {
168                         res = ast_waitfordigit(chan, 3000);
169                 }
170                 ast_stopstream(chan);
171         
172                 if (res > -1) {
173                         switch (res) {
174                                 case '1':
175                                         loop = 0;
176                                         if (ast_exists_extension(chan,dialcontext,ext,1,chan->callerid)) {
177                                                 strncpy(chan->exten, ext, sizeof(chan->exten)-1);
178                                                 chan->priority = 0;
179                                                 strncpy(chan->context, dialcontext, sizeof(chan->context)-1);
180                                         } else {
181                                                 ast_log(LOG_WARNING,
182                                                         "Can't find extension '%s' in context '%s'.  "
183                                                         "Did you pass the wrong context to Directory?\n",
184                                                         ext, context);
185                                                 res = -1;
186                                         }
187                                         break;
188         
189                                 case '*':   
190                                         loop = 0;
191                                         break;
192         
193                                 default:
194                                         res = 0;
195                                         break;
196                         } /* end switch */
197                 } /* end if */
198         } /* end while */
199
200         return(res);
201 }
202
203 static int do_directory(struct ast_channel *chan, struct ast_config *cfg, char *context, char *dialcontext, char digit)
204 {
205         /* Read in the first three digits..  "digit" is the first digit, already read */
206         char ext[NUMDIGITS + 1];
207         char name[80] = "";
208         struct ast_variable *v;
209         int res;
210         int found=0;
211         int lastuserchoice = 0;
212         char *start, *pos, *conv,*stringp=NULL;
213
214         if (!context || ast_strlen_zero(context)) {
215                 ast_log(LOG_WARNING,
216                         "Directory must be called with an argument "
217                         "(context in which to interpret extensions)\n");
218                 return -1;
219         }
220
221         memset(ext, 0, sizeof(ext));
222         ext[0] = digit;
223         res = 0;
224         if (ast_readstring(chan, ext + 1, NUMDIGITS - 1, 3000, 3000, "#") < 0) res = -1;
225         if (!res) {
226                 /* Search for all names which start with those digits */
227                 v = ast_variable_browse(cfg, context);
228                 while(v && !res) {
229                         /* Find all candidate extensions */
230                         while(v) {
231                                 /* Find a candidate extension */
232                                 start = strdup(v->value);
233                                 if (start) {
234                                         stringp=start;
235                                         strsep(&stringp, ",");
236                                         pos = strsep(&stringp, ",");
237                                         if (pos) {
238                                                 strncpy(name, pos, sizeof(name) - 1);
239                                                 /* Grab the last name */
240                                                 if (strrchr(pos, ' '))
241                                                         pos = strrchr(pos, ' ') + 1;
242                                                 conv = convert(pos);
243                                                 if (conv) {
244                                                         if (!strcmp(conv, ext)) {
245                                                                 /* Match! */
246                                                                 found++;
247                                                                 free(conv);
248                                                                 free(start);
249                                                                 break;
250                                                         }
251                                                         free(conv);
252                                                 }
253                                         }
254                                         free(start);
255                                 }
256                                 v = v->next;
257                         }
258
259                         if (v) {
260                                 /* We have a match -- play a greeting if they have it */
261                                 res = play_mailbox_owner(chan, context, dialcontext, v->name, name);
262                                 switch (res) {
263                                         case -1:
264                                                 /* user pressed '1' but extension does not exist */
265                                                 lastuserchoice = 0;
266                                                 break;
267                                         case '1':
268                                                 /* user pressed '1' and extensions exists */
269                                                 lastuserchoice = res;
270                                                 break;
271                                         case '*':
272                                                 /* user pressed '*' to skip something found */
273                                                 lastuserchoice = res;
274                                                 res = 0;
275                                                 break;
276                                         default:
277                                                 break;
278                                 }
279                                 v = v->next;
280                         }
281                 }
282
283                 if (lastuserchoice != '1') {
284                         if (found) 
285                                 res = ast_streamfile(chan, "dir-nomore", chan->language);
286                         else
287                                 res = ast_streamfile(chan, "dir-nomatch", chan->language);
288                         if (!res)
289                                 res = 1;
290                         return res;
291                 }
292                 
293         }
294         return res;
295 }
296
297 static int directory_exec(struct ast_channel *chan, void *data)
298 {
299         int res = 0;
300         struct localuser *u;
301         struct ast_config *cfg;
302         char *context, *dialcontext, *dirintro;
303         if (!data) {
304                 ast_log(LOG_WARNING, "directory requires an argument (context)\n");
305                 return -1;
306         }
307         cfg = ast_load(DIRECTORY_CONFIG);
308         if (!cfg) {
309                 ast_log(LOG_WARNING, "Unable to open directory configuration %s\n", DIRECTORY_CONFIG);
310                 return -1;
311         }
312         LOCAL_USER_ADD(u);
313 top:
314         context = ast_strdupa(data);
315         dialcontext = strchr(context, '|');
316         if (dialcontext) {
317                 *dialcontext = '\0';
318                 dialcontext++;
319         } else
320                 dialcontext = context;
321         dirintro = ast_variable_retrieve(cfg, context, "directoryintro");
322         if (!dirintro || ast_strlen_zero(dirintro))
323                 dirintro = ast_variable_retrieve(cfg, "general", "directoryintro");
324         if (!dirintro || ast_strlen_zero(dirintro))
325                 dirintro = "dir-intro";
326         if (chan->_state != AST_STATE_UP) 
327                 res = ast_answer(chan);
328         if (!res)
329                 res = ast_streamfile(chan, dirintro, chan->language);
330         if (!res)
331                 res = ast_waitstream(chan, AST_DIGIT_ANY);
332         ast_stopstream(chan);
333         if (!res)
334                 res = ast_waitfordigit(chan, 5000);
335         if (res > 0) {
336                 res = do_directory(chan, cfg, context, dialcontext, res);
337                 if (res > 0) {
338                         res = ast_waitstream(chan, AST_DIGIT_ANY);
339                         ast_stopstream(chan);
340                         if (res >= 0) {
341                                 goto top;
342                         }
343                 }
344         }
345         ast_destroy(cfg);
346         LOCAL_USER_REMOVE(u);
347         return res;
348 }
349
350 int unload_module(void)
351 {
352         STANDARD_HANGUP_LOCALUSERS;
353         return ast_unregister_application(app);
354 }
355
356 int load_module(void)
357 {
358         return ast_register_application(app, directory_exec, synopsis, descrip);
359 }
360
361 char *description(void)
362 {
363         return tdesc;
364 }
365
366 int usecount(void)
367 {
368         int res;
369         STANDARD_USECOUNT(res);
370         return res;
371 }
372
373 char *key()
374 {
375         return ASTERISK_GPL_KEY;
376 }