Revert part of previous fix, and heavily comment the logic for object
[asterisk-bristuff.git] / apps / app_disa.c
blob53bcc2c9909ed2a6cc1f8350e02954510f61ea41
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 <math.h>
34 #include <sys/time.h>
36 #include "asterisk/lock.h"
37 #include "asterisk/file.h"
38 #include "asterisk/channel.h"
39 #include "asterisk/app.h"
40 #include "asterisk/indications.h"
41 #include "asterisk/pbx.h"
42 #include "asterisk/module.h"
43 #include "asterisk/translate.h"
44 #include "asterisk/ulaw.h"
45 #include "asterisk/callerid.h"
46 #include "asterisk/stringfields.h"
48 static char *app = "DISA";
50 static char *synopsis = "DISA (Direct Inward System Access)";
52 static char *descrip =
53 "DISA(<numeric passcode>[,<context>[,<cid>[,mailbox[,options]]]]) or\n"
54 "DISA(<filename>[,,,,options])\n"
55 "The DISA, Direct Inward System Access, application allows someone from \n"
56 "outside the telephone switch (PBX) to obtain an \"internal\" system \n"
57 "dialtone and to place calls from it as if they were placing a call from \n"
58 "within the switch.\n"
59 "DISA plays a dialtone. The user enters their numeric passcode, followed by\n"
60 "the pound sign (#). If the passcode is correct, the user is then given\n"
61 "system dialtone within <context> on which a call may be placed. If the user\n"
62 "enters an invalid extension and extension \"i\" exists in the specified\n"
63 "context, it will be used.\n"
64 "\n"
65 "If you need to present a DISA dialtone without entering a password, simply\n"
66 "set <passcode> to \"no-password\".\n"
67 "\n"
68 "Be aware that using this may compromise the security of your PBX.\n"
69 "\n"
70 "The arguments to this application (in extensions.conf) allow either\n"
71 "specification of a single global passcode (that everyone uses), or\n"
72 "individual passcodes contained in a file.\n"
73 "\n"
74 "The file that contains the passcodes (if used) allows a complete\n"
75 "specification of all of the same arguments available on the command\n"
76 "line, with the sole exception of the options. The file may contain blank\n"
77 "lines, or comments starting with \"#\" or \";\".\n"
78 "\n"
79 "<context> specifies the dialplan context in which the user-entered extension\n"
80 "will be matched. If no context is specified, the DISA application defaults\n"
81 "the context to \"disa\". Presumably a normal system will have a special\n"
82 "context set up for DISA use with some or a lot of restrictions.\n"
83 "\n"
84 "<cid> specifies a new (different) callerid to be used for this call.\n"
85 "\n"
86 "<mailbox[@context]> will cause a stutter-dialtone (indication \"dialrecall\")\n"
87 "to be used, if the specified mailbox contains any new messages.\n"
88 "\n"
89 "The following options are available:\n"
90 " n - the DISA application will not answer initially.\n"
91 " p - the extension entered will be considered complete when a '#' is entered.\n";
93 enum {
94 NOANSWER_FLAG = (1 << 0),
95 POUND_TO_END_FLAG = (1 << 1),
96 } option_flags;
98 AST_APP_OPTIONS(app_opts, {
99 AST_APP_OPTION('n', NOANSWER_FLAG),
100 AST_APP_OPTION('p', POUND_TO_END_FLAG),
103 static void play_dialtone(struct ast_channel *chan, char *mailbox)
105 const struct ind_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);
116 static int disa_exec(struct ast_channel *chan, void *data)
118 int i = 0, j, k = 0, did_ignore = 0, special_noanswer = 0;
119 int firstdigittimeout = (chan->pbx ? chan->pbx->rtimeoutms : 20000);
120 int digittimeout = (chan->pbx ? chan->pbx->dtimeoutms : 10000);
121 struct ast_flags flags;
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 FILE *fp;
129 AST_DECLARE_APP_ARGS(args,
130 AST_APP_ARG(passcode);
131 AST_APP_ARG(context);
132 AST_APP_ARG(cid);
133 AST_APP_ARG(mailbox);
134 AST_APP_ARG(options);
137 if (ast_strlen_zero(data)) {
138 ast_log(LOG_WARNING, "DISA requires an argument (passcode/passcode file)\n");
139 return -1;
142 ast_debug(1, "Digittimeout: %d\n", digittimeout);
143 ast_debug(1, "Responsetimeout: %d\n", firstdigittimeout);
145 tmp = ast_strdupa(data);
147 AST_STANDARD_APP_ARGS(args, tmp);
149 if (ast_strlen_zero(args.context))
150 args.context = "disa";
151 if (ast_strlen_zero(args.mailbox))
152 args.mailbox = "";
153 if (!ast_strlen_zero(args.options))
154 ast_app_parse_options(app_opts, &flags, NULL, args.options);
156 ast_debug(1, "Mailbox: %s\n",args.mailbox);
158 if (!ast_test_flag(&flags, NOANSWER_FLAG)) {
159 if (chan->_state != AST_STATE_UP) {
160 /* answer */
161 ast_answer(chan);
163 } else
164 special_noanswer = 1;
166 ast_debug(1, "Context: %s\n",args.context);
168 if (!strcasecmp(args.passcode, "no-password")) {
169 k |= 1; /* We have the password */
170 ast_debug(1, "DISA no-password login success\n");
173 lastdigittime = ast_tvnow();
175 play_dialtone(chan, args.mailbox);
177 ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
179 for (;;) {
180 /* if outa time, give em reorder */
181 if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > ((k&2) ? digittimeout : firstdigittimeout)) {
182 ast_debug(1,"DISA %s entry timeout on chan %s\n",
183 ((k&1) ? "extension" : "password"),chan->name);
184 break;
187 if ((res = ast_waitfor(chan, -1) < 0)) {
188 ast_debug(1, "Waitfor returned %d\n", res);
189 continue;
192 if (!(f = ast_read(chan))) {
193 ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
194 return -1;
197 if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
198 if (f->seqno)
199 chan->hangupcause = f->seqno;
200 ast_frfree(f);
201 ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
202 return -1;
205 /* If the frame coming in is not DTMF, just drop it and continue */
206 if (f->frametype != AST_FRAME_DTMF) {
207 ast_frfree(f);
208 continue;
211 j = f->subclass; /* save digit */
212 ast_frfree(f);
214 if (!i) {
215 k |= 2; /* We have the first digit */
216 ast_playtones_stop(chan);
219 lastdigittime = ast_tvnow();
221 /* got a DTMF tone */
222 if (i < AST_MAX_EXTENSION) { /* if still valid number of digits */
223 if (!(k&1)) { /* if in password state */
224 if (j == '#') { /* end of password */
225 /* see if this is an integer */
226 if (sscanf(args.passcode,"%d",&j) < 1) { /* nope, it must be a filename */
227 fp = fopen(args.passcode,"r");
228 if (!fp) {
229 ast_log(LOG_WARNING,"DISA password file %s not found on chan %s\n",args.passcode,chan->name);
230 ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
231 return -1;
233 pwline[0] = 0;
234 while(fgets(pwline,sizeof(pwline) - 1,fp)) {
235 if (!pwline[0])
236 continue;
237 if (pwline[strlen(pwline) - 1] == '\n')
238 pwline[strlen(pwline) - 1] = 0;
239 if (!pwline[0])
240 continue;
241 /* skip comments */
242 if (pwline[0] == '#')
243 continue;
244 if (pwline[0] == ';')
245 continue;
247 AST_STANDARD_APP_ARGS(args, pwline);
249 ast_debug(1, "Mailbox: %s\n",args.mailbox);
251 /* password must be in valid format (numeric) */
252 if (sscanf(args.passcode,"%d", &j) < 1)
253 continue;
254 /* if we got it */
255 if (!strcmp(exten,args.passcode)) {
256 if (ast_strlen_zero(args.context))
257 args.context = "disa";
258 if (ast_strlen_zero(args.mailbox))
259 args.mailbox = "";
260 break;
263 fclose(fp);
265 /* compare the two */
266 if (strcmp(exten,args.passcode)) {
267 ast_log(LOG_WARNING,"DISA on chan %s got bad password %s\n",chan->name,exten);
268 goto reorder;
271 /* password good, set to dial state */
272 ast_debug(1,"DISA on chan %s password is good\n",chan->name);
273 play_dialtone(chan, args.mailbox);
275 k|=1; /* In number mode */
276 i = 0; /* re-set buffer pointer */
277 exten[sizeof(acctcode)] = 0;
278 ast_copy_string(acctcode, exten, sizeof(acctcode));
279 exten[0] = 0;
280 ast_debug(1,"Successful DISA log-in on chan %s\n", chan->name);
281 continue;
283 } else {
284 if (j == '#') { /* end of extension */
285 break;
289 exten[i++] = j; /* save digit */
290 exten[i] = 0;
291 if (!(k&1))
292 continue; /* if getting password, continue doing it */
293 /* if this exists */
295 /* user wants end of number, remove # */
296 if (ast_test_flag(&flags, POUND_TO_END_FLAG) && j == '#') {
297 exten[--i] = 0;
298 break;
301 if (ast_ignore_pattern(args.context, exten)) {
302 play_dialtone(chan, "");
303 did_ignore = 1;
304 } else
305 if (did_ignore) {
306 ast_playtones_stop(chan);
307 did_ignore = 0;
310 /* if can do some more, do it */
311 if (!ast_matchmore_extension(chan,args.context,exten,1, chan->cid.cid_num)) {
312 break;
317 ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
319 if (k == 3) {
320 int recheck = 0;
321 struct ast_flags flags = { AST_CDR_FLAG_POSTED };
323 if (!ast_exists_extension(chan, args.context, exten, 1, chan->cid.cid_num)) {
324 pbx_builtin_setvar_helper(chan, "INVALID_EXTEN", exten);
325 exten[0] = 'i';
326 exten[1] = '\0';
327 recheck = 1;
329 if (!recheck || ast_exists_extension(chan, args.context, exten, 1, chan->cid.cid_num)) {
330 ast_playtones_stop(chan);
331 /* We're authenticated and have a target extension */
332 if (!ast_strlen_zero(args.cid)) {
333 ast_callerid_split(args.cid, ourcidname, sizeof(ourcidname), ourcidnum, sizeof(ourcidnum));
334 ast_set_callerid(chan, ourcidnum, ourcidname, ourcidnum);
337 if (!ast_strlen_zero(acctcode))
338 ast_string_field_set(chan, accountcode, acctcode);
340 if (special_noanswer) flags.flags = 0;
341 ast_cdr_reset(chan->cdr, &flags);
342 ast_explicit_goto(chan, args.context, exten, 1);
343 return 0;
347 /* Received invalid, but no "i" extension exists in the given context */
349 reorder:
350 /* Play congestion for a bit */
351 ast_indicate(chan, AST_CONTROL_CONGESTION);
352 ast_safe_sleep(chan, 10*1000);
354 ast_playtones_stop(chan);
356 return -1;
359 static int unload_module(void)
361 return ast_unregister_application(app);
364 static int load_module(void)
366 return ast_register_application(app, disa_exec, synopsis, descrip);
369 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "DISA (Direct Inward System Access) Application");