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