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: 220291 $")
00032
00033 #include <math.h>
00034 #include <sys/time.h>
00035
00036 #include "asterisk/lock.h"
00037 #include "asterisk/file.h"
00038 #include "asterisk/channel.h"
00039 #include "asterisk/app.h"
00040 #include "asterisk/indications.h"
00041 #include "asterisk/pbx.h"
00042 #include "asterisk/module.h"
00043 #include "asterisk/translate.h"
00044 #include "asterisk/ulaw.h"
00045 #include "asterisk/callerid.h"
00046 #include "asterisk/stringfields.h"
00047
00048 static char *app = "DISA";
00049
00050 static char *synopsis = "DISA (Direct Inward System Access)";
00051
00052 static char *descrip =
00053 "DISA(<numeric passcode>[,<context>[,<cid>[,mailbox[,options]]]]) or\n"
00054 "DISA(<filename>[,,,,options])\n"
00055 "The DISA, Direct Inward System Access, application allows someone from \n"
00056 "outside the telephone switch (PBX) to obtain an \"internal\" system \n"
00057 "dialtone and to place calls from it as if they were placing a call from \n"
00058 "within the switch.\n"
00059 "DISA plays a dialtone. The user enters their numeric passcode, followed by\n"
00060 "the pound sign (#). If the passcode is correct, the user is then given\n"
00061 "system dialtone within <context> on which a call may be placed. If the user\n"
00062 "enters an invalid extension and extension \"i\" exists in the specified\n"
00063 "context, it will be used.\n"
00064 "\n"
00065 "If you need to present a DISA dialtone without entering a password, simply\n"
00066 "set <passcode> to \"no-password\".\n"
00067 "\n"
00068 "Be aware that using this may compromise the security of your PBX.\n"
00069 "\n"
00070 "The arguments to this application (in extensions.conf) allow either\n"
00071 "specification of a single global passcode (that everyone uses), or\n"
00072 "individual passcodes contained in a file.\n"
00073 "\n"
00074 "The file that contains the passcodes (if used) allows a complete\n"
00075 "specification of all of the same arguments available on the command\n"
00076 "line, with the sole exception of the options. The file may contain blank\n"
00077 "lines, or comments starting with \"#\" or \";\".\n"
00078 "\n"
00079 "<context> specifies the dialplan context in which the user-entered extension\n"
00080 "will be matched. If no context is specified, the DISA application defaults\n"
00081 "the context to \"disa\". Presumably a normal system will have a special\n"
00082 "context set up for DISA use with some or a lot of restrictions.\n"
00083 "\n"
00084 "<cid> specifies a new (different) callerid to be used for this call.\n"
00085 "\n"
00086 "<mailbox[@context]> will cause a stutter-dialtone (indication \"dialrecall\")\n"
00087 "to be used, if the specified mailbox contains any new messages.\n"
00088 "\n"
00089 "The following options are available:\n"
00090 " n - the DISA application will not answer initially.\n"
00091 " p - the extension entered will be considered complete when a '#' is entered.\n";
00092
00093 enum {
00094 NOANSWER_FLAG = (1 << 0),
00095 POUND_TO_END_FLAG = (1 << 1),
00096 } option_flags;
00097
00098 AST_APP_OPTIONS(app_opts, {
00099 AST_APP_OPTION('n', NOANSWER_FLAG),
00100 AST_APP_OPTION('p', POUND_TO_END_FLAG),
00101 });
00102
00103 static void play_dialtone(struct ast_channel *chan, char *mailbox)
00104 {
00105 const struct tone_zone_sound *ts = NULL;
00106 if(ast_app_has_voicemail(mailbox, NULL))
00107 ts = ast_get_indication_tone(chan->zone, "dialrecall");
00108 else
00109 ts = ast_get_indication_tone(chan->zone, "dial");
00110 if (ts)
00111 ast_playtones_start(chan, 0, ts->data, 0);
00112 else
00113 ast_tonepair_start(chan, 350, 440, 0, 0);
00114 }
00115
00116 static int disa_exec(struct ast_channel *chan, void *data)
00117 {
00118 int i = 0, j, k = 0, did_ignore = 0, special_noanswer = 0;
00119 int firstdigittimeout = (chan->pbx ? chan->pbx->rtimeoutms : 20000);
00120 int digittimeout = (chan->pbx ? chan->pbx->dtimeoutms : 10000);
00121 struct ast_flags flags;
00122 char *tmp, exten[AST_MAX_EXTENSION] = "", acctcode[20]="";
00123 char pwline[256];
00124 char ourcidname[256],ourcidnum[256];
00125 struct ast_frame *f;
00126 struct timeval lastdigittime;
00127 int res;
00128 FILE *fp;
00129 AST_DECLARE_APP_ARGS(args,
00130 AST_APP_ARG(passcode);
00131 AST_APP_ARG(context);
00132 AST_APP_ARG(cid);
00133 AST_APP_ARG(mailbox);
00134 AST_APP_ARG(options);
00135 );
00136
00137 if (ast_strlen_zero(data)) {
00138 ast_log(LOG_WARNING, "DISA requires an argument (passcode/passcode file)\n");
00139 return -1;
00140 }
00141
00142 ast_debug(1, "Digittimeout: %d\n", digittimeout);
00143 ast_debug(1, "Responsetimeout: %d\n", firstdigittimeout);
00144
00145 tmp = ast_strdupa(data);
00146
00147 AST_STANDARD_APP_ARGS(args, tmp);
00148
00149 if (ast_strlen_zero(args.context))
00150 args.context = "disa";
00151 if (ast_strlen_zero(args.mailbox))
00152 args.mailbox = "";
00153 if (!ast_strlen_zero(args.options))
00154 ast_app_parse_options(app_opts, &flags, NULL, args.options);
00155
00156 ast_debug(1, "Mailbox: %s\n",args.mailbox);
00157
00158 if (!ast_test_flag(&flags, NOANSWER_FLAG)) {
00159 if (chan->_state != AST_STATE_UP) {
00160
00161 ast_answer(chan);
00162 }
00163 } else special_noanswer = 1;
00164
00165 ast_debug(1, "Context: %s\n",args.context);
00166
00167 if (!strcasecmp(args.passcode, "no-password")) {
00168 k |= 1;
00169 ast_debug(1, "DISA no-password login success\n");
00170 }
00171
00172 lastdigittime = ast_tvnow();
00173
00174 play_dialtone(chan, args.mailbox);
00175
00176 ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
00177
00178 for (;;) {
00179
00180 if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > ((k&2) ? digittimeout : firstdigittimeout)) {
00181 ast_debug(1,"DISA %s entry timeout on chan %s\n",
00182 ((k&1) ? "extension" : "password"),chan->name);
00183 break;
00184 }
00185
00186 if ((res = ast_waitfor(chan, -1) < 0)) {
00187 ast_debug(1, "Waitfor returned %d\n", res);
00188 continue;
00189 }
00190
00191 if (!(f = ast_read(chan))) {
00192 ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
00193 return -1;
00194 }
00195
00196 if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
00197 if (f->data.uint32)
00198 chan->hangupcause = f->data.uint32;
00199 ast_frfree(f);
00200 ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
00201 return -1;
00202 }
00203
00204
00205 if (f->frametype != AST_FRAME_DTMF) {
00206 ast_frfree(f);
00207 continue;
00208 }
00209
00210 j = f->subclass;
00211 ast_frfree(f);
00212
00213 if (!i) {
00214 k |= 2;
00215 ast_playtones_stop(chan);
00216 }
00217
00218 lastdigittime = ast_tvnow();
00219
00220
00221 if (i < AST_MAX_EXTENSION) {
00222 if (!(k&1)) {
00223 if (j == '#') {
00224
00225 if (sscanf(args.passcode,"%30d",&j) < 1) {
00226 fp = fopen(args.passcode,"r");
00227 if (!fp) {
00228 ast_log(LOG_WARNING,"DISA password file %s not found on chan %s\n",args.passcode,chan->name);
00229 ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
00230 return -1;
00231 }
00232 pwline[0] = 0;
00233 while(fgets(pwline,sizeof(pwline) - 1,fp)) {
00234 if (!pwline[0])
00235 continue;
00236 if (pwline[strlen(pwline) - 1] == '\n')
00237 pwline[strlen(pwline) - 1] = 0;
00238 if (!pwline[0])
00239 continue;
00240
00241 if (pwline[0] == '#')
00242 continue;
00243 if (pwline[0] == ';')
00244 continue;
00245
00246 AST_STANDARD_APP_ARGS(args, pwline);
00247
00248 ast_debug(1, "Mailbox: %s\n",args.mailbox);
00249
00250
00251 if (sscanf(args.passcode,"%30d", &j) < 1)
00252 continue;
00253
00254 if (!strcmp(exten,args.passcode)) {
00255 if (ast_strlen_zero(args.context))
00256 args.context = "disa";
00257 if (ast_strlen_zero(args.mailbox))
00258 args.mailbox = "";
00259 break;
00260 }
00261 }
00262 fclose(fp);
00263 }
00264
00265 if (strcmp(exten,args.passcode)) {
00266 ast_log(LOG_WARNING,"DISA on chan %s got bad password %s\n",chan->name,exten);
00267 goto reorder;
00268
00269 }
00270
00271 ast_debug(1,"DISA on chan %s password is good\n",chan->name);
00272 play_dialtone(chan, args.mailbox);
00273
00274 k|=1;
00275 i = 0;
00276 exten[sizeof(acctcode)] = 0;
00277 ast_copy_string(acctcode, exten, sizeof(acctcode));
00278 exten[0] = 0;
00279 ast_debug(1,"Successful DISA log-in on chan %s\n", chan->name);
00280 continue;
00281 }
00282 } else {
00283 if (j == '#') {
00284 if (i == 0 &&
00285 (ast_matchmore_extension(chan, args.context, "#", 1, chan->cid.cid_num) ||
00286 ast_exists_extension(chan, args.context, "#", 1, chan->cid.cid_num)) ) {
00287
00288 } else {
00289 break;
00290 }
00291 }
00292 }
00293
00294 exten[i++] = j;
00295 exten[i] = 0;
00296 if (!(k&1))
00297 continue;
00298
00299
00300
00301 if (ast_test_flag(&flags, POUND_TO_END_FLAG) && j == '#') {
00302 exten[--i] = 0;
00303 break;
00304 }
00305
00306 if (ast_ignore_pattern(args.context, exten)) {
00307 play_dialtone(chan, "");
00308 did_ignore = 1;
00309 } else
00310 if (did_ignore) {
00311 ast_playtones_stop(chan);
00312 did_ignore = 0;
00313 }
00314
00315
00316 if (!ast_matchmore_extension(chan,args.context,exten,1, chan->cid.cid_num)) {
00317 break;
00318 }
00319 }
00320 }
00321
00322 ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
00323
00324 if (k == 3) {
00325 int recheck = 0;
00326 struct ast_flags cdr_flags = { AST_CDR_FLAG_POSTED };
00327
00328 if (!ast_exists_extension(chan, args.context, exten, 1, chan->cid.cid_num)) {
00329 pbx_builtin_setvar_helper(chan, "INVALID_EXTEN", exten);
00330 exten[0] = 'i';
00331 exten[1] = '\0';
00332 recheck = 1;
00333 }
00334 if (!recheck || ast_exists_extension(chan, args.context, exten, 1, chan->cid.cid_num)) {
00335 ast_playtones_stop(chan);
00336
00337 if (!ast_strlen_zero(args.cid)) {
00338 ast_callerid_split(args.cid, ourcidname, sizeof(ourcidname), ourcidnum, sizeof(ourcidnum));
00339 ast_set_callerid(chan, ourcidnum, ourcidname, ourcidnum);
00340 }
00341
00342 if (!ast_strlen_zero(acctcode))
00343 ast_string_field_set(chan, accountcode, acctcode);
00344
00345 if (special_noanswer) cdr_flags.flags = 0;
00346 ast_cdr_reset(chan->cdr, &cdr_flags);
00347 ast_explicit_goto(chan, args.context, exten, 1);
00348 return 0;
00349 }
00350 }
00351
00352
00353
00354 reorder:
00355
00356 ast_indicate(chan, AST_CONTROL_CONGESTION);
00357 ast_safe_sleep(chan, 10*1000);
00358
00359 ast_playtones_stop(chan);
00360
00361 return -1;
00362 }
00363
00364 static int unload_module(void)
00365 {
00366 return ast_unregister_application(app);
00367 }
00368
00369 static int load_module(void)
00370 {
00371 return ast_register_application(app, disa_exec, synopsis, descrip) ?
00372 AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS;
00373 }
00374
00375 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "DISA (Direct Inward System Access) Application");