(closes issue #12846)
[asterisk-bristuff.git] / apps / app_disa.c
blobf0c9aced7f40a37997be9eacfa15096c37ae704b
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
7 * Made only slightly more sane by Mark Spencer <markster@digium.com>
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.
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.
20 /*! \file
22 * \brief DISA -- Direct Inward System Access Application
24 * \author Jim Dixon <jim@lambdatel.com>
26 * \ingroup applications
29 #include "asterisk.h"
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
33 #include <string.h>
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <math.h>
37 #include <sys/time.h>
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 #include "asterisk/stringfields.h"
52 static char *app = "DISA";
54 static char *synopsis = "DISA (Direct Inward System Access)";
56 static char *descrip =
57 "DISA(<numeric passcode>[|<context>]) or DISA(<filename>)\n"
58 "The DISA, Direct Inward System Access, application allows someone from \n"
59 "outside the telephone switch (PBX) to obtain an \"internal\" system \n"
60 "dialtone and to place calls from it as if they were placing a call from \n"
61 "within the switch.\n"
62 "DISA plays a dialtone. The user enters their numeric passcode, followed by\n"
63 "the pound sign (#). If the passcode is correct, the user is then given\n"
64 "system dialtone on which a call may be placed. Obviously, this type\n"
65 "of access has SERIOUS security implications, and GREAT care must be\n"
66 "taken NOT to compromise your security.\n\n"
67 "There is a possibility of accessing DISA without password. Simply\n"
68 "exchange your password with \"no-password\".\n\n"
69 " Example: exten => s,1,DISA(no-password|local)\n\n"
70 "Be aware that using this compromises the security of your PBX.\n\n"
71 "The arguments to this application (in extensions.conf) allow either\n"
72 "specification of a single global passcode (that everyone uses), or\n"
73 "individual passcodes contained in a file. It also allows specification\n"
74 "of the context on which the user will be dialing. If no context is\n"
75 "specified, the DISA application defaults the context to \"disa\".\n"
76 "Presumably a normal system will have a special context set up\n"
77 "for DISA use with some or a lot of restrictions. \n\n"
78 "The file that contains the passcodes (if used) allows specification\n"
79 "of either just a passcode (defaulting to the \"disa\" context, or\n"
80 "passcode|context on each line of the file. The file may contain blank\n"
81 "lines, or comments starting with \"#\" or \";\". In addition, the\n"
82 "above arguments may have |new-callerid-string appended to them, to\n"
83 "specify a new (different) callerid to be used for this call, for\n"
84 "example: numeric-passcode|context|\"My Phone\" <(234) 123-4567> or \n"
85 "full-pathname-of-passcode-file|\"My Phone\" <(234) 123-4567>. Last\n"
86 "but not least, |mailbox[@context] may be appended, which will cause\n"
87 "a stutter-dialtone (indication \"dialrecall\") to be used, if the\n"
88 "specified mailbox contains any new messages, for example:\n"
89 "numeric-passcode|context||1234 (w/a changing callerid). Note that\n"
90 "in the case of specifying the numeric-passcode, the context must be\n"
91 "specified if the callerid is specified also.\n\n"
92 "If login is successful, the application looks up the dialed number in\n"
93 "the specified (or default) context, and executes it if found.\n"
94 "If the user enters an invalid extension and extension \"i\" (invalid) \n"
95 "exists in the context, it will be used. Also, if you set the 5th argument\n"
96 "to 'NOANSWER', the DISA application will not answer initially.\n";
99 static void play_dialtone(struct ast_channel *chan, char *mailbox)
101 const struct ind_tone_zone_sound *ts = NULL;
102 if(ast_app_has_voicemail(mailbox, NULL))
103 ts = ast_get_indication_tone(chan->zone, "dialrecall");
104 else
105 ts = ast_get_indication_tone(chan->zone, "dial");
106 if (ts)
107 ast_playtones_start(chan, 0, ts->data, 0);
108 else
109 ast_tonepair_start(chan, 350, 440, 0, 0);
112 static int disa_exec(struct ast_channel *chan, void *data)
114 int i,j,k,x,did_ignore,special_noanswer;
115 int firstdigittimeout = 20000;
116 int digittimeout = 10000;
117 struct ast_module_user *u;
118 char *tmp, exten[AST_MAX_EXTENSION],acctcode[20]="";
119 char pwline[256];
120 char ourcidname[256],ourcidnum[256];
121 struct ast_frame *f;
122 struct timeval lastdigittime;
123 int res;
124 time_t rstart;
125 FILE *fp;
126 AST_DECLARE_APP_ARGS(args,
127 AST_APP_ARG(passcode);
128 AST_APP_ARG(context);
129 AST_APP_ARG(cid);
130 AST_APP_ARG(mailbox);
131 AST_APP_ARG(noanswer);
134 if (ast_strlen_zero(data)) {
135 ast_log(LOG_WARNING, "DISA requires an argument (passcode/passcode file)\n");
136 return -1;
139 u = ast_module_user_add(chan);
141 if (chan->pbx) {
142 firstdigittimeout = chan->pbx->rtimeout*1000;
143 digittimeout = chan->pbx->dtimeout*1000;
146 if (ast_set_write_format(chan,AST_FORMAT_ULAW)) {
147 ast_log(LOG_WARNING, "Unable to set write format to Mu-law on %s\n", chan->name);
148 ast_module_user_remove(u);
149 return -1;
151 if (ast_set_read_format(chan,AST_FORMAT_ULAW)) {
152 ast_log(LOG_WARNING, "Unable to set read format to Mu-law on %s\n", chan->name);
153 ast_module_user_remove(u);
154 return -1;
157 ast_log(LOG_DEBUG, "Digittimeout: %d\n", digittimeout);
158 ast_log(LOG_DEBUG, "Responsetimeout: %d\n", firstdigittimeout);
160 tmp = ast_strdupa(data);
162 AST_STANDARD_APP_ARGS(args, tmp);
164 if (ast_strlen_zero(args.context))
165 args.context = "disa";
166 if (ast_strlen_zero(args.mailbox))
167 args.mailbox = "";
169 ast_log(LOG_DEBUG, "Mailbox: %s\n",args.mailbox);
172 special_noanswer = 0;
173 if ((!args.noanswer) || strcmp(args.noanswer,"NOANSWER"))
175 if (chan->_state != AST_STATE_UP) {
176 /* answer */
177 ast_answer(chan);
179 } else special_noanswer = 1;
180 i = k = x = 0; /* k is 0 for pswd entry, 1 for ext entry */
181 did_ignore = 0;
182 exten[0] = 0;
183 acctcode[0] = 0;
184 /* can we access DISA without password? */
186 ast_log(LOG_DEBUG, "Context: %s\n",args.context);
188 if (!strcasecmp(args.passcode, "no-password")) {
189 k |= 1; /* We have the password */
190 ast_log(LOG_DEBUG, "DISA no-password login success\n");
192 lastdigittime = ast_tvnow();
194 play_dialtone(chan, args.mailbox);
196 for (;;) {
197 /* if outa time, give em reorder */
198 if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) >
199 ((k&2) ? digittimeout : firstdigittimeout)) {
200 ast_log(LOG_DEBUG,"DISA %s entry timeout on chan %s\n",
201 ((k&1) ? "extension" : "password"),chan->name);
202 break;
204 if ((res = ast_waitfor(chan, -1) < 0)) {
205 ast_log(LOG_DEBUG, "Waitfor returned %d\n", res);
206 continue;
209 f = ast_read(chan);
210 if (f == NULL) {
211 ast_module_user_remove(u);
212 return -1;
214 if ((f->frametype == AST_FRAME_CONTROL) &&
215 (f->subclass == AST_CONTROL_HANGUP)) {
216 ast_frfree(f);
217 ast_module_user_remove(u);
218 return -1;
220 if (f->frametype == AST_FRAME_VOICE) {
221 ast_frfree(f);
222 continue;
225 /* if not DTMF, just do it again */
226 if (f->frametype != AST_FRAME_DTMF) {
227 ast_frfree(f);
228 continue;
231 j = f->subclass; /* save digit */
232 ast_frfree(f);
233 if (i == 0) {
234 k|=2; /* We have the first digit */
235 ast_playtones_stop(chan);
237 lastdigittime = ast_tvnow();
238 /* got a DTMF tone */
239 if (i < AST_MAX_EXTENSION) { /* if still valid number of digits */
240 if (!(k&1)) { /* if in password state */
241 if (j == '#') { /* end of password */
242 /* see if this is an integer */
243 if (sscanf(args.passcode,"%d",&j) < 1) { /* nope, it must be a filename */
244 fp = fopen(args.passcode,"r");
245 if (!fp) {
246 ast_log(LOG_WARNING,"DISA password file %s not found on chan %s\n",args.passcode,chan->name);
247 ast_module_user_remove(u);
248 return -1;
250 pwline[0] = 0;
251 while(fgets(pwline,sizeof(pwline) - 1,fp)) {
252 if (!pwline[0])
253 continue;
254 if (pwline[strlen(pwline) - 1] == '\n')
255 pwline[strlen(pwline) - 1] = 0;
256 if (!pwline[0])
257 continue;
258 /* skip comments */
259 if (pwline[0] == '#')
260 continue;
261 if (pwline[0] == ';')
262 continue;
264 AST_STANDARD_APP_ARGS(args, pwline);
266 ast_log(LOG_DEBUG, "Mailbox: %s\n",args.mailbox);
268 /* password must be in valid format (numeric) */
269 if (sscanf(args.passcode,"%d", &j) < 1)
270 continue;
271 /* if we got it */
272 if (!strcmp(exten,args.passcode)) {
273 if (ast_strlen_zero(args.context))
274 args.context = "disa";
275 if (ast_strlen_zero(args.mailbox))
276 args.mailbox = "";
277 break;
280 fclose(fp);
282 /* compare the two */
283 if (strcmp(exten,args.passcode)) {
284 ast_log(LOG_WARNING,"DISA on chan %s got bad password %s\n",chan->name,exten);
285 goto reorder;
288 /* password good, set to dial state */
289 ast_log(LOG_DEBUG,"DISA on chan %s password is good\n",chan->name);
290 play_dialtone(chan, args.mailbox);
292 k|=1; /* In number mode */
293 i = 0; /* re-set buffer pointer */
294 exten[sizeof(acctcode)] = 0;
295 ast_copy_string(acctcode, exten, sizeof(acctcode));
296 exten[0] = 0;
297 ast_log(LOG_DEBUG,"Successful DISA log-in on chan %s\n", chan->name);
298 continue;
300 } else {
301 if (j == '#') { /* end of extension */
302 break;
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 */
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;
321 /* if can do some more, do it */
322 if (!ast_matchmore_extension(chan,args.context,exten,1, chan->cid.cid_num)) {
323 break;
328 if (k == 3) {
329 int recheck = 0;
330 struct ast_flags flags = { AST_CDR_FLAG_POSTED };
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;
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);
346 if (!ast_strlen_zero(acctcode))
347 ast_string_field_set(chan, accountcode, acctcode);
349 if (special_noanswer) flags.flags = 0;
350 ast_cdr_reset(chan->cdr, &flags);
351 ast_explicit_goto(chan, args.context, exten, 1);
352 ast_module_user_remove(u);
353 return 0;
357 /* Received invalid, but no "i" extension exists in the given context */
359 reorder:
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);
372 ast_playtones_stop(chan);
373 ast_module_user_remove(u);
374 return -1;
377 static int unload_module(void)
379 int res;
381 res = ast_unregister_application(app);
383 ast_module_user_hangup_all();
385 return res;
388 static int load_module(void)
390 return ast_register_application(app, disa_exec, synopsis, descrip);
393 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "DISA (Direct Inward System Access) Application");