don't force CLID to be a phone-number-looking-thingie (issue #5325)
[asterisk/asterisk.git] / apps / app_disa.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Jim Dixon <jim@lambdatel.com>
7  *
8  * Made only slightly more sane by Mark Spencer <markster@digium.com>
9  *
10  * See http://www.asterisk.org for more information about
11  * the Asterisk project. Please do not directly contact
12  * any of the maintainers of this project for assistance;
13  * the project provides a web site, mailing lists and IRC
14  * channels for your use.
15  *
16  * This program is free software, distributed under the terms of
17  * the GNU General Public License Version 2. See the LICENSE file
18  * at the top of the source tree.
19  */
20
21 /*
22  *
23  * DISA -- Direct Inward System Access Application  6/20/2001
24  * 
25  */
26  
27 #include <string.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <math.h>
31 #include <sys/time.h>
32
33 #include "asterisk.h"
34
35 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
36
37 #include "asterisk/lock.h"
38 #include "asterisk/file.h"
39 #include "asterisk/logger.h"
40 #include "asterisk/channel.h"
41 #include "asterisk/app.h"
42 #include "asterisk/indications.h"
43 #include "asterisk/pbx.h"
44 #include "asterisk/module.h"
45 #include "asterisk/translate.h"
46 #include "asterisk/ulaw.h"
47 #include "asterisk/callerid.h"
48
49 static char *tdesc = "DISA (Direct Inward System Access) Application";
50
51 static char *app = "DISA";
52
53 static char *synopsis = "DISA (Direct Inward System Access)";
54
55 static char *descrip = 
56         "DISA (Direct Inward System Access) -- Allows someone from outside\n"
57         "the telephone switch (PBX) to obtain an \"internal\" system dialtone\n"
58         "and to place calls from it as if they were placing a call from within\n"
59         "the switch. A user calls a number that connects to the DISA application\n"
60         "and is given dialtone. The user enters their passcode, followed by the\n"
61         "pound sign (#). If the passcode is correct, the user is then given\n"
62         "system dialtone on which a call may be placed. Obviously, this type\n"
63         "of access has SERIOUS security implications, and GREAT care must be\n"
64         "taken NOT to compromise your security.\n\n"
65         "There is a possibility of accessing DISA without password. Simply\n"
66         "exchange your password with no-password.\n\n"
67         "  Example: exten => s,1,DISA,no-password|local\n\n"
68         "but be aware of using this for your security compromising.\n\n"
69         "The arguments to this application (in extensions.conf) allow either\n"
70         "specification of a single global password (that everyone uses), or\n"
71         "individual passwords contained in a file. It also allow specification\n"
72         "of the context on which the user will be dialing. If no context is\n"
73         "specified, the DISA application defaults the context to \"disa\"\n"
74         "presumably that a normal system will have a special context set up\n"
75         "for DISA use with some or a lot of restrictions. The arguments are\n"
76         "one of the following:\n\n"
77         "    numeric-passcode\n"
78         "    numeric-passcode|context\n"
79         "    full-pathname-of-file-that-contains-passcodes\n\n"
80         "The file that contains the passcodes (if used) allows specification\n"
81         "of either just a passcode (defaulting to the \"disa\" context, or\n"
82         "passcode|context on each line of the file. The file may contain blank\n"
83         "lines, or comments starting with \"#\" or \";\". In addition, the\n"
84         "above arguments may have |new-callerid-string appended to them, to\n"
85         "specify a new (different) callerid to be used for this call, for\n"
86         "example: numeric-passcode|context|\"My Phone\" <(234) 123-4567> or \n"
87         "full-pathname-of-passcode-file|\"My Phone\" <(234) 123-4567>.  Last\n"
88         "but not least, |mailbox[@context] may be appended, which will cause\n"
89         "a stutter-dialtone (indication \"dialrecall\") to be used, if the\n"
90         "specified mailbox contains any new messages, for example:\n"
91         "numeric-passcode|context||1234 (w/a changing callerid).  Note that\n"
92         "in the case of specifying the numeric-passcode, the context must be\n"
93         "specified if the callerid is specified also.\n\n"
94         "If login is successful, the application parses the dialed number in\n"
95         "the specified (or default) context, and returns 0 with the new extension\n"
96         "context filled-in and the priority set to 1, so that the PBX may\n"
97         "re-apply the routing tables to it and complete the call normally.";
98
99
100 STANDARD_LOCAL_USER;
101
102 LOCAL_USER_DECL;
103
104 static void play_dialtone(struct ast_channel *chan, char *mailbox)
105 {
106         const struct tone_zone_sound *ts = NULL;
107         if(ast_app_has_voicemail(mailbox, NULL))
108                 ts = ast_get_indication_tone(chan->zone, "dialrecall");
109         else
110                 ts = ast_get_indication_tone(chan->zone, "dial");
111         if (ts)
112                 ast_playtones_start(chan, 0, ts->data, 0);
113         else
114                 ast_tonepair_start(chan, 350, 440, 0, 0);
115 }
116
117 static int disa_exec(struct ast_channel *chan, void *data)
118 {
119         int i,j,k,x,did_ignore;
120         int firstdigittimeout = 20000;
121         int digittimeout = 10000;
122         struct localuser *u;
123         char tmp[256],arg2[256]="",exten[AST_MAX_EXTENSION],acctcode[20]="";
124         char *ourcontext,*ourcallerid,ourcidname[256],ourcidnum[256],*mailbox;
125         struct ast_frame *f;
126         struct timeval lastdigittime;
127         int res;
128         time_t rstart;
129         FILE *fp;
130         char *stringp=NULL;
131
132         if (chan->pbx) {
133                 firstdigittimeout = chan->pbx->rtimeout*1000;
134                 digittimeout = chan->pbx->dtimeout*1000;
135         }
136         
137         if (ast_set_write_format(chan,AST_FORMAT_ULAW))
138         {
139                 ast_log(LOG_WARNING, "Unable to set write format to Mu-law on %s\n",chan->name);
140                 return -1;
141         }
142         if (ast_set_read_format(chan,AST_FORMAT_ULAW))
143         {
144                 ast_log(LOG_WARNING, "Unable to set read format to Mu-law on %s\n",chan->name);
145                 return -1;
146         }
147         if (!data || !strlen((char *)data)) {
148                 ast_log(LOG_WARNING, "disa requires an argument (passcode/passcode file)\n");
149                 return -1;
150         }
151         ast_log(LOG_DEBUG, "Digittimeout: %d\n", digittimeout);
152         ast_log(LOG_DEBUG, "Responsetimeout: %d\n", firstdigittimeout);
153         strncpy(tmp, (char *)data, sizeof(tmp)-1);
154         stringp=tmp;
155         strsep(&stringp, "|");
156         ourcontext = strsep(&stringp, "|");
157         /* if context specified, save 2nd arg and parse third */
158         if (ourcontext) {
159                 strncpy(arg2,ourcontext, sizeof(arg2) - 1);
160                 ourcallerid = strsep(&stringp,"|");
161         }
162           /* if context not specified, use "disa" */
163         else {
164                 arg2[0] = 0;
165                 ourcallerid = NULL;
166                 ourcontext = "disa";
167         }
168         mailbox = strsep(&stringp, "|");
169         if (!mailbox)
170                 mailbox = "";
171         ast_log(LOG_DEBUG, "Mailbox: %s\n",mailbox);
172         LOCAL_USER_ADD(u);
173         if (chan->_state != AST_STATE_UP)
174         {
175                 /* answer */
176                 ast_answer(chan);
177         }
178         i = k = x = 0; /* k is 0 for pswd entry, 1 for ext entry */
179         did_ignore = 0;
180         exten[0] = 0;
181         acctcode[0] = 0;
182         /* can we access DISA without password? */ 
183
184         ast_log(LOG_DEBUG, "Context: %s\n",ourcontext);
185
186         if (!strcasecmp(tmp, "no-password"))
187         {;
188                 k |= 1; /* We have the password */
189                 ast_log(LOG_DEBUG, "DISA no-password login success\n");
190         }
191         lastdigittime = ast_tvnow();
192
193         play_dialtone(chan, mailbox);
194
195         for(;;)
196         {
197                   /* if outa time, give em reorder */
198                 if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > 
199                     ((k&2) ? digittimeout : firstdigittimeout))
200                 {
201                         ast_log(LOG_DEBUG,"DISA %s entry timeout on chan %s\n",
202                                 ((k&1) ? "extension" : "password"),chan->name);
203                         break;
204                 }
205                 if ((res = ast_waitfor(chan, -1) < 0)) {
206                         ast_log(LOG_DEBUG, "Waitfor returned %d\n", res);
207                         continue;
208                 }
209                         
210                 f = ast_read(chan);
211                 if (f == NULL) 
212                 {
213                         LOCAL_USER_REMOVE(u);
214                         return -1;
215                 }
216                 if ((f->frametype == AST_FRAME_CONTROL) &&
217                     (f->subclass == AST_CONTROL_HANGUP))
218                 {
219                         ast_frfree(f);
220                         LOCAL_USER_REMOVE(u);
221                         return -1;
222                 }
223                 if (f->frametype == AST_FRAME_VOICE) {
224                         ast_frfree(f);
225                         continue;
226                 }
227                   /* if not DTMF, just do it again */
228                 if (f->frametype != AST_FRAME_DTMF) 
229                 {
230                         ast_frfree(f);
231                         continue;
232                 }
233
234                 j = f->subclass;  /* save digit */
235                 ast_frfree(f);
236                 if (i == 0) 
237                 {
238                         k|=2; /* We have the first digit */ 
239                         ast_playtones_stop(chan);
240                 }
241                 lastdigittime = ast_tvnow();
242                   /* got a DTMF tone */
243                 if (i < AST_MAX_EXTENSION) /* if still valid number of digits */
244                 {
245                         if (!(k&1)) /* if in password state */
246                         {
247                                 if (j == '#') /* end of password */
248                                 {
249                                           /* see if this is an integer */
250                                         if (sscanf(tmp,"%d",&j) < 1)
251                                            { /* nope, it must be a filename */
252                                                 fp = fopen(tmp,"r");
253                                                 if (!fp)
254                                                    {
255                                                         ast_log(LOG_WARNING,"DISA password file %s not found on chan %s\n",tmp,chan->name);
256                                                         LOCAL_USER_REMOVE(u);
257                                                         return -1;
258                                                    }
259                                                 tmp[0] = 0;
260                                                 while(fgets(tmp,sizeof(tmp) - 1,fp))
261                                                    {
262                                                         char *stringp=NULL,*stringp2;
263                                                         if (!tmp[0]) continue;
264                                                         if (tmp[strlen(tmp) - 1] == '\n') 
265                                                                 tmp[strlen(tmp) - 1] = 0;
266                                                         if (!tmp[0]) continue;
267                                                           /* skip comments */
268                                                         if (tmp[0] == '#') continue;
269                                                         if (tmp[0] == ';') continue;
270                                                         stringp=tmp;
271                                                         strsep(&stringp, "|");
272                                                         stringp2=strsep(&stringp, "|");
273                                                         if (stringp2) {
274                                                                 ourcontext=stringp2;
275                                                                 stringp2=strsep(&stringp, "|");
276                                                                 if (stringp2) ourcallerid=stringp2;
277                                                         }
278                                                         mailbox = strsep(&stringp, "|");
279                                                         if (!mailbox)
280                                                                 mailbox = "";
281                                                         ast_log(LOG_DEBUG, "Mailbox: %s\n",mailbox);
282
283                                                           /* password must be in valid format (numeric) */
284                                                         if (sscanf(tmp,"%d",&j) < 1) continue;
285                                                           /* if we got it */
286                                                         if (!strcmp(exten,tmp)) break;
287                                                    }
288                                                 fclose(fp);
289                                            }
290                                           /* compare the two */
291                                         if (strcmp(exten,tmp))
292                                         {
293                                                 ast_log(LOG_WARNING,"DISA on chan %s got bad password %s\n",chan->name,exten);
294                                                 goto reorder;
295
296                                         }
297                                          /* password good, set to dial state */
298                                         ast_log(LOG_DEBUG,"DISA on chan %s password is good\n",chan->name);
299                                         play_dialtone(chan, mailbox);
300
301                                         k|=1; /* In number mode */
302                                         i = 0;  /* re-set buffer pointer */
303                                         exten[sizeof(acctcode)] = 0;
304                                         strncpy(acctcode,exten, sizeof(acctcode) - 1);
305                                         exten[0] = 0;
306                                         ast_log(LOG_DEBUG,"Successful DISA log-in on chan %s\n",chan->name);
307                                         continue;
308                                 }
309                         }
310
311                         exten[i++] = j;  /* save digit */
312                         exten[i] = 0;
313                         if (!(k&1)) continue; /* if getting password, continue doing it */
314                           /* if this exists */
315
316                         if (ast_ignore_pattern(ourcontext, exten)) {
317                                 play_dialtone(chan, "");
318                                 did_ignore = 1;
319                         } else
320                                 if (did_ignore) {
321                                         ast_playtones_stop(chan);
322                                         did_ignore = 0;
323                                 }
324
325                           /* if can do some more, do it */
326                         if (!ast_matchmore_extension(chan,ourcontext,exten,1, chan->cid.cid_num)) {
327                                 break;
328                         }
329                 }
330         }
331
332         if (k==3 && ast_exists_extension(chan, ourcontext, exten, 1, chan->cid.cid_num))
333         {
334                 ast_playtones_stop(chan);
335                 /* We're authenticated and have a valid extension */
336                 if (ourcallerid && *ourcallerid)
337                 {
338                         ast_callerid_split(ourcallerid, ourcidname, sizeof(ourcidname), ourcidnum, sizeof(ourcidnum));
339                         ast_set_callerid(chan, ourcidnum, ourcidname, ourcidnum);
340                 }
341
342                 if (!ast_strlen_zero(acctcode)) {
343                         strncpy(chan->accountcode, acctcode, sizeof(chan->accountcode) - 1);
344                 }
345                 ast_cdr_reset(chan->cdr, AST_CDR_FLAG_POSTED);
346                 ast_goto_if_exists(chan, ourcontext, exten, 1);
347                 LOCAL_USER_REMOVE(u);
348                 return 0;
349         }
350
351 reorder:
352
353         ast_indicate(chan,AST_CONTROL_CONGESTION);
354         /* something is invalid, give em reorder for several seconds */
355         time(&rstart);
356         while(time(NULL) < rstart + 10)
357         {
358                 if (ast_waitfor(chan, -1) < 0)
359                         break;
360                 f = ast_read(chan);
361                 if (!f)
362                         break;
363                 ast_frfree(f);
364         }
365         ast_playtones_stop(chan);
366         LOCAL_USER_REMOVE(u);
367         return -1;
368 }
369
370 int unload_module(void)
371 {
372         STANDARD_HANGUP_LOCALUSERS;
373         return ast_unregister_application(app);
374 }
375
376 int load_module(void)
377 {
378         return ast_register_application(app, disa_exec, synopsis, descrip);
379 }
380
381 char *description(void)
382 {
383         return tdesc;
384 }
385
386 int usecount(void)
387 {
388         int res;
389         STANDARD_USECOUNT(res);
390         return res;
391 }
392
393 char *key(void)
394 {
395         return ASTERISK_GPL_KEY;
396 }