Make read/write mode have a lock parameter and use it properly.
[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, 1))
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, 1))
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                                 make_tone_block(tone_block.buf, 350, 440, f->datalen, &x);
216                                 wf.samples = wf.datalen;
217                                 ast_frfree(f);
218                             if (ast_write(chan, &wf)) 
219                                 {
220                                 ast_log(LOG_WARNING, "DISA Failed to write frame on %s\n",chan->name);
221                                         LOCAL_USER_REMOVE(u);
222                                         return -1;
223                                 }
224                         } else
225                                 ast_frfree(f);
226                         continue;
227                 }
228                   /* if not DTMF, just do it again */
229                 if (f->frametype != AST_FRAME_DTMF) 
230                 {
231                         ast_frfree(f);
232                         continue;
233                 }
234
235                 j = f->subclass;  /* save digit */
236                 ast_frfree(f);
237                 
238                 gettimeofday(&lastdigittime,NULL);
239                   /* got a DTMF tone */
240                 if (i < AST_MAX_EXTENSION) /* if still valid number of digits */
241                 {
242                         if (!k) /* if in password state */
243                         {
244                                 if (j == '#') /* end of password */
245                                 {
246                                           /* see if this is an integer */
247                                         if (sscanf(tmp,"%d",&j) < 1)
248                                            { /* nope, it must be a filename */
249                                                 fp = fopen(tmp,"r");
250                                                 if (!fp)
251                                                    {
252                                                         ast_log(LOG_WARNING,"DISA password file %s not found on chan %s\n",tmp,chan->name);
253                                                         LOCAL_USER_REMOVE(u);
254                                                         return -1;
255                                                    }
256                                                 tmp[0] = 0;
257                                                 while(fgets(tmp,sizeof(tmp) - 1,fp))
258                                                    {
259                                                         char *stringp=NULL,*stringp2;
260                                                         if (!tmp[0]) continue;
261                                                         if (tmp[strlen(tmp) - 1] == '\n') 
262                                                                 tmp[strlen(tmp) - 1] = 0;
263                                                         if (!tmp[0]) continue;
264                                                           /* skip comments */
265                                                         if (tmp[0] == '#') continue;
266                                                         if (tmp[0] == ';') continue;
267                                                         stringp=tmp;
268                                                         strsep(&stringp, "|");
269                                                         stringp2=strsep(&stringp, "|");
270                                                         if (stringp2) {
271                                                                 ourcontext=stringp2;
272                                                                 stringp2=strsep(&stringp, "|");
273                                                                 if (stringp2) ourcallerid=stringp2;
274                                                         }
275                                                           /* password must be in valid format (numeric) */
276                                                         if (sscanf(tmp,"%d",&j) < 1) continue;
277                                                           /* if we got it */
278                                                         if (!strcmp(exten,tmp)) break;
279                                                    }
280                                                 fclose(fp);
281                                            }
282                                           /* compare the two */
283                                         if (strcmp(exten,tmp))
284                                         {
285                                                 ast_log(LOG_WARNING,"DISA on chan %s got bad password %s\n",chan->name,exten);
286                                                 goto reorder;
287
288                                         }
289                                          /* password good, set to dial state */
290                                                 ast_log(LOG_WARNING,"DISA on chan %s password is good\n",chan->name);
291                                         k = 1;
292                                         i = 0;  /* re-set buffer pointer */
293                                         exten[sizeof(acctcode)] = 0;
294                                         strcpy(acctcode,exten);
295                                         exten[0] = 0;
296                                         ast_log(LOG_DEBUG,"Successful DISA log-in on chan %s\n",chan->name);
297                                         continue;
298                                 }
299                         }
300                         exten[i++] = j;  /* save digit */
301                         exten[i] = 0;
302                         if (!k) continue; /* if getting password, continue doing it */
303                           /* if this exists */
304
305                           /* if can do some more, do it */
306                         if (!ast_matchmore_extension(chan,ourcontext,exten,1, chan->callerid)) 
307                                 break;
308                 }
309         }
310
311         if (k && ast_exists_extension(chan,ourcontext,exten,1, chan->callerid))
312         {
313                 /* We're authenticated and have a valid extension */
314                 if (ourcallerid && *ourcallerid)
315                 {
316                         if (chan->callerid) free(chan->callerid);
317                         chan->callerid = strdup(ourcallerid);
318                 }
319                 strcpy(chan->exten,exten);
320                 strcpy(chan->context,ourcontext);
321                 strcpy(chan->accountcode,acctcode);
322                 chan->priority = 0;
323                 ast_cdr_init(chan->cdr,chan);
324                 LOCAL_USER_REMOVE(u);
325                 return 0;
326         }
327
328 reorder:
329                 
330         /* something is invalid, give em reorder forever */
331         x = 0;
332         k = 0;  /* k = 0 means busy tone, k = 1 means silence) */
333         i = 0;  /* Number of samples we've done */
334         for(;;)
335         {
336                 if (ast_waitfor(chan, -1) < 0)
337                         break;
338                 f = ast_read(chan);
339                 if (!f)
340                         break;
341                 if (f->frametype == AST_FRAME_VOICE) {
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 = tone_block.buf;
347                         wf.datalen = f->datalen;
348                         wf.samples = wf.datalen;
349                         if (k) 
350                                 memset(tone_block.buf, 0x7f, wf.datalen);
351                         else
352                                 make_tone_block(tone_block.buf,480.0, 620.0,wf.datalen, &x);
353                         i += wf.datalen / 8;
354                         if (i > 250) {
355                                 i = 0;
356                                 k = !k;
357                         }
358                     if (ast_write(chan, &wf)) 
359                         {
360                         ast_log(LOG_WARNING, "DISA Failed to write frame on %s\n",chan->name);
361                                 LOCAL_USER_REMOVE(u);
362                                 return -1;
363                         }
364                 }
365                 ast_frfree(f);
366         }
367         LOCAL_USER_REMOVE(u);
368         return -1;
369 }
370
371 int unload_module(void)
372 {
373         STANDARD_HANGUP_LOCALUSERS;
374         return ast_unregister_application(app);
375 }
376
377 int load_module(void)
378 {
379         return ast_register_application(app, disa_exec, synopsis, descrip);
380 }
381
382 char *description(void)
383 {
384         return tdesc;
385 }
386
387 int usecount(void)
388 {
389         int res;
390         STANDARD_USECOUNT(res);
391         return res;
392 }
393
394 char *key(void)
395 {
396         return ASTERISK_GPL_KEY;
397 }