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