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.
22 * \brief DISA -- Direct Inward System Access Application
24 * \author Jim Dixon <jim@lambdatel.com>
26 * \ingroup applications
31 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
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"
65 "If you need to present a DISA dialtone without entering a password, simply\n"
66 "set <passcode> to \"no-password\".\n"
68 "Be aware that using this may compromise the security of your PBX.\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"
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"
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"
84 "<cid> specifies a new (different) callerid to be used for this call.\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"
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";
94 NOANSWER_FLAG
= (1 << 0),
95 POUND_TO_END_FLAG
= (1 << 1),
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");
109 ts
= ast_get_indication_tone(chan
->zone
, "dial");
111 ast_playtones_start(chan
, 0, ts
->data
, 0);
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]="";
124 char ourcidname
[256],ourcidnum
[256];
126 struct timeval lastdigittime
;
129 AST_DECLARE_APP_ARGS(args
,
130 AST_APP_ARG(passcode
);
131 AST_APP_ARG(context
);
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");
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
))
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
) {
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
);
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
);
187 if ((res
= ast_waitfor(chan
, -1) < 0)) {
188 ast_debug(1, "Waitfor returned %d\n", res
);
192 if (!(f
= ast_read(chan
))) {
193 ast_clear_flag(chan
, AST_FLAG_END_DTMF_ONLY
);
197 if ((f
->frametype
== AST_FRAME_CONTROL
) && (f
->subclass
== AST_CONTROL_HANGUP
)) {
199 chan
->hangupcause
= f
->seqno
;
201 ast_clear_flag(chan
, AST_FLAG_END_DTMF_ONLY
);
205 /* If the frame coming in is not DTMF, just drop it and continue */
206 if (f
->frametype
!= AST_FRAME_DTMF
) {
211 j
= f
->subclass
; /* save digit */
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");
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
);
234 while(fgets(pwline
,sizeof(pwline
) - 1,fp
)) {
237 if (pwline
[strlen(pwline
) - 1] == '\n')
238 pwline
[strlen(pwline
) - 1] = 0;
242 if (pwline
[0] == '#')
244 if (pwline
[0] == ';')
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)
255 if (!strcmp(exten
,args
.passcode
)) {
256 if (ast_strlen_zero(args
.context
))
257 args
.context
= "disa";
258 if (ast_strlen_zero(args
.mailbox
))
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
);
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
));
280 ast_debug(1,"Successful DISA log-in on chan %s\n", chan
->name
);
284 if (j
== '#') { /* end of extension */
289 exten
[i
++] = j
; /* save digit */
292 continue; /* if getting password, continue doing it */
295 /* user wants end of number, remove # */
296 if (ast_test_flag(&flags
, POUND_TO_END_FLAG
) && j
== '#') {
301 if (ast_ignore_pattern(args
.context
, exten
)) {
302 play_dialtone(chan
, "");
306 ast_playtones_stop(chan
);
310 /* if can do some more, do it */
311 if (!ast_matchmore_extension(chan
,args
.context
,exten
,1, chan
->cid
.cid_num
)) {
317 ast_clear_flag(chan
, AST_FLAG_END_DTMF_ONLY
);
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
);
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);
347 /* Received invalid, but no "i" extension exists in the given context */
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
);
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");