Version 0.1.9 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 + 32768];
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];
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));
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                                          /* password good, set to dial state */
260                                         k = 1;
261                                         i = 0;  /* re-set buffer pointer */
262                                         exten[0] = 0;
263                                         ast_log(LOG_DEBUG,"Successful DISA log-in on chan %s\n",chan->name);
264                                         continue;
265                                 }
266                         }
267                         exten[i++] = j;  /* save digit */
268                         exten[i] = 0;
269                         if (!k) continue; /* if getting password, continue doing it */
270                           /* if this exists */
271                         if (ast_exists_extension(chan,ourcontext,exten,1, chan->callerid))
272                         {
273                                 strcpy(chan->exten,exten);
274                                 strcpy(chan->context,ourcontext);
275                                 chan->priority = 0;
276                                 LOCAL_USER_REMOVE(u);
277                                 return 0;
278                         }
279                           /* if can do some more, do it */
280                         if (ast_canmatch_extension(chan,ourcontext,exten,1, chan->callerid)) continue;
281                 }
282 reorder:
283                 /* something is invalid, give em reorder forever */
284                 x = 0;
285                 for(;;)
286                 {
287                         for(i = 0; i < 10; i++)
288                         {
289                                 do gettimeofday(&now,NULL);
290                                 while (lastout.tv_sec && 
291                                         (ms_diff(&now,&lastout) < 25)) ;
292                                 lastout.tv_sec = now.tv_sec;
293                                 lastout.tv_usec = now.tv_usec;
294                                 wf.frametype = AST_FRAME_VOICE;
295                                 wf.subclass = AST_FORMAT_ULAW;
296                                 wf.offset = AST_FRIENDLY_OFFSET;
297                                 wf.mallocd = 0;
298                                 wf.data = tone_block;
299                                 wf.datalen = TONE_BLOCK_SIZE;
300                                 /* make this tone block */
301                                 make_tone_block(tone_block,480.0,620.0,&x);
302                                 wf.timelen = wf.datalen / 8;
303                                 if (ast_write(chan, &wf)) 
304                                 {
305                                         ast_log(LOG_WARNING, "DISA Failed to write frame on %s\n",chan->name);
306                                         LOCAL_USER_REMOVE(u);
307                                         return -1;
308                                 }
309                                 FD_ZERO(&readfds);
310                                 FD_SET(chan->fds[0],&readfds);
311                                   /* if no read avail, do send again */
312                                 if (select(chan->fds[0] + 1,&readfds,NULL,
313                                         NULL,&notime) < 1) continue;
314                                   /* read frame */
315                                 f = ast_read(chan);
316                                 if (f == NULL) 
317                                 {
318                                         LOCAL_USER_REMOVE(u);
319                                         return -1;
320                                 }
321                                 if ((f->frametype == AST_FRAME_CONTROL) &&
322                                     (f->subclass == AST_CONTROL_HANGUP))
323                                 {
324                                         ast_frfree(f);
325                                         LOCAL_USER_REMOVE(u);
326                                         return -1;
327                                 }
328                                 ast_frfree(f);
329                         }
330                         for(i = 0; i < 10; i++)
331                         {
332                                 do gettimeofday(&now,NULL);
333                                 while (lastout.tv_sec && 
334                                         (ms_diff(&now,&lastout) < 25)) ;
335                                 lastout.tv_sec = now.tv_sec;
336                                 lastout.tv_usec = now.tv_usec;
337                                 wf.frametype = AST_FRAME_VOICE;
338                                 wf.subclass = AST_FORMAT_ULAW;
339                                 wf.offset = AST_FRIENDLY_OFFSET;
340                                 wf.mallocd = 0;
341                                 wf.data = sil_block;
342                                 wf.datalen = TONE_BLOCK_SIZE;
343                                 wf.timelen = wf.datalen / 8;
344                                 if (ast_write(chan, &wf)) 
345                                 {
346                                         ast_log(LOG_WARNING, "DISA Failed to write frame on %s\n",chan->name);
347                                         LOCAL_USER_REMOVE(u);
348                                         return -1;
349                                 }
350                                 FD_ZERO(&readfds);
351                                 FD_SET(chan->fds[0],&readfds);
352                                   /* if no read avail, do send again */
353                                 if (select(chan->fds[0] + 1,&readfds,NULL,
354                                         NULL,&notime) < 1) continue;
355                                   /* read frame */
356                                 f = ast_read(chan);
357                                 if (f == NULL) 
358                                 {
359                                         LOCAL_USER_REMOVE(u);
360                                         return -1;
361                                 }
362                                 if ((f->frametype == AST_FRAME_CONTROL) &&
363                                     (f->subclass == AST_CONTROL_HANGUP))
364                                 {
365                                         ast_frfree(f);
366                                         LOCAL_USER_REMOVE(u);
367                                         return -1;
368                                 }
369                                 ast_frfree(f);
370                         }
371                 }
372         }
373 }
374
375 int unload_module(void)
376 {
377         STANDARD_HANGUP_LOCALUSERS;
378         return ast_unregister_application(app);
379 }
380
381 int load_module(void)
382 {
383         return ast_register_application(app, disa_exec, synopsis, descrip);
384 }
385
386 char *description(void)
387 {
388         return tdesc;
389 }
390
391 int usecount(void)
392 {
393         int res;
394         STANDARD_USECOUNT(res);
395         return res;
396 }
397
398 char *key()
399 {
400         return ASTERISK_GPL_KEY;
401 }