remove lots of useless 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
166         AST_STANDARD_APP_ARGS(args, tmp);
167
168         if (ast_strlen_zero(args.context)) 
169                 args.context = "disa";  
170         if (ast_strlen_zero(args.mailbox))
171                 args.mailbox = "";
172
173         ast_log(LOG_DEBUG, "Mailbox: %s\n",args.mailbox);
174         
175
176         special_noanswer = 0;
177         if ((!args.noanswer) || strcmp(args.noanswer,"NOANSWER"))
178         {
179                 if (chan->_state != AST_STATE_UP) {
180                         /* answer */
181                         ast_answer(chan);
182                 }
183         } else special_noanswer = 1;
184         i = k = x = 0; /* k is 0 for pswd entry, 1 for ext entry */
185         did_ignore = 0;
186         exten[0] = 0;
187         acctcode[0] = 0;
188         /* can we access DISA without password? */ 
189
190         ast_log(LOG_DEBUG, "Context: %s\n",args.context);
191
192         if (!strcasecmp(args.passcode, "no-password")) {
193                 k |= 1; /* We have the password */
194                 ast_log(LOG_DEBUG, "DISA no-password login success\n");
195         }
196         lastdigittime = ast_tvnow();
197
198         play_dialtone(chan, args.mailbox);
199
200         for (;;) {
201                   /* if outa time, give em reorder */
202                 if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > 
203                     ((k&2) ? digittimeout : firstdigittimeout)) {
204                         ast_log(LOG_DEBUG,"DISA %s entry timeout on chan %s\n",
205                                 ((k&1) ? "extension" : "password"),chan->name);
206                         break;
207                 }
208                 if ((res = ast_waitfor(chan, -1) < 0)) {
209                         ast_log(LOG_DEBUG, "Waitfor returned %d\n", res);
210                         continue;
211                 }
212                         
213                 f = ast_read(chan);
214                 if (f == NULL) {
215                         LOCAL_USER_REMOVE(u);
216                         return -1;
217                 }
218                 if ((f->frametype == AST_FRAME_CONTROL) &&
219                     (f->subclass == AST_CONTROL_HANGUP)) {
220                         ast_frfree(f);
221                         LOCAL_USER_REMOVE(u);
222                         return -1;
223                 }
224                 if (f->frametype == AST_FRAME_VOICE) {
225                         ast_frfree(f);
226                         continue;
227                 }
228
229                 /* if not DTMF, just do it again */
230                 if (f->frametype != AST_FRAME_DTMF) {
231                         ast_frfree(f);
232                         continue;
233                 }
234
235                 j = f->subclass;  /* save digit */
236                 ast_frfree(f);
237                 if (i == 0) {
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                         if (!(k&1)) { /* if in password state */
245                                 if (j == '#') { /* end of password */
246                                           /* see if this is an integer */
247                                         if (sscanf(args.passcode,"%d",&j) < 1) { /* nope, it must be a filename */
248                                                 fp = fopen(args.passcode,"r");
249                                                 if (!fp) {
250                                                         ast_log(LOG_WARNING,"DISA password file %s not found on chan %s\n",args.passcode,chan->name);
251                                                         LOCAL_USER_REMOVE(u);
252                                                         return -1;
253                                                 }
254                                                 pwline[0] = 0;
255                                                 while(fgets(pwline,sizeof(pwline) - 1,fp)) {
256                                                         if (!pwline[0])
257                                                                 continue;
258                                                         if (pwline[strlen(pwline) - 1] == '\n') 
259                                                                 pwline[strlen(pwline) - 1] = 0;
260                                                         if (!pwline[0])
261                                                                 continue;
262                                                          /* skip comments */
263                                                         if (pwline[0] == '#')
264                                                                 continue;
265                                                         if (pwline[0] == ';')
266                                                                 continue;
267
268                                                         AST_STANDARD_APP_ARGS(args, pwline);
269                         
270                                                         ast_log(LOG_DEBUG, "Mailbox: %s\n",args.mailbox);
271
272                                                         /* password must be in valid format (numeric) */
273                                                         if (sscanf(args.passcode,"%d", &j) < 1)
274                                                                 continue;
275                                                          /* if we got it */
276                                                         if (!strcmp(exten,args.passcode)) {
277                                                                 if (ast_strlen_zero(args.context))
278                                                                         args.context = "disa";
279                                                                 if (ast_strlen_zero(args.mailbox))
280                                                                         args.mailbox = "";
281                                                                 break;
282                                                         }
283                                                 }
284                                                 fclose(fp);
285                                         }
286                                         /* compare the two */
287                                         if (strcmp(exten,args.passcode)) {
288                                                 ast_log(LOG_WARNING,"DISA on chan %s got bad password %s\n",chan->name,exten);
289                                                 goto reorder;
290
291                                         }
292                                          /* password good, set to dial state */
293                                         ast_log(LOG_DEBUG,"DISA on chan %s password is good\n",chan->name);
294                                         play_dialtone(chan, args.mailbox);
295
296                                         k|=1; /* In number mode */
297                                         i = 0;  /* re-set buffer pointer */
298                                         exten[sizeof(acctcode)] = 0;
299                                         ast_copy_string(acctcode, exten, sizeof(acctcode));
300                                         exten[0] = 0;
301                                         ast_log(LOG_DEBUG,"Successful DISA log-in on chan %s\n", chan->name);
302                                         continue;
303                                 }
304                         }
305
306                         exten[i++] = j;  /* save digit */
307                         exten[i] = 0;
308                         if (!(k&1))
309                                 continue; /* if getting password, continue doing it */
310                         /* if this exists */
311
312                         if (ast_ignore_pattern(args.context, exten)) {
313                                 play_dialtone(chan, "");
314                                 did_ignore = 1;
315                         } else
316                                 if (did_ignore) {
317                                         ast_playtones_stop(chan);
318                                         did_ignore = 0;
319                                 }
320
321                         /* if can do some more, do it */
322                         if (!ast_matchmore_extension(chan,args.context,exten,1, chan->cid.cid_num)) {
323                                 break;
324                         }
325                 }
326         }
327
328         if (k == 3) {
329                 int recheck = 0;
330                 struct ast_flags flags = { AST_CDR_FLAG_POSTED };
331
332                 if (!ast_exists_extension(chan, args.context, exten, 1, chan->cid.cid_num)) {
333                         pbx_builtin_setvar_helper(chan, "INVALID_EXTEN", exten);
334                         exten[0] = 'i';
335                         exten[1] = '\0';
336                         recheck = 1;
337                 }
338                 if (!recheck || ast_exists_extension(chan, args.context, exten, 1, chan->cid.cid_num)) {
339                         ast_playtones_stop(chan);
340                         /* We're authenticated and have a target extension */
341                         if (!ast_strlen_zero(args.cid)) {
342                                 ast_callerid_split(args.cid, ourcidname, sizeof(ourcidname), ourcidnum, sizeof(ourcidnum));
343                                 ast_set_callerid(chan, ourcidnum, ourcidname, ourcidnum);
344                         }
345
346                         if (!ast_strlen_zero(acctcode))
347                                 ast_copy_string(chan->accountcode, acctcode, sizeof(chan->accountcode));
348
349                         if (special_noanswer) flags.flags = 0;
350                         ast_cdr_reset(chan->cdr, &flags);
351                         ast_explicit_goto(chan, args.context, exten, 1);
352                         LOCAL_USER_REMOVE(u);
353                         return 0;
354                 }
355         }
356
357         /* Received invalid, but no "i" extension exists in the given context */
358
359 reorder:
360
361         ast_indicate(chan,AST_CONTROL_CONGESTION);
362         /* something is invalid, give em reorder for several seconds */
363         time(&rstart);
364         while(time(NULL) < rstart + 10) {
365                 if (ast_waitfor(chan, -1) < 0)
366                         break;
367                 f = ast_read(chan);
368                 if (!f)
369                         break;
370                 ast_frfree(f);
371         }
372         ast_playtones_stop(chan);
373         LOCAL_USER_REMOVE(u);
374         return -1;
375 }
376
377 int unload_module(void)
378 {
379         int res;
380
381         res = ast_unregister_application(app);
382
383         STANDARD_HANGUP_LOCALUSERS;
384
385         return res;
386 }
387
388 int load_module(void)
389 {
390         return ast_register_application(app, disa_exec, synopsis, descrip);
391 }
392
393 char *description(void)
394 {
395         return tdesc;
396 }
397
398 int usecount(void)
399 {
400         int res;
401         STANDARD_USECOUNT(res);
402         return res;
403 }
404
405 char *key(void)
406 {
407         return ASTERISK_GPL_KEY;
408 }