Internationalize DISA and fix Voicemail when receiving messages and you're checking...
[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         unsigned char tone_block[640];
122         char *ourcontext,*ourcallerid;
123         struct ast_frame *f,wf;
124         struct timeval lastout, now, lastdigittime;
125         int res;
126         FILE *fp;
127         char *stringp=NULL;
128
129         if (ast_set_write_format(chan,AST_FORMAT_ULAW))
130         {
131                 ast_log(LOG_WARNING, "Unable to set write format to Mu-law on %s\n",chan->name);
132                 return -1;
133         }
134         if (ast_set_read_format(chan,AST_FORMAT_ULAW))
135         {
136                 ast_log(LOG_WARNING, "Unable to set read format to Mu-law on %s\n",chan->name);
137                 return -1;
138         }
139         lastout.tv_sec = lastout.tv_usec = 0;
140         if (!data || !strlen((char *)data)) {
141                 ast_log(LOG_WARNING, "disa requires an argument (passcode/passcode file)\n");
142                 return -1;
143         }
144         strncpy(tmp, (char *)data, sizeof(tmp)-1);
145         stringp=tmp;
146         strsep(&stringp, "|");
147         ourcontext = strsep(&stringp, "|");
148         /* if context specified, save 2nd arg and parse third */
149         if (ourcontext) {
150                 strcpy(arg2,ourcontext);
151                 ourcallerid = strsep(&stringp,"|");
152         }
153           /* if context not specified, use "disa" */
154         else {
155                 arg2[0] = 0;
156                 ourcallerid = NULL;
157                 ourcontext = "disa";
158         }
159         LOCAL_USER_ADD(u);
160         if (chan->_state != AST_STATE_UP)
161         {
162                 /* answer */
163                 ast_answer(chan);
164         }
165         i = k = x = 0; /* k is 0 for pswd entry, 1 for ext entry */
166         exten[0] = 0;
167         acctcode[0] = 0;
168         /* can we access DISA without password? */ 
169         if (!strcasecmp(tmp, "no-password"))
170         {
171                 k = 1;
172                 ast_log(LOG_DEBUG, "DISA no-password login success\n");
173         }
174         gettimeofday(&lastdigittime,NULL);
175         for(;;)
176         {
177                 gettimeofday(&now,NULL);
178                   /* if outa time, give em reorder */
179                 if (ms_diff(&now,&lastdigittime) > 
180                     ((k) ? digittimeout : firstdigittimeout))
181                 {
182                         ast_log(LOG_DEBUG,"DISA %s entry timeout on chan %s\n",
183                                 ((k) ? "extension" : "password"),chan->name);
184                         break;
185                 }
186                 if ((res = ast_waitfor(chan, -1) < 0)) {
187                         ast_log(LOG_DEBUG, "Waitfor returned %d\n", res);
188                         continue;
189                 }
190                         
191                 f = ast_read(chan);
192                 if (f == NULL) 
193                 {
194                         LOCAL_USER_REMOVE(u);
195                         return -1;
196                 }
197                 if ((f->frametype == AST_FRAME_CONTROL) &&
198                     (f->subclass == AST_CONTROL_HANGUP))
199                 {
200                         ast_frfree(f);
201                         LOCAL_USER_REMOVE(u);
202                         return -1;
203                 }
204                 if (f->frametype == AST_FRAME_VOICE) {
205                         if (!i || (ast_ignore_pattern(ourcontext, exten) && k)) {
206                                 wf.frametype = AST_FRAME_VOICE;
207                                 wf.subclass = AST_FORMAT_ULAW;
208                                 wf.offset = AST_FRIENDLY_OFFSET;
209                                 wf.mallocd = 0;
210                                 wf.data = tone_block;
211                                 wf.datalen = f->datalen;
212                                 make_tone_block(tone_block, 350, 440, f->datalen, &x);
213                                 wf.samples = wf.datalen;
214                                 ast_frfree(f);
215                             if (ast_write(chan, &wf)) 
216                                 {
217                                 ast_log(LOG_WARNING, "DISA Failed to write frame on %s\n",chan->name);
218                                         LOCAL_USER_REMOVE(u);
219                                         return -1;
220                                 }
221                         } else
222                                 ast_frfree(f);
223                         continue;
224                 }
225                   /* if not DTMF, just do it again */
226                 if (f->frametype != AST_FRAME_DTMF) 
227                 {
228                         ast_frfree(f);
229                         continue;
230                 }
231
232                 j = f->subclass;  /* save digit */
233                 ast_frfree(f);
234                 
235                 gettimeofday(&lastdigittime,NULL);
236                   /* got a DTMF tone */
237                 if (i < AST_MAX_EXTENSION) /* if still valid number of digits */
238                 {
239                         if (!k) /* if in password state */
240                         {
241                                 if (j == '#') /* end of password */
242                                 {
243                                           /* see if this is an integer */
244                                         if (sscanf(tmp,"%d",&j) < 1)
245                                            { /* nope, it must be a filename */
246                                                 fp = fopen(tmp,"r");
247                                                 if (!fp)
248                                                    {
249                                                         ast_log(LOG_WARNING,"DISA password file %s not found on chan %s\n",tmp,chan->name);
250                                                         LOCAL_USER_REMOVE(u);
251                                                         return -1;
252                                                    }
253                                                 tmp[0] = 0;
254                                                 while(fgets(tmp,sizeof(tmp) - 1,fp))
255                                                    {
256                                                         char *stringp=NULL,*stringp2;
257                                                         if (!tmp[0]) continue;
258                                                         if (tmp[strlen(tmp) - 1] == '\n') 
259                                                                 tmp[strlen(tmp) - 1] = 0;
260                                                         if (!tmp[0]) continue;
261                                                           /* skip comments */
262                                                         if (tmp[0] == '#') continue;
263                                                         if (tmp[0] == ';') continue;
264                                                         stringp=tmp;
265                                                         strsep(&stringp, "|");
266                                                         stringp2=strsep(&stringp, "|");
267                                                         if (stringp2) {
268                                                                 ourcontext=stringp2;
269                                                                 stringp2=strsep(&stringp, "|");
270                                                                 if (stringp2) ourcallerid=stringp2;
271                                                         }
272                                                           /* password must be in valid format (numeric) */
273                                                         if (sscanf(tmp,"%d",&j) < 1) continue;
274                                                           /* if we got it */
275                                                         if (!strcmp(exten,tmp)) break;
276                                                    }
277                                                 fclose(fp);
278                                            }
279                                           /* compare the two */
280                                         if (strcmp(exten,tmp))
281                                         {
282                                                 ast_log(LOG_WARNING,"DISA on chan %s got bad password %s\n",chan->name,exten);
283                                                 goto reorder;
284
285                                         }
286                                          /* password good, set to dial state */
287                                                 ast_log(LOG_WARNING,"DISA on chan %s password is good\n",chan->name);
288                                         k = 1;
289                                         i = 0;  /* re-set buffer pointer */
290                                         exten[sizeof(acctcode)] = 0;
291                                         strcpy(acctcode,exten);
292                                         exten[0] = 0;
293                                         ast_log(LOG_DEBUG,"Successful DISA log-in on chan %s\n",chan->name);
294                                         continue;
295                                 }
296                         }
297                         exten[i++] = j;  /* save digit */
298                         exten[i] = 0;
299                         if (!k) continue; /* if getting password, continue doing it */
300                           /* if this exists */
301
302                           /* if can do some more, do it */
303                         if (!ast_matchmore_extension(chan,ourcontext,exten,1, chan->callerid)) 
304                                 break;
305                 }
306         }
307
308         if (k && ast_exists_extension(chan,ourcontext,exten,1, chan->callerid))
309         {
310                 /* We're authenticated and have a valid extension */
311                 if (ourcallerid && *ourcallerid)
312                 {
313                         if (chan->callerid) free(chan->callerid);
314                         chan->callerid = strdup(ourcallerid);
315                 }
316                 strcpy(chan->exten,exten);
317                 strcpy(chan->context,ourcontext);
318                 strcpy(chan->accountcode,acctcode);
319                 chan->priority = 0;
320                 ast_cdr_init(chan->cdr,chan);
321                 LOCAL_USER_REMOVE(u);
322                 return 0;
323         }
324
325 reorder:
326                 
327         /* something is invalid, give em reorder forever */
328         x = 0;
329         k = 0;  /* k = 0 means busy tone, k = 1 means silence) */
330         i = 0;  /* Number of samples we've done */
331         for(;;)
332         {
333                 if (ast_waitfor(chan, -1) < 0)
334                         break;
335                 f = ast_read(chan);
336                 if (!f)
337                         break;
338                 if (f->frametype == AST_FRAME_VOICE) {
339                         wf.frametype = AST_FRAME_VOICE;
340                         wf.subclass = AST_FORMAT_ULAW;
341                         wf.offset = AST_FRIENDLY_OFFSET;
342                         wf.mallocd = 0;
343                         wf.data = tone_block;
344                         wf.datalen = f->datalen;
345                         wf.samples = wf.datalen;
346                         if (k) 
347                                 memset(tone_block, 0x7f, wf.datalen);
348                         else
349                                 make_tone_block(tone_block,480.0, 620.0,wf.datalen, &x);
350                         i += wf.datalen / 8;
351                         if (i > 250) {
352                                 i = 0;
353                                 k = !k;
354                         }
355                     if (ast_write(chan, &wf)) 
356                         {
357                         ast_log(LOG_WARNING, "DISA Failed to write frame on %s\n",chan->name);
358                                 LOCAL_USER_REMOVE(u);
359                                 return -1;
360                         }
361                 }
362                 ast_frfree(f);
363         }
364         LOCAL_USER_REMOVE(u);
365         return -1;
366 }
367
368 int unload_module(void)
369 {
370         STANDARD_HANGUP_LOCALUSERS;
371         return ast_unregister_application(app);
372 }
373
374 int load_module(void)
375 {
376         return ast_register_application(app, disa_exec, synopsis, descrip);
377 }
378
379 char *description(void)
380 {
381         return tdesc;
382 }
383
384 int usecount(void)
385 {
386         int res;
387         STANDARD_USECOUNT(res);
388         return res;
389 }
390
391 char *key(void)
392 {
393         return ASTERISK_GPL_KEY;
394 }