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$")
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 tone_zone_sound
*ts
= NULL
;
102 if(ast_app_has_voicemail(mailbox
, NULL
))
103 ts
= ast_get_indication_tone(chan
->zone
, "dialrecall");
105 ts
= ast_get_indication_tone(chan
->zone
, "dial");
107 ast_playtones_start(chan
, 0, ts
->data
, 0);
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]="";
120 char ourcidname
[256],ourcidnum
[256];
122 struct timeval lastdigittime
;
126 AST_DECLARE_APP_ARGS(args
,
127 AST_APP_ARG(passcode
);
128 AST_APP_ARG(context
);
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");
139 u
= ast_module_user_add(chan
);
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
);
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
);
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
))
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
) {
179 } else special_noanswer
= 1;
180 i
= k
= x
= 0; /* k is 0 for pswd entry, 1 for ext entry */
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
);
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
);
204 if ((res
= ast_waitfor(chan
, -1) < 0)) {
205 ast_log(LOG_DEBUG
, "Waitfor returned %d\n", res
);
211 ast_module_user_remove(u
);
214 if ((f
->frametype
== AST_FRAME_CONTROL
) &&
215 (f
->subclass
== AST_CONTROL_HANGUP
)) {
217 ast_module_user_remove(u
);
220 if (f
->frametype
== AST_FRAME_VOICE
) {
225 /* if not DTMF, just do it again */
226 if (f
->frametype
!= AST_FRAME_DTMF
) {
231 j
= f
->subclass
; /* save digit */
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");
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
);
251 while(fgets(pwline
,sizeof(pwline
) - 1,fp
)) {
254 if (pwline
[strlen(pwline
) - 1] == '\n')
255 pwline
[strlen(pwline
) - 1] = 0;
259 if (pwline
[0] == '#')
261 if (pwline
[0] == ';')
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)
272 if (!strcmp(exten
,args
.passcode
)) {
273 if (ast_strlen_zero(args
.context
))
274 args
.context
= "disa";
275 if (ast_strlen_zero(args
.mailbox
))
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
);
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
));
297 ast_log(LOG_DEBUG
,"Successful DISA log-in on chan %s\n", chan
->name
);
302 exten
[i
++] = j
; /* save digit */
305 continue; /* if getting password, continue doing it */
308 if (ast_ignore_pattern(args
.context
, exten
)) {
309 play_dialtone(chan
, "");
313 ast_playtones_stop(chan
);
317 /* if can do some more, do it */
318 if (!ast_matchmore_extension(chan
,args
.context
,exten
,1, chan
->cid
.cid_num
)) {
326 struct ast_flags flags
= { AST_CDR_FLAG_POSTED
};
328 if (!ast_exists_extension(chan
, args
.context
, exten
, 1, chan
->cid
.cid_num
)) {
329 pbx_builtin_setvar_helper(chan
, "INVALID_EXTEN", exten
);
334 if (!recheck
|| ast_exists_extension(chan
, args
.context
, exten
, 1, chan
->cid
.cid_num
)) {
335 ast_playtones_stop(chan
);
336 /* We're authenticated and have a target extension */
337 if (!ast_strlen_zero(args
.cid
)) {
338 ast_callerid_split(args
.cid
, ourcidname
, sizeof(ourcidname
), ourcidnum
, sizeof(ourcidnum
));
339 ast_set_callerid(chan
, ourcidnum
, ourcidname
, ourcidnum
);
342 if (!ast_strlen_zero(acctcode
))
343 ast_string_field_set(chan
, accountcode
, acctcode
);
345 if (special_noanswer
) flags
.flags
= 0;
346 ast_cdr_reset(chan
->cdr
, &flags
);
347 ast_explicit_goto(chan
, args
.context
, exten
, 1);
348 ast_module_user_remove(u
);
353 /* Received invalid, but no "i" extension exists in the given context */
357 ast_indicate(chan
,AST_CONTROL_CONGESTION
);
358 /* something is invalid, give em reorder for several seconds */
360 while(time(NULL
) < rstart
+ 10) {
361 if (ast_waitfor(chan
, -1) < 0)
368 ast_playtones_stop(chan
);
369 ast_module_user_remove(u
);
373 static int unload_module(void)
377 res
= ast_unregister_application(app
);
379 ast_module_user_hangup_all();
384 static int load_module(void)
386 return ast_register_application(app
, disa_exec
, synopsis
, descrip
);
389 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY
, "DISA (Direct Inward System Access) Application");