Merge rgagnon's pedantic string checks (apps a-m, bug #2035)
[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 <sys/time.h>
29
30 /*
31 #define TONE_BLOCK_SIZE 320
32 */
33
34 static char *tdesc = "DISA (Direct Inward System Access) Application";
35
36 static char *app = "DISA";
37
38 static char *synopsis = "DISA (Direct Inward System Access)";
39
40 static char *descrip = 
41         "DISA (Direct Inward System Access) -- Allows someone from outside\n"
42         "the telephone switch (PBX) to obtain an \"internal\" system dialtone\n"
43         "and to place calls from it as if they were placing a call from within\n"
44         "the switch. A user calls a number that connects to the DISA application\n"
45         "and is given dialtone. The user enters their passcode, followed by the\n"
46         "pound sign (#). If the passcode is correct, the user is then given\n"
47         "system dialtone on which a call may be placed. Obviously, this type\n"
48         "of access has SERIOUS security implications, and GREAT care must be\n"
49         "taken NOT to compromise your security.\n\n"
50         "There is a possibility of accessing DISA without password. Simply\n"
51         "exchange your password with no-password.\n\n"
52         "  Example: exten => s,1,DISA,no-password|local\n\n"
53         "but be aware of using this for your security compromising.\n\n"
54         "The arguments to this application (in extensions.conf) allow either\n"
55         "specification of a single global password (that everyone uses), or\n"
56         "individual passwords contained in a file. It also allow specification\n"
57         "of the context on which the user will be dialing. If no context is\n"
58         "specified, the DISA application defaults the context to \"disa\"\n"
59         "presumably that a normal system will have a special context set up\n"
60         "for DISA use with some or a lot of restrictions. The arguments are\n"
61         "one of the following:\n\n"
62         "    numeric-passcode\n"
63         "    numeric-passcode|context\n"
64         "    full-pathname-of-file-that-contains-passcodes\n\n"
65         "The file that contains the passcodes (if used) allows specification\n"
66         "of either just a passcode (defaulting to the \"disa\" context, or\n"
67         "passcode|context on each line of the file. The file may contain blank\n"
68         "lines, or comments starting with \"#\" or \";\". In addition, the\n"
69         "above arguments may have |new-callerid-string appended to them, to\n"
70         "specify a new (different) callerid to be used for this call, for\n"
71         "example: numeric-passcode|context|\"My Phone\" <(234) 123-4567> or \n"
72         "full-pathname-of-passcode-file|\"My Phone\" <(234) 123-4567>. Note that\n"
73         "in the case of specifying the numeric-passcode, the context must be\n"
74         "specified if the callerid is specified also.\n\n"
75         "If login is successful, the application parses the dialed number in\n"
76         "the specified (or default) context, and returns 0 with the new extension\n"
77         "context filled-in and the priority set to 1, so that the PBX may\n"
78         "re-apply the routing tables to it and complete the call normally.";
79
80
81 STANDARD_LOCAL_USER;
82
83 LOCAL_USER_DECL;
84
85 static float loudness=4096.0;
86
87 static int firstdigittimeout = 20000; /* 20 seconds first digit timeout */
88 static int digittimeout = 10000; /* 10 seconds subsequent digit timeout */
89
90 static void make_tone_block(unsigned char *data, float f1, float f2, int len, int *x)
91 {
92 int     i;
93 float   val;
94
95         for(i = 0; i < len; i++)
96         {
97                 val = loudness * sin((f1 * 2.0 * M_PI * (*x))/8000.0);
98                 val += loudness * sin((f2 * 2.0 * M_PI * (*x)++)/8000.0);
99                 data[i] = AST_LIN2MU((int)val);
100          }              
101           /* wrap back around from 8000 */
102         if (*x >= 8000) *x = 0;
103         return;
104 }
105
106 static int ms_diff(struct timeval *tv1, struct timeval *tv2)
107 {
108 int     ms;
109         
110         ms = (tv1->tv_sec - tv2->tv_sec) * 1000;
111         ms += (tv1->tv_usec - tv2->tv_usec) / 1000;
112         return(ms);
113 }
114
115 static int disa_exec(struct ast_channel *chan, void *data)
116 {
117         int i,j,k,x;
118         struct localuser *u;
119         char tmp[256],arg2[256]="",exten[AST_MAX_EXTENSION],acctcode[20]="";
120         struct {
121                 unsigned char offset[AST_FRIENDLY_OFFSET];
122                 unsigned char buf[640];
123         } tone_block;
124         char *ourcontext,*ourcallerid;
125         struct ast_frame *f,wf;
126         struct timeval lastout, now, lastdigittime;
127         int res;
128         FILE *fp;
129         char *stringp=NULL;
130
131         if (ast_set_write_format(chan,AST_FORMAT_ULAW))
132         {
133                 ast_log(LOG_WARNING, "Unable to set write format to Mu-law on %s\n",chan->name);
134                 return -1;
135         }
136         if (ast_set_read_format(chan,AST_FORMAT_ULAW))
137         {
138                 ast_log(LOG_WARNING, "Unable to set read format to Mu-law on %s\n",chan->name);
139                 return -1;
140         }
141         lastout.tv_sec = lastout.tv_usec = 0;
142         if (!data || !strlen((char *)data)) {
143                 ast_log(LOG_WARNING, "disa requires an argument (passcode/passcode file)\n");
144                 return -1;
145         }
146         strncpy(tmp, (char *)data, sizeof(tmp)-1);
147         stringp=tmp;
148         strsep(&stringp, "|");
149         ourcontext = strsep(&stringp, "|");
150         /* if context specified, save 2nd arg and parse third */
151         if (ourcontext) {
152                 strncpy(arg2,ourcontext, sizeof(arg2) - 1);
153                 ourcallerid = strsep(&stringp,"|");
154         }
155           /* if context not specified, use "disa" */
156         else {
157                 arg2[0] = 0;
158                 ourcallerid = NULL;
159                 ourcontext = "disa";
160         }
161         LOCAL_USER_ADD(u);
162         if (chan->_state != AST_STATE_UP)
163         {
164                 /* answer */
165                 ast_answer(chan);
166         }
167         i = k = x = 0; /* k is 0 for pswd entry, 1 for ext entry */
168         exten[0] = 0;
169         acctcode[0] = 0;
170         /* can we access DISA without password? */ 
171         if (!strcasecmp(tmp, "no-password"))
172         {;
173                 k = 1;
174                 ast_log(LOG_DEBUG, "DISA no-password login success\n");
175         }
176         gettimeofday(&lastdigittime,NULL);
177         for(;;)
178         {
179                 gettimeofday(&now,NULL);
180                   /* if outa time, give em reorder */
181                 if (ms_diff(&now,&lastdigittime) > 
182                     ((k) ? digittimeout : firstdigittimeout))
183                 {
184                         ast_log(LOG_DEBUG,"DISA %s entry timeout on chan %s\n",
185                                 ((k) ? "extension" : "password"),chan->name);
186                         break;
187                 }
188                 if ((res = ast_waitfor(chan, -1) < 0)) {
189                         ast_log(LOG_DEBUG, "Waitfor returned %d\n", res);
190                         continue;
191                 }
192                         
193                 f = ast_read(chan);
194                 if (f == NULL) 
195                 {
196                         LOCAL_USER_REMOVE(u);
197                         return -1;
198                 }
199                 if ((f->frametype == AST_FRAME_CONTROL) &&
200                     (f->subclass == AST_CONTROL_HANGUP))
201                 {
202                         ast_frfree(f);
203                         LOCAL_USER_REMOVE(u);
204                         return -1;
205                 }
206                 if (f->frametype == AST_FRAME_VOICE) {
207                         if (!i || (ast_ignore_pattern(ourcontext, exten) && k)) {
208                                 wf.frametype = AST_FRAME_VOICE;
209                                 wf.subclass = AST_FORMAT_ULAW;
210                                 wf.offset = AST_FRIENDLY_OFFSET;
211                                 wf.mallocd = 0;
212                                 wf.data = tone_block.buf;
213                                 wf.datalen = f->datalen;
214                                 wf.delivery.tv_sec = wf.delivery.tv_usec = 0;
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                                         strncpy(acctcode,exten, sizeof(acctcode) - 1);
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                 strncpy(chan->exten, exten, sizeof(chan->exten) - 1);
320                 strncpy(chan->context, ourcontext, sizeof(chan->context) - 1);
321                 strncpy(chan->accountcode, acctcode, sizeof(chan->accountcode) - 1);
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 }