Use find_user for existsmailbox
[asterisk/asterisk.git] / apps / app_disa.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * DISA -- Direct Inward System Access Application  6/20/2001
5  * 
6  * Copyright (C) 2001, Linux Support Services, Inc.
7  *
8  * Jim Dixon <jim@lambdatel.com>
9  *
10  * Made only slightly more sane by Mark Spencer <markster@digium.com>
11  *
12  * This program is free software, distributed under the terms of
13  * the GNU General Public License
14  */
15  
16 #include <asterisk/lock.h>
17 #include <asterisk/file.h>
18 #include <asterisk/logger.h>
19 #include <asterisk/channel.h>
20 #include <asterisk/pbx.h>
21 #include <asterisk/module.h>
22 #include <asterisk/translate.h>
23 #include <asterisk/ulaw.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <math.h>
28 #include <pthread.h>
29 #include <sys/time.h>
30
31 /*
32 #define TONE_BLOCK_SIZE 320
33 */
34
35 static char *tdesc = "DISA (Direct Inward System Access) Application";
36
37 static char *app = "DISA";
38
39 static char *synopsis = "DISA (Direct Inward System Access)";
40
41 static char *descrip = 
42         "DISA (Direct Inward System Access) -- Allows someone from outside\n"
43         "the telephone switch (PBX) to obtain an \"internal\" system dialtone\n"
44         "and to place calls from it as if they were placing a call from within\n"
45         "the switch. A user calls a number that connects to the DISA application\n"
46         "and is given dialtone. The user enters their passcode, followed by the\n"
47         "pound sign (#). If the passcode is correct, the user is then given\n"
48         "system dialtone on which a call may be placed. Obviously, this type\n"
49         "of access has SERIOUS security implications, and GREAT care must be\n"
50         "taken NOT to compromise your security.\n\n"
51         "There is a possibility of accessing DISA without password. Simply\n"
52         "exchange your password with no-password.\n\n"
53         "  Example: exten => s,1,DISA,no-password|local\n\n"
54         "but be aware of using this for your security compromising.\n\n"
55         "The arguments to this application (in extensions.conf) allow either\n"
56         "specification of a single global password (that everyone uses), or\n"
57         "individual passwords contained in a file. It also allow specification\n"
58         "of the context on which the user will be dialing. If no context is\n"
59         "specified, the DISA application defaults the context to \"disa\"\n"
60         "presumably that a normal system will have a special context set up\n"
61         "for DISA use with some or a lot of restrictions. The arguments are\n"
62         "one of the following:\n\n"
63         "    numeric-passcode\n"
64         "    numeric-passcode|context\n"
65         "    full-pathname-of-file-that-contains-passcodes\n\n"
66         "The file that contains the passcodes (if used) allows specification\n"
67         "of either just a passcode (defaulting to the \"disa\" context, or\n"
68         "passcode|context on each line of the file. The file may contain blank\n"
69         "lines, or comments starting with \"#\" or \";\". In addition, the\n"
70         "above arguments may have |new-callerid-string appended to them, to\n"
71         "specify a new (different) callerid to be used for this call, for\n"
72         "example: numeric-passcode|context|\"My Phone\" <(234) 123-4567> or \n"
73         "full-pathname-of-passcode-file|\"My Phone\" <(234) 123-4567>. Note that\n"
74         "in the case of specifying the numeric-passcode, the context must be\n"
75         "specified if the callerid is specified also.\n\n"
76         "If login is successful, the application parses the dialed number in\n"
77         "the specified (or default) context, and returns 0 with the new extension\n"
78         "context filled-in and the priority set to 1, so that the PBX may\n"
79         "re-apply the routing tables to it and complete the call normally.";
80
81
82 STANDARD_LOCAL_USER;
83
84 LOCAL_USER_DECL;
85
86 static float loudness=4096.0;
87
88 static int firstdigittimeout = 20000; /* 20 seconds first digit timeout */
89 static int digittimeout = 10000; /* 10 seconds subsequent digit timeout */
90
91 static void make_tone_block(unsigned char *data, float f1, float f2, int len, int *x)
92 {
93 int     i;
94 float   val;
95
96         for(i = 0; i < len; i++)
97         {
98                 val = loudness * sin((f1 * 2.0 * M_PI * (*x))/8000.0);
99                 val += loudness * sin((f2 * 2.0 * M_PI * (*x)++)/8000.0);
100                 data[i] = AST_LIN2MU((int)val);
101          }              
102           /* wrap back around from 8000 */
103         if (*x >= 8000) *x = 0;
104         return;
105 }
106
107 static int ms_diff(struct timeval *tv1, struct timeval *tv2)
108 {
109 int     ms;
110         
111         ms = (tv1->tv_sec - tv2->tv_sec) * 1000;
112         ms += (tv1->tv_usec - tv2->tv_usec) / 1000;
113         return(ms);
114 }
115
116 static int disa_exec(struct ast_channel *chan, void *data)
117 {
118         int i,j,k,x;
119         struct localuser *u;
120         char tmp[256],arg2[256],exten[AST_MAX_EXTENSION],acctcode[20];
121         struct {
122                 unsigned char offset[AST_FRIENDLY_OFFSET];
123                 unsigned char buf[640];
124         } tone_block;
125         char *ourcontext,*ourcallerid;
126         struct ast_frame *f,wf;
127         struct timeval lastout, now, lastdigittime;
128         int res;
129         FILE *fp;
130         char *stringp=NULL;
131
132         if (ast_set_write_format(chan,AST_FORMAT_ULAW))
133         {
134                 ast_log(LOG_WARNING, "Unable to set write format to Mu-law on %s\n",chan->name);
135                 return -1;
136         }
137         if (ast_set_read_format(chan,AST_FORMAT_ULAW))
138         {
139                 ast_log(LOG_WARNING, "Unable to set read format to Mu-law on %s\n",chan->name);
140                 return -1;
141         }
142         lastout.tv_sec = lastout.tv_usec = 0;
143         if (!data || !strlen((char *)data)) {
144                 ast_log(LOG_WARNING, "disa requires an argument (passcode/passcode file)\n");
145                 return -1;
146         }
147         strncpy(tmp, (char *)data, sizeof(tmp)-1);
148         stringp=tmp;
149         strsep(&stringp, "|");
150         ourcontext = strsep(&stringp, "|");
151         /* if context specified, save 2nd arg and parse third */
152         if (ourcontext) {
153                 strcpy(arg2,ourcontext);
154                 ourcallerid = strsep(&stringp,"|");
155         }
156           /* if context not specified, use "disa" */
157         else {
158                 arg2[0] = 0;
159                 ourcallerid = NULL;
160                 ourcontext = "disa";
161         }
162         LOCAL_USER_ADD(u);
163         if (chan->_state != AST_STATE_UP)
164         {
165                 /* answer */
166                 ast_answer(chan);
167         }
168         i = k = x = 0; /* k is 0 for pswd entry, 1 for ext entry */
169         exten[0] = 0;
170         acctcode[0] = 0;
171         /* can we access DISA without password? */ 
172         if (!strcasecmp(tmp, "no-password"))
173         {;
174                 k = 1;
175                 ast_log(LOG_DEBUG, "DISA no-password login success\n");
176         }
177         gettimeofday(&lastdigittime,NULL);
178         for(;;)
179         {
180                 gettimeofday(&now,NULL);
181                   /* if outa time, give em reorder */
182                 if (ms_diff(&now,&lastdigittime) > 
183                     ((k) ? digittimeout : firstdigittimeout))
184                 {
185                         ast_log(LOG_DEBUG,"DISA %s entry timeout on chan %s\n",
186                                 ((k) ? "extension" : "password"),chan->name);
187                         break;
188                 }
189                 if ((res = ast_waitfor(chan, -1) < 0)) {
190                         ast_log(LOG_DEBUG, "Waitfor returned %d\n", res);
191                         continue;
192                 }
193                         
194                 f = ast_read(chan);
195                 if (f == NULL) 
196                 {
197                         LOCAL_USER_REMOVE(u);
198                         return -1;
199                 }
200                 if ((f->frametype == AST_FRAME_CONTROL) &&
201                     (f->subclass == AST_CONTROL_HANGUP))
202                 {
203                         ast_frfree(f);
204                         LOCAL_USER_REMOVE(u);
205                         return -1;
206                 }
207                 if (f->frametype == AST_FRAME_VOICE) {
208                         if (!i || (ast_ignore_pattern(ourcontext, exten) && k)) {
209                                 wf.frametype = AST_FRAME_VOICE;
210                                 wf.subclass = AST_FORMAT_ULAW;
211                                 wf.offset = AST_FRIENDLY_OFFSET;
212                                 wf.mallocd = 0;
213                                 wf.data = tone_block.buf;
214                                 wf.datalen = f->datalen;
215                                 wf.delivery.tv_sec = wf.delivery.tv_usec = 0;
216                                 make_tone_block(tone_block.buf, 350, 440, f->datalen, &x);
217                                 wf.samples = wf.datalen;
218                                 ast_frfree(f);
219                             if (ast_write(chan, &wf)) 
220                                 {
221                                 ast_log(LOG_WARNING, "DISA Failed to write frame on %s\n",chan->name);
222                                         LOCAL_USER_REMOVE(u);
223                                         return -1;
224                                 }
225                         } else
226                                 ast_frfree(f);
227                         continue;
228                 }
229                   /* if not DTMF, just do it again */
230                 if (f->frametype != AST_FRAME_DTMF) 
231                 {
232                         ast_frfree(f);
233                         continue;
234                 }
235
236                 j = f->subclass;  /* save digit */
237                 ast_frfree(f);
238                 
239                 gettimeofday(&lastdigittime,NULL);
240                   /* got a DTMF tone */
241                 if (i < AST_MAX_EXTENSION) /* if still valid number of digits */
242                 {
243                         if (!k) /* if in password state */
244                         {
245                                 if (j == '#') /* end of password */
246                                 {
247                                           /* see if this is an integer */
248                                         if (sscanf(tmp,"%d",&j) < 1)
249                                            { /* nope, it must be a filename */
250                                                 fp = fopen(tmp,"r");
251                                                 if (!fp)
252                                                    {
253                                                         ast_log(LOG_WARNING,"DISA password file %s not found on chan %s\n",tmp,chan->name);
254                                                         LOCAL_USER_REMOVE(u);
255                                                         return -1;
256                                                    }
257                                                 tmp[0] = 0;
258                                                 while(fgets(tmp,sizeof(tmp) - 1,fp))
259                                                    {
260                                                         char *stringp=NULL,*stringp2;
261                                                         if (!tmp[0]) continue;
262                                                         if (tmp[strlen(tmp) - 1] == '\n') 
263                                                                 tmp[strlen(tmp) - 1] = 0;
264                                                         if (!tmp[0]) continue;
265                                                           /* skip comments */
266                                                         if (tmp[0] == '#') continue;
267                                                         if (tmp[0] == ';') continue;
268                                                         stringp=tmp;
269                                                         strsep(&stringp, "|");
270                                                         stringp2=strsep(&stringp, "|");
271                                                         if (stringp2) {
272                                                                 ourcontext=stringp2;
273                                                                 stringp2=strsep(&stringp, "|");
274                                                                 if (stringp2) ourcallerid=stringp2;
275                                                         }
276                                                           /* password must be in valid format (numeric) */
277                                                         if (sscanf(tmp,"%d",&j) < 1) continue;
278                                                           /* if we got it */
279                                                         if (!strcmp(exten,tmp)) break;
280                                                    }
281                                                 fclose(fp);
282                                            }
283                                           /* compare the two */
284                                         if (strcmp(exten,tmp))
285                                         {
286                                                 ast_log(LOG_WARNING,"DISA on chan %s got bad password %s\n",chan->name,exten);
287                                                 goto reorder;
288
289                                         }
290                                          /* password good, set to dial state */
291                                                 ast_log(LOG_WARNING,"DISA on chan %s password is good\n",chan->name);
292                                         k = 1;
293                                         i = 0;  /* re-set buffer pointer */
294                                         exten[sizeof(acctcode)] = 0;
295                                         strcpy(acctcode,exten);
296                                         exten[0] = 0;
297                                         ast_log(LOG_DEBUG,"Successful DISA log-in on chan %s\n",chan->name);
298                                         continue;
299                                 }
300                         }
301                         exten[i++] = j;  /* save digit */
302                         exten[i] = 0;
303                         if (!k) continue; /* if getting password, continue doing it */
304                           /* if this exists */
305
306                           /* if can do some more, do it */
307                         if (!ast_matchmore_extension(chan,ourcontext,exten,1, chan->callerid)) 
308                                 break;
309                 }
310         }
311
312         if (k && ast_exists_extension(chan,ourcontext,exten,1, chan->callerid))
313         {
314                 /* We're authenticated and have a valid extension */
315                 if (ourcallerid && *ourcallerid)
316                 {
317                         if (chan->callerid) free(chan->callerid);
318                         chan->callerid = strdup(ourcallerid);
319                 }
320                 strcpy(chan->exten,exten);
321                 strcpy(chan->context,ourcontext);
322                 strcpy(chan->accountcode,acctcode);
323                 chan->priority = 0;
324                 ast_cdr_init(chan->cdr,chan);
325                 LOCAL_USER_REMOVE(u);
326                 return 0;
327         }
328
329 reorder:
330                 
331         /* something is invalid, give em reorder forever */
332         x = 0;
333         k = 0;  /* k = 0 means busy tone, k = 1 means silence) */
334         i = 0;  /* Number of samples we've done */
335         for(;;)
336         {
337                 if (ast_waitfor(chan, -1) < 0)
338                         break;
339                 f = ast_read(chan);
340                 if (!f)
341                         break;
342                 if (f->frametype == AST_FRAME_VOICE) {
343                         wf.frametype = AST_FRAME_VOICE;
344                         wf.subclass = AST_FORMAT_ULAW;
345                         wf.offset = AST_FRIENDLY_OFFSET;
346                         wf.mallocd = 0;
347                         wf.data = tone_block.buf;
348                         wf.datalen = f->datalen;
349                         wf.samples = wf.datalen;
350                         if (k) 
351                                 memset(tone_block.buf, 0x7f, wf.datalen);
352                         else
353                                 make_tone_block(tone_block.buf,480.0, 620.0,wf.datalen, &x);
354                         i += wf.datalen / 8;
355                         if (i > 250) {
356                                 i = 0;
357                                 k = !k;
358                         }
359                     if (ast_write(chan, &wf)) 
360                         {
361                         ast_log(LOG_WARNING, "DISA Failed to write frame on %s\n",chan->name);
362                                 LOCAL_USER_REMOVE(u);
363                                 return -1;
364                         }
365                 }
366                 ast_frfree(f);
367         }
368         LOCAL_USER_REMOVE(u);
369         return -1;
370 }
371
372 int unload_module(void)
373 {
374         STANDARD_HANGUP_LOCALUSERS;
375         return ast_unregister_application(app);
376 }
377
378 int load_module(void)
379 {
380         return ast_register_application(app, disa_exec, synopsis, descrip);
381 }
382
383 char *description(void)
384 {
385         return tdesc;
386 }
387
388 int usecount(void)
389 {
390         int res;
391         STANDARD_USECOUNT(res);
392         return res;
393 }
394
395 char *key(void)
396 {
397         return ASTERISK_GPL_KEY;
398 }