Update DISA to be internationalized (bug #2284)
[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 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;
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         exten[0] = 0;
156         acctcode[0] = 0;
157         /* can we access DISA without password? */ 
158
159         ast_log(LOG_DEBUG, "Context: %s\n",ourcontext);
160
161         if (!strcasecmp(tmp, "no-password"))
162         {;
163                 k = 1;
164                 ast_log(LOG_DEBUG, "DISA no-password login success\n");
165         }
166         gettimeofday(&lastdigittime,NULL);
167
168         play_dialtone(chan);
169
170         for(;;)
171         {
172                 gettimeofday(&now,NULL);
173                   /* if outa time, give em reorder */
174                 if (ms_diff(&now,&lastdigittime) > 
175                     ((k) ? digittimeout : firstdigittimeout))
176                 {
177                         ast_log(LOG_DEBUG,"DISA %s entry timeout on chan %s\n",
178                                 ((k) ? "extension" : "password"),chan->name);
179                         break;
180                 }
181                 if ((res = ast_waitfor(chan, -1) < 0)) {
182                         ast_log(LOG_DEBUG, "Waitfor returned %d\n", res);
183                         continue;
184                 }
185                         
186                 f = ast_read(chan);
187                 if (f == NULL) 
188                 {
189                         LOCAL_USER_REMOVE(u);
190                         return -1;
191                 }
192                 if ((f->frametype == AST_FRAME_CONTROL) &&
193                     (f->subclass == AST_CONTROL_HANGUP))
194                 {
195                         ast_frfree(f);
196                         LOCAL_USER_REMOVE(u);
197                         return -1;
198                 }
199                 if (f->frametype == AST_FRAME_VOICE) {
200                         ast_frfree(f);
201                         continue;
202                 }
203                   /* if not DTMF, just do it again */
204                 if (f->frametype != AST_FRAME_DTMF) 
205                 {
206                         ast_frfree(f);
207                         continue;
208                 }
209
210                 j = f->subclass;  /* save digit */
211                 ast_frfree(f);
212                 if (i == 0) 
213                         ast_playtones_stop(chan);
214
215                 gettimeofday(&lastdigittime,NULL);
216                   /* got a DTMF tone */
217                 if (i < AST_MAX_EXTENSION) /* if still valid number of digits */
218                 {
219                         if (!k) /* if in password state */
220                         {
221                                 if (j == '#') /* end of password */
222                                 {
223                                           /* see if this is an integer */
224                                         if (sscanf(tmp,"%d",&j) < 1)
225                                            { /* nope, it must be a filename */
226                                                 fp = fopen(tmp,"r");
227                                                 if (!fp)
228                                                    {
229                                                         ast_log(LOG_WARNING,"DISA password file %s not found on chan %s\n",tmp,chan->name);
230                                                         LOCAL_USER_REMOVE(u);
231                                                         return -1;
232                                                    }
233                                                 tmp[0] = 0;
234                                                 while(fgets(tmp,sizeof(tmp) - 1,fp))
235                                                    {
236                                                         char *stringp=NULL,*stringp2;
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                                                         stringp=tmp;
245                                                         strsep(&stringp, "|");
246                                                         stringp2=strsep(&stringp, "|");
247                                                         if (stringp2) {
248                                                                 ourcontext=stringp2;
249                                                                 stringp2=strsep(&stringp, "|");
250                                                                 if (stringp2) ourcallerid=stringp2;
251                                                         }
252                                                           /* password must be in valid format (numeric) */
253                                                         if (sscanf(tmp,"%d",&j) < 1) continue;
254                                                           /* if we got it */
255                                                         if (!strcmp(exten,tmp)) break;
256                                                    }
257                                                 fclose(fp);
258                                            }
259                                           /* compare the two */
260                                         if (strcmp(exten,tmp))
261                                         {
262                                                 ast_log(LOG_WARNING,"DISA on chan %s got bad password %s\n",chan->name,exten);
263                                                 goto reorder;
264
265                                         }
266                                          /* password good, set to dial state */
267                                         ast_log(LOG_DEBUG,"DISA on chan %s password is good\n",chan->name);
268                                         play_dialtone(chan);
269
270                                         k = 1;
271                                         i = 0;  /* re-set buffer pointer */
272                                         exten[sizeof(acctcode)] = 0;
273                                         strncpy(acctcode,exten, sizeof(acctcode) - 1);
274                                         exten[0] = 0;
275                                         ast_log(LOG_DEBUG,"Successful DISA log-in on chan %s\n",chan->name);
276                                         continue;
277                                 }
278                         }
279                         exten[i++] = j;  /* save digit */
280                         exten[i] = 0;
281                         if (!k) continue; /* if getting password, continue doing it */
282                           /* if this exists */
283
284                           /* if can do some more, do it */
285                         if (!ast_matchmore_extension(chan,ourcontext,exten,1, chan->callerid)) {
286                                 break;
287                         }
288                 }
289         }
290
291         if (k && ast_exists_extension(chan,ourcontext,exten,1, chan->callerid))
292         {
293                 ast_playtones_stop(chan);
294                 /* We're authenticated and have a valid extension */
295                 if (ourcallerid && *ourcallerid)
296                 {
297                         if (chan->callerid) free(chan->callerid);
298                         chan->callerid = strdup(ourcallerid);
299                 }
300                 strncpy(chan->exten, exten, sizeof(chan->exten) - 1);
301                 strncpy(chan->context, ourcontext, sizeof(chan->context) - 1);
302                 strncpy(chan->accountcode, acctcode, sizeof(chan->accountcode) - 1);
303                 chan->priority = 0;
304                 ast_cdr_init(chan->cdr,chan);
305                 LOCAL_USER_REMOVE(u);
306                 return 0;
307         }
308
309 reorder:
310
311         ast_indicate(chan,AST_CONTROL_CONGESTION);
312         /* something is invalid, give em reorder for several seconds */
313         time(&rstart);
314         while(time(NULL) < rstart + 10)
315         {
316                 if (ast_waitfor(chan, -1) < 0)
317                         break;
318                 f = ast_read(chan);
319                 if (!f)
320                         break;
321                 ast_frfree(f);
322         }
323         ast_playtones_stop(chan);
324         LOCAL_USER_REMOVE(u);
325         return -1;
326 }
327
328 int unload_module(void)
329 {
330         STANDARD_HANGUP_LOCALUSERS;
331         return ast_unregister_application(app);
332 }
333
334 int load_module(void)
335 {
336         return ast_register_application(app, disa_exec, synopsis, descrip);
337 }
338
339 char *description(void)
340 {
341         return tdesc;
342 }
343
344 int usecount(void)
345 {
346         int res;
347         STANDARD_USECOUNT(res);
348         return res;
349 }
350
351 char *key(void)
352 {
353         return ASTERISK_GPL_KEY;
354 }