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
37 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"
51 static char *tdesc = "DISA (Direct Inward System Access) Application";
53 static char *app = "DISA";
55 static char *synopsis = "DISA (Direct Inward System Access)";
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.\n";
103 static void play_dialtone(struct ast_channel *chan, char *mailbox)
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");
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,j,k,x,did_ignore;
119 int firstdigittimeout = 20000;
120 int digittimeout = 10000;
122 char *tmp, arg2[256]="",exten[AST_MAX_EXTENSION],acctcode[20]="";
123 char *ourcontext,*ourcallerid,ourcidname[256],ourcidnum[256],*mailbox;
125 struct timeval lastdigittime;
131 if (ast_strlen_zero(data)) {
132 ast_log(LOG_WARNING, "disa requires an argument (passcode/passcode file)\n");
139 firstdigittimeout = chan->pbx->rtimeout*1000;
140 digittimeout = chan->pbx->dtimeout*1000;
143 if (ast_set_write_format(chan,AST_FORMAT_ULAW)) {
144 ast_log(LOG_WARNING, "Unable to set write format to Mu-law on %s\n",chan->name);
145 LOCAL_USER_REMOVE(u);
148 if (ast_set_read_format(chan,AST_FORMAT_ULAW)) {
149 ast_log(LOG_WARNING, "Unable to set read format to Mu-law on %s\n",chan->name);
150 LOCAL_USER_REMOVE(u);
154 ast_log(LOG_DEBUG, "Digittimeout: %d\n", digittimeout);
155 ast_log(LOG_DEBUG, "Responsetimeout: %d\n", firstdigittimeout);
157 tmp = ast_strdupa(data);
159 ast_log(LOG_ERROR, "Out of memory\n");
160 LOCAL_USER_REMOVE(u);
165 strsep(&stringp, "|");
166 ourcontext = strsep(&stringp, "|");
167 /* if context specified, save 2nd arg and parse third */
169 ast_copy_string(arg2, ourcontext, sizeof(arg2));
170 ourcallerid = strsep(&stringp,"|");
172 /* if context not specified, use "disa" */
178 mailbox = strsep(&stringp, "|");
181 ast_log(LOG_DEBUG, "Mailbox: %s\n",mailbox);
183 if (chan->_state != AST_STATE_UP) {
187 i = k = x = 0; /* k is 0 for pswd entry, 1 for ext entry */
191 /* can we access DISA without password? */
193 ast_log(LOG_DEBUG, "Context: %s\n",ourcontext);
195 if (!strcasecmp(tmp, "no-password")) {
196 k |= 1; /* We have the password */
197 ast_log(LOG_DEBUG, "DISA no-password login success\n");
199 lastdigittime = ast_tvnow();
201 play_dialtone(chan, mailbox);
204 /* if outa time, give em reorder */
205 if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) >
206 ((k&2) ? digittimeout : firstdigittimeout))
208 ast_log(LOG_DEBUG,"DISA %s entry timeout on chan %s\n",
209 ((k&1) ? "extension" : "password"),chan->name);
212 if ((res = ast_waitfor(chan, -1) < 0)) {
213 ast_log(LOG_DEBUG, "Waitfor returned %d\n", res);
220 LOCAL_USER_REMOVE(u);
223 if ((f->frametype == AST_FRAME_CONTROL) &&
224 (f->subclass == AST_CONTROL_HANGUP))
227 LOCAL_USER_REMOVE(u);
230 if (f->frametype == AST_FRAME_VOICE) {
234 /* if not DTMF, just do it again */
235 if (f->frametype != AST_FRAME_DTMF)
241 j = f->subclass; /* save digit */
245 k|=2; /* We have the first digit */
246 ast_playtones_stop(chan);
248 lastdigittime = ast_tvnow();
249 /* got a DTMF tone */
250 if (i < AST_MAX_EXTENSION) /* if still valid number of digits */
252 if (!(k&1)) /* if in password state */
254 if (j == '#') /* end of password */
256 /* see if this is an integer */
257 if (sscanf(tmp,"%d",&j) < 1)
258 { /* nope, it must be a filename */
262 ast_log(LOG_WARNING,"DISA password file %s not found on chan %s\n",tmp,chan->name);
263 LOCAL_USER_REMOVE(u);
267 while(fgets(tmp,sizeof(tmp) - 1,fp))
269 char *stringp=NULL,*stringp2;
270 if (!tmp[0]) continue;
271 if (tmp[strlen(tmp) - 1] == '\n')
272 tmp[strlen(tmp) - 1] = 0;
273 if (!tmp[0]) continue;
275 if (tmp[0] == '#') continue;
276 if (tmp[0] == ';') continue;
278 strsep(&stringp, "|");
279 stringp2=strsep(&stringp, "|");
282 stringp2=strsep(&stringp, "|");
283 if (stringp2) ourcallerid=stringp2;
285 mailbox = strsep(&stringp, "|");
288 ast_log(LOG_DEBUG, "Mailbox: %s\n",mailbox);
290 /* password must be in valid format (numeric) */
291 if (sscanf(tmp,"%d",&j) < 1) continue;
293 if (!strcmp(exten,tmp)) break;
297 /* compare the two */
298 if (strcmp(exten,tmp))
300 ast_log(LOG_WARNING,"DISA on chan %s got bad password %s\n",chan->name,exten);
304 /* password good, set to dial state */
305 ast_log(LOG_DEBUG,"DISA on chan %s password is good\n",chan->name);
306 play_dialtone(chan, mailbox);
308 k|=1; /* In number mode */
309 i = 0; /* re-set buffer pointer */
310 exten[sizeof(acctcode)] = 0;
311 ast_copy_string(acctcode, exten, sizeof(acctcode));
313 ast_log(LOG_DEBUG,"Successful DISA log-in on chan %s\n",chan->name);
318 exten[i++] = j; /* save digit */
320 if (!(k&1)) continue; /* if getting password, continue doing it */
323 if (ast_ignore_pattern(ourcontext, exten)) {
324 play_dialtone(chan, "");
328 ast_playtones_stop(chan);
332 /* if can do some more, do it */
333 if (!ast_matchmore_extension(chan,ourcontext,exten,1, chan->cid.cid_num)) {
342 if (!ast_exists_extension(chan, ourcontext, exten, 1, chan->cid.cid_num)) {
343 pbx_builtin_setvar_helper(chan, "INVALID_EXTEN", exten);
348 if (!recheck || ast_exists_extension(chan, ourcontext, exten, 1, chan->cid.cid_num)) {
349 ast_playtones_stop(chan);
350 /* We're authenticated and have a target extension */
351 if (ourcallerid && *ourcallerid)
353 ast_callerid_split(ourcallerid, ourcidname, sizeof(ourcidname), ourcidnum, sizeof(ourcidnum));
354 ast_set_callerid(chan, ourcidnum, ourcidname, ourcidnum);
357 if (!ast_strlen_zero(acctcode))
358 ast_copy_string(chan->accountcode, acctcode, sizeof(chan->accountcode));
360 ast_cdr_reset(chan->cdr, AST_CDR_FLAG_POSTED);
361 ast_explicit_goto(chan, ourcontext, exten, 1);
362 LOCAL_USER_REMOVE(u);
367 /* Received invalid, but no "i" extension exists in the given context */
371 ast_indicate(chan,AST_CONTROL_CONGESTION);
372 /* something is invalid, give em reorder for several seconds */
374 while(time(NULL) < rstart + 10)
376 if (ast_waitfor(chan, -1) < 0)
383 ast_playtones_stop(chan);
384 LOCAL_USER_REMOVE(u);
388 int unload_module(void)
392 res = ast_unregister_application(app);
394 STANDARD_HANGUP_LOCALUSERS;
399 int load_module(void)
401 return ast_register_application(app, disa_exec, synopsis, descrip);
404 char *description(void)
412 STANDARD_USECOUNT(res);
418 return ASTERISK_GPL_KEY;