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