00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include "asterisk.h"
00030
00031 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
00032
00033 #include <string.h>
00034 #include <stdlib.h>
00035 #include <stdio.h>
00036 #include <math.h>
00037 #include <sys/time.h>
00038
00039 #include "asterisk/lock.h"
00040 #include "asterisk/file.h"
00041 #include "asterisk/logger.h"
00042 #include "asterisk/channel.h"
00043 #include "asterisk/app.h"
00044 #include "asterisk/indications.h"
00045 #include "asterisk/pbx.h"
00046 #include "asterisk/module.h"
00047 #include "asterisk/translate.h"
00048 #include "asterisk/ulaw.h"
00049 #include "asterisk/callerid.h"
00050 #include "asterisk/stringfields.h"
00051
00052 static char *app = "DISA";
00053
00054 static char *synopsis = "DISA (Direct Inward System Access)";
00055
00056 static char *descrip =
00057 "DISA(<numeric passcode>[|<context>]) or DISA(<filename>)\n"
00058 "The DISA, Direct Inward System Access, application allows someone from \n"
00059 "outside the telephone switch (PBX) to obtain an \"internal\" system \n"
00060 "dialtone and to place calls from it as if they were placing a call from \n"
00061 "within the switch.\n"
00062 "DISA plays a dialtone. The user enters their numeric passcode, followed by\n"
00063 "the pound sign (#). If the passcode is correct, the user is then given\n"
00064 "system dialtone on which a call may be placed. Obviously, this type\n"
00065 "of access has SERIOUS security implications, and GREAT care must be\n"
00066 "taken NOT to compromise your security.\n\n"
00067 "There is a possibility of accessing DISA without password. Simply\n"
00068 "exchange your password with \"no-password\".\n\n"
00069 " Example: exten => s,1,DISA(no-password|local)\n\n"
00070 "Be aware that using this compromises the security of your PBX.\n\n"
00071 "The arguments to this application (in extensions.conf) allow either\n"
00072 "specification of a single global passcode (that everyone uses), or\n"
00073 "individual passcodes contained in a file. It also allows specification\n"
00074 "of the context on which the user will be dialing. If no context is\n"
00075 "specified, the DISA application defaults the context to \"disa\".\n"
00076 "Presumably a normal system will have a special context set up\n"
00077 "for DISA use with some or a lot of restrictions. \n\n"
00078 "The file that contains the passcodes (if used) allows specification\n"
00079 "of either just a passcode (defaulting to the \"disa\" context, or\n"
00080 "passcode|context on each line of the file. The file may contain blank\n"
00081 "lines, or comments starting with \"#\" or \";\". In addition, the\n"
00082 "above arguments may have |new-callerid-string appended to them, to\n"
00083 "specify a new (different) callerid to be used for this call, for\n"
00084 "example: numeric-passcode|context|\"My Phone\" <(234) 123-4567> or \n"
00085 "full-pathname-of-passcode-file|\"My Phone\" <(234) 123-4567>. Last\n"
00086 "but not least, |mailbox[@context] may be appended, which will cause\n"
00087 "a stutter-dialtone (indication \"dialrecall\") to be used, if the\n"
00088 "specified mailbox contains any new messages, for example:\n"
00089 "numeric-passcode|context||1234 (w/a changing callerid). Note that\n"
00090 "in the case of specifying the numeric-passcode, the context must be\n"
00091 "specified if the callerid is specified also.\n\n"
00092 "If login is successful, the application looks up the dialed number in\n"
00093 "the specified (or default) context, and executes it if found.\n"
00094 "If the user enters an invalid extension and extension \"i\" (invalid) \n"
00095 "exists in the context, it will be used. Also, if you set the 5th argument\n"
00096 "to 'NOANSWER', the DISA application will not answer initially.\n";
00097
00098
00099 static void play_dialtone(struct ast_channel *chan, char *mailbox)
00100 {
00101 const struct tone_zone_sound *ts = NULL;
00102 if(ast_app_has_voicemail(mailbox, NULL))
00103 ts = ast_get_indication_tone(chan->zone, "dialrecall");
00104 else
00105 ts = ast_get_indication_tone(chan->zone, "dial");
00106 if (ts)
00107 ast_playtones_start(chan, 0, ts->data, 0);
00108 else
00109 ast_tonepair_start(chan, 350, 440, 0, 0);
00110 }
00111
00112 static int disa_exec(struct ast_channel *chan, void *data)
00113 {
00114 int i,j,k,x,did_ignore,special_noanswer;
00115 int firstdigittimeout = 20000;
00116 int digittimeout = 10000;
00117 struct ast_module_user *u;
00118 char *tmp, exten[AST_MAX_EXTENSION],acctcode[20]="";
00119 char pwline[256];
00120 char ourcidname[256],ourcidnum[256];
00121 struct ast_frame *f;
00122 struct timeval lastdigittime;
00123 int res;
00124 time_t rstart;
00125 FILE *fp;
00126 AST_DECLARE_APP_ARGS(args,
00127 AST_APP_ARG(passcode);
00128 AST_APP_ARG(context);
00129 AST_APP_ARG(cid);
00130 AST_APP_ARG(mailbox);
00131 AST_APP_ARG(noanswer);
00132 );
00133
00134 if (ast_strlen_zero(data)) {
00135 ast_log(LOG_WARNING, "DISA requires an argument (passcode/passcode file)\n");
00136 return -1;
00137 }
00138
00139 u = ast_module_user_add(chan);
00140
00141 if (chan->pbx) {
00142 firstdigittimeout = chan->pbx->rtimeout*1000;
00143 digittimeout = chan->pbx->dtimeout*1000;
00144 }
00145
00146 if (ast_set_write_format(chan,AST_FORMAT_ULAW)) {
00147 ast_log(LOG_WARNING, "Unable to set write format to Mu-law on %s\n", chan->name);
00148 ast_module_user_remove(u);
00149 return -1;
00150 }
00151 if (ast_set_read_format(chan,AST_FORMAT_ULAW)) {
00152 ast_log(LOG_WARNING, "Unable to set read format to Mu-law on %s\n", chan->name);
00153 ast_module_user_remove(u);
00154 return -1;
00155 }
00156
00157 ast_log(LOG_DEBUG, "Digittimeout: %d\n", digittimeout);
00158 ast_log(LOG_DEBUG, "Responsetimeout: %d\n", firstdigittimeout);
00159
00160 tmp = ast_strdupa(data);
00161
00162 AST_STANDARD_APP_ARGS(args, tmp);
00163
00164 if (ast_strlen_zero(args.context))
00165 args.context = "disa";
00166 if (ast_strlen_zero(args.mailbox))
00167 args.mailbox = "";
00168
00169 ast_log(LOG_DEBUG, "Mailbox: %s\n",args.mailbox);
00170
00171
00172 special_noanswer = 0;
00173 if ((!args.noanswer) || strcmp(args.noanswer,"NOANSWER"))
00174 {
00175 if (chan->_state != AST_STATE_UP) {
00176
00177 ast_answer(chan);
00178 }
00179 } else special_noanswer = 1;
00180 i = k = x = 0;
00181 did_ignore = 0;
00182 exten[0] = 0;
00183 acctcode[0] = 0;
00184
00185
00186 ast_log(LOG_DEBUG, "Context: %s\n",args.context);
00187
00188 if (!strcasecmp(args.passcode, "no-password")) {
00189 k |= 1;
00190 ast_log(LOG_DEBUG, "DISA no-password login success\n");
00191 }
00192 lastdigittime = ast_tvnow();
00193
00194 play_dialtone(chan, args.mailbox);
00195
00196 for (;;) {
00197
00198 if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) >
00199 ((k&2) ? digittimeout : firstdigittimeout)) {
00200 ast_log(LOG_DEBUG,"DISA %s entry timeout on chan %s\n",
00201 ((k&1) ? "extension" : "password"),chan->name);
00202 break;
00203 }
00204 if ((res = ast_waitfor(chan, -1) < 0)) {
00205 ast_log(LOG_DEBUG, "Waitfor returned %d\n", res);
00206 continue;
00207 }
00208
00209 f = ast_read(chan);
00210 if (f == NULL) {
00211 ast_module_user_remove(u);
00212 return -1;
00213 }
00214 if ((f->frametype == AST_FRAME_CONTROL) &&
00215 (f->subclass == AST_CONTROL_HANGUP)) {
00216 ast_frfree(f);
00217 ast_module_user_remove(u);
00218 return -1;
00219 }
00220 if (f->frametype == AST_FRAME_VOICE) {
00221 ast_frfree(f);
00222 continue;
00223 }
00224
00225
00226 if (f->frametype != AST_FRAME_DTMF) {
00227 ast_frfree(f);
00228 continue;
00229 }
00230
00231 j = f->subclass;
00232 ast_frfree(f);
00233 if (i == 0) {
00234 k|=2;
00235 ast_playtones_stop(chan);
00236 }
00237 lastdigittime = ast_tvnow();
00238
00239 if (i < AST_MAX_EXTENSION) {
00240 if (!(k&1)) {
00241 if (j == '#') {
00242
00243 if (sscanf(args.passcode,"%d",&j) < 1) {
00244 fp = fopen(args.passcode,"r");
00245 if (!fp) {
00246 ast_log(LOG_WARNING,"DISA password file %s not found on chan %s\n",args.passcode,chan->name);
00247 ast_module_user_remove(u);
00248 return -1;
00249 }
00250 pwline[0] = 0;
00251 while(fgets(pwline,sizeof(pwline) - 1,fp)) {
00252 if (!pwline[0])
00253 continue;
00254 if (pwline[strlen(pwline) - 1] == '\n')
00255 pwline[strlen(pwline) - 1] = 0;
00256 if (!pwline[0])
00257 continue;
00258
00259 if (pwline[0] == '#')
00260 continue;
00261 if (pwline[0] == ';')
00262 continue;
00263
00264 AST_STANDARD_APP_ARGS(args, pwline);
00265
00266 ast_log(LOG_DEBUG, "Mailbox: %s\n",args.mailbox);
00267
00268
00269 if (sscanf(args.passcode,"%d", &j) < 1)
00270 continue;
00271
00272 if (!strcmp(exten,args.passcode)) {
00273 if (ast_strlen_zero(args.context))
00274 args.context = "disa";
00275 if (ast_strlen_zero(args.mailbox))
00276 args.mailbox = "";
00277 break;
00278 }
00279 }
00280 fclose(fp);
00281 }
00282
00283 if (strcmp(exten,args.passcode)) {
00284 ast_log(LOG_WARNING,"DISA on chan %s got bad password %s\n",chan->name,exten);
00285 goto reorder;
00286
00287 }
00288
00289 ast_log(LOG_DEBUG,"DISA on chan %s password is good\n",chan->name);
00290 play_dialtone(chan, args.mailbox);
00291
00292 k|=1;
00293 i = 0;
00294 exten[sizeof(acctcode)] = 0;
00295 ast_copy_string(acctcode, exten, sizeof(acctcode));
00296 exten[0] = 0;
00297 ast_log(LOG_DEBUG,"Successful DISA log-in on chan %s\n", chan->name);
00298 continue;
00299 }
00300 } else {
00301 if (j == '#') {
00302 break;
00303 }
00304 }
00305
00306 exten[i++] = j;
00307 exten[i] = 0;
00308 if (!(k&1))
00309 continue;
00310
00311
00312 if (ast_ignore_pattern(args.context, exten)) {
00313 play_dialtone(chan, "");
00314 did_ignore = 1;
00315 } else
00316 if (did_ignore) {
00317 ast_playtones_stop(chan);
00318 did_ignore = 0;
00319 }
00320
00321
00322 if (!ast_matchmore_extension(chan,args.context,exten,1, chan->cid.cid_num)) {
00323 break;
00324 }
00325 }
00326 }
00327
00328 if (k == 3) {
00329 int recheck = 0;
00330 struct ast_flags flags = { AST_CDR_FLAG_POSTED };
00331
00332 if (!ast_exists_extension(chan, args.context, exten, 1, chan->cid.cid_num)) {
00333 pbx_builtin_setvar_helper(chan, "INVALID_EXTEN", exten);
00334 exten[0] = 'i';
00335 exten[1] = '\0';
00336 recheck = 1;
00337 }
00338 if (!recheck || ast_exists_extension(chan, args.context, exten, 1, chan->cid.cid_num)) {
00339 ast_playtones_stop(chan);
00340
00341 if (!ast_strlen_zero(args.cid)) {
00342 ast_callerid_split(args.cid, ourcidname, sizeof(ourcidname), ourcidnum, sizeof(ourcidnum));
00343 ast_set_callerid(chan, ourcidnum, ourcidname, ourcidnum);
00344 }
00345
00346 if (!ast_strlen_zero(acctcode))
00347 ast_string_field_set(chan, accountcode, acctcode);
00348
00349 if (special_noanswer) flags.flags = 0;
00350 ast_cdr_reset(chan->cdr, &flags);
00351 ast_explicit_goto(chan, args.context, exten, 1);
00352 ast_module_user_remove(u);
00353 return 0;
00354 }
00355 }
00356
00357
00358
00359 reorder:
00360
00361 ast_indicate(chan,AST_CONTROL_CONGESTION);
00362
00363 time(&rstart);
00364 while(time(NULL) < rstart + 10) {
00365 if (ast_waitfor(chan, -1) < 0)
00366 break;
00367 f = ast_read(chan);
00368 if (!f)
00369 break;
00370 ast_frfree(f);
00371 }
00372 ast_playtones_stop(chan);
00373 ast_module_user_remove(u);
00374 return -1;
00375 }
00376
00377 static int unload_module(void)
00378 {
00379 int res;
00380
00381 res = ast_unregister_application(app);
00382
00383 ast_module_user_hangup_all();
00384
00385 return res;
00386 }
00387
00388 static int load_module(void)
00389 {
00390 return ast_register_application(app, disa_exec, synopsis, descrip);
00391 }
00392
00393 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "DISA (Direct Inward System Access) Application");