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