6d1964990bf6b29d6fe403ae17f4d3adce5ce497
[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-2004, Digium, 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 void play_dialtone(struct ast_channel *chan)
96 {
97         const struct tone_zone_sound *ts = NULL;
98         ts = ast_get_indication_tone(chan->zone, "dial");
99         if (ts)
100                 ast_playtones_start(chan, 0, ts->data, 0);
101         else
102                 ast_tonepair_start(chan, 350, 440, 0, 0);
103 }
104
105 static int disa_exec(struct ast_channel *chan, void *data)
106 {
107         int i,j,k,x,did_ignore;
108         struct localuser *u;
109         char tmp[256],arg2[256]="",exten[AST_MAX_EXTENSION],acctcode[20]="";
110         char *ourcontext,*ourcallerid;
111         struct ast_frame *f;
112         struct timeval lastout, now, lastdigittime;
113         int res;
114         time_t rstart;
115         FILE *fp;
116         char *stringp=NULL;
117
118         if (ast_set_write_format(chan,AST_FORMAT_ULAW))
119         {
120                 ast_log(LOG_WARNING, "Unable to set write format to Mu-law on %s\n",chan->name);
121                 return -1;
122         }
123         if (ast_set_read_format(chan,AST_FORMAT_ULAW))
124         {
125                 ast_log(LOG_WARNING, "Unable to set read format to Mu-law on %s\n",chan->name);
126                 return -1;
127         }
128         lastout.tv_sec = lastout.tv_usec = 0;
129         if (!data || !strlen((char *)data)) {
130                 ast_log(LOG_WARNING, "disa requires an argument (passcode/passcode file)\n");
131                 return -1;
132         }
133         strncpy(tmp, (char *)data, sizeof(tmp)-1);
134         stringp=tmp;
135         strsep(&stringp, "|");
136         ourcontext = strsep(&stringp, "|");
137         /* if context specified, save 2nd arg and parse third */
138         if (ourcontext) {
139                 strncpy(arg2,ourcontext, sizeof(arg2) - 1);
140                 ourcallerid = strsep(&stringp,"|");
141         }
142           /* if context not specified, use "disa" */
143         else {
144                 arg2[0] = 0;
145                 ourcallerid = NULL;
146                 ourcontext = "disa";
147         }
148         LOCAL_USER_ADD(u);
149         if (chan->_state != AST_STATE_UP)
150         {
151                 /* answer */
152                 ast_answer(chan);
153         }
154         i = k = x = 0; /* k is 0 for pswd entry, 1 for ext entry */
155         did_ignore = 0;
156         exten[0] = 0;
157         acctcode[0] = 0;
158         /* can we access DISA without password? */ 
159
160         ast_log(LOG_DEBUG, "Context: %s\n",ourcontext);
161
162         if (!strcasecmp(tmp, "no-password"))
163         {;
164                 k = 1;
165                 ast_log(LOG_DEBUG, "DISA no-password login success\n");
166         }
167         gettimeofday(&lastdigittime,NULL);
168
169         play_dialtone(chan);
170
171         for(;;)
172         {
173                 gettimeofday(&now,NULL);
174                   /* if outa time, give em reorder */
175                 if (ms_diff(&now,&lastdigittime) > 
176                     ((k) ? digittimeout : firstdigittimeout))
177                 {
178                         ast_log(LOG_DEBUG,"DISA %s entry timeout on chan %s\n",
179                                 ((k) ? "extension" : "password"),chan->name);
180                         break;
181                 }
182                 if ((res = ast_waitfor(chan, -1) < 0)) {
183                         ast_log(LOG_DEBUG, "Waitfor returned %d\n", res);
184                         continue;
185                 }
186                         
187                 f = ast_read(chan);
188                 if (f == NULL) 
189                 {
190                         LOCAL_USER_REMOVE(u);
191                         return -1;
192                 }
193                 if ((f->frametype == AST_FRAME_CONTROL) &&
194                     (f->subclass == AST_CONTROL_HANGUP))
195                 {
196                         ast_frfree(f);
197                         LOCAL_USER_REMOVE(u);
198                         return -1;
199                 }
200                 if (f->frametype == AST_FRAME_VOICE) {
201                         ast_frfree(f);
202                         continue;
203                 }
204                   /* if not DTMF, just do it again */
205                 if (f->frametype != AST_FRAME_DTMF) 
206                 {
207                         ast_frfree(f);
208                         continue;
209                 }
210
211                 j = f->subclass;  /* save digit */
212                 ast_frfree(f);
213                 if (i == 0) 
214                         ast_playtones_stop(chan);
215
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                                                         char *stringp=NULL,*stringp2;
238                                                         if (!tmp[0]) continue;
239                                                         if (tmp[strlen(tmp) - 1] == '\n') 
240                                                                 tmp[strlen(tmp) - 1] = 0;
241                                                         if (!tmp[0]) continue;
242                                                           /* skip comments */
243                                                         if (tmp[0] == '#') continue;
244                                                         if (tmp[0] == ';') continue;
245                                                         stringp=tmp;
246                                                         strsep(&stringp, "|");
247                                                         stringp2=strsep(&stringp, "|");
248                                                         if (stringp2) {
249                                                                 ourcontext=stringp2;
250                                                                 stringp2=strsep(&stringp, "|");
251                                                                 if (stringp2) ourcallerid=stringp2;
252                                                         }
253                                                           /* password must be in valid format (numeric) */
254                                                         if (sscanf(tmp,"%d",&j) < 1) continue;
255                                                           /* if we got it */
256                                                         if (!strcmp(exten,tmp)) break;
257                                                    }
258                                                 fclose(fp);
259                                            }
260                                           /* compare the two */
261                                         if (strcmp(exten,tmp))
262                                         {
263                                                 ast_log(LOG_WARNING,"DISA on chan %s got bad password %s\n",chan->name,exten);
264                                                 goto reorder;
265
266                                         }
267                                          /* password good, set to dial state */
268                                         ast_log(LOG_DEBUG,"DISA on chan %s password is good\n",chan->name);
269                                         play_dialtone(chan);
270
271                                         k = 1;
272                                         i = 0;  /* re-set buffer pointer */
273                                         exten[sizeof(acctcode)] = 0;
274                                         strncpy(acctcode,exten, sizeof(acctcode) - 1);
275                                         exten[0] = 0;
276                                         ast_log(LOG_DEBUG,"Successful DISA log-in on chan %s\n",chan->name);
277                                         continue;
278                                 }
279                         }
280
281                         exten[i++] = j;  /* save digit */
282                         exten[i] = 0;
283                         if (!k) continue; /* if getting password, continue doing it */
284                           /* if this exists */
285
286                         if (ast_ignore_pattern(ourcontext, exten)) {
287                                 play_dialtone(chan);
288                                 did_ignore = 1;
289                         } else
290                                 if (did_ignore) {
291                                         ast_playtones_stop(chan);
292                                         did_ignore = 0;
293                                 }
294
295                           /* if can do some more, do it */
296                         if (!ast_matchmore_extension(chan,ourcontext,exten,1, chan->cid.cid_num)) {
297                                 break;
298                         }
299                 }
300         }
301
302         if (k && ast_exists_extension(chan,ourcontext,exten,1, chan->cid.cid_num))
303         {
304                 ast_playtones_stop(chan);
305                 /* We're authenticated and have a valid extension */
306                 if (ourcallerid && *ourcallerid)
307                 {
308                         if (chan->cid.cid_num) free(chan->cid.cid_num);
309                         chan->cid.cid_num = strdup(ourcallerid);
310                 }
311                 strncpy(chan->exten, exten, sizeof(chan->exten) - 1);
312                 strncpy(chan->context, ourcontext, sizeof(chan->context) - 1);
313                 strncpy(chan->accountcode, acctcode, sizeof(chan->accountcode) - 1);
314                 chan->priority = 0;
315                 ast_cdr_init(chan->cdr,chan);
316                 LOCAL_USER_REMOVE(u);
317                 return 0;
318         }
319
320 reorder:
321
322         ast_indicate(chan,AST_CONTROL_CONGESTION);
323         /* something is invalid, give em reorder for several seconds */
324         time(&rstart);
325         while(time(NULL) < rstart + 10)
326         {
327                 if (ast_waitfor(chan, -1) < 0)
328                         break;
329                 f = ast_read(chan);
330                 if (!f)
331                         break;
332                 ast_frfree(f);
333         }
334         ast_playtones_stop(chan);
335         LOCAL_USER_REMOVE(u);
336         return -1;
337 }
338
339 int unload_module(void)
340 {
341         STANDARD_HANGUP_LOCALUSERS;
342         return ast_unregister_application(app);
343 }
344
345 int load_module(void)
346 {
347         return ast_register_application(app, disa_exec, synopsis, descrip);
348 }
349
350 char *description(void)
351 {
352         return tdesc;
353 }
354
355 int usecount(void)
356 {
357         int res;
358         STANDARD_USECOUNT(res);
359         return res;
360 }
361
362 char *key(void)
363 {
364         return ASTERISK_GPL_KEY;
365 }