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: 220290 $")
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->rtimeout * 1000 : 20000);
00120 int digittimeout = (chan->pbx ? chan->pbx->dtimeout * 1000 : 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 ast_frfree(f);
00198 ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
00199 return -1;
00200 }
00201
00202
00203 if (f->frametype != AST_FRAME_DTMF) {
00204 ast_frfree(f);
00205 continue;
00206 }
00207
00208 j = f->subclass;
00209 ast_frfree(f);
00210
00211 if (!i) {
00212 k |= 2;
00213 ast_playtones_stop(chan);
00214 }
00215
00216 lastdigittime = ast_tvnow();
00217
00218
00219 if (i < AST_MAX_EXTENSION) {
00220 if (!(k&1)) {
00221 if (j == '#') {
00222
00223 if (sscanf(args.passcode,"%30d",&j) < 1) {
00224 fp = fopen(args.passcode,"r");
00225 if (!fp) {
00226 ast_log(LOG_WARNING,"DISA password file %s not found on chan %s\n",args.passcode,chan->name);
00227 ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
00228 return -1;
00229 }
00230 pwline[0] = 0;
00231 while(fgets(pwline,sizeof(pwline) - 1,fp)) {
00232 if (!pwline[0])
00233 continue;
00234 if (pwline[strlen(pwline) - 1] == '\n')
00235 pwline[strlen(pwline) - 1] = 0;
00236 if (!pwline[0])
00237 continue;
00238
00239 if (pwline[0] == '#')
00240 continue;
00241 if (pwline[0] == ';')
00242 continue;
00243
00244 AST_STANDARD_APP_ARGS(args, pwline);
00245
00246 ast_debug(1, "Mailbox: %s\n",args.mailbox);
00247
00248
00249 if (sscanf(args.passcode,"%30d", &j) < 1)
00250 continue;
00251
00252 if (!strcmp(exten,args.passcode)) {
00253 if (ast_strlen_zero(args.context))
00254 args.context = "disa";
00255 if (ast_strlen_zero(args.mailbox))
00256 args.mailbox = "";
00257 break;
00258 }
00259 }
00260 fclose(fp);
00261 }
00262
00263 if (strcmp(exten,args.passcode)) {
00264 ast_log(LOG_WARNING,"DISA on chan %s got bad password %s\n",chan->name,exten);
00265 goto reorder;
00266
00267 }
00268
00269 ast_debug(1,"DISA on chan %s password is good\n",chan->name);
00270 play_dialtone(chan, args.mailbox);
00271
00272 k|=1;
00273 i = 0;
00274 exten[sizeof(acctcode)] = 0;
00275 ast_copy_string(acctcode, exten, sizeof(acctcode));
00276 exten[0] = 0;
00277 ast_debug(1,"Successful DISA log-in on chan %s\n", chan->name);
00278 continue;
00279 }
00280 } else {
00281 if (j == '#') {
00282 if (i == 0 &&
00283 (ast_matchmore_extension(chan, args.context, "#", 1, chan->cid.cid_num) ||
00284 ast_exists_extension(chan, args.context, "#", 1, chan->cid.cid_num)) ) {
00285
00286 } else {
00287 break;
00288 }
00289 }
00290 }
00291
00292 exten[i++] = j;
00293 exten[i] = 0;
00294 if (!(k&1))
00295 continue;
00296
00297
00298
00299 if (ast_test_flag(&flags, POUND_TO_END_FLAG) && j == '#') {
00300 exten[--i] = 0;
00301 break;
00302 }
00303
00304 if (ast_ignore_pattern(args.context, exten)) {
00305 play_dialtone(chan, "");
00306 did_ignore = 1;
00307 } else
00308 if (did_ignore) {
00309 ast_playtones_stop(chan);
00310 did_ignore = 0;
00311 }
00312
00313
00314 if (!ast_matchmore_extension(chan,args.context,exten,1, chan->cid.cid_num)) {
00315 break;
00316 }
00317 }
00318 }
00319
00320 ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
00321
00322 if (k == 3) {
00323 int recheck = 0;
00324 struct ast_flags flags = { AST_CDR_FLAG_POSTED };
00325
00326 if (!ast_exists_extension(chan, args.context, exten, 1, chan->cid.cid_num)) {
00327 pbx_builtin_setvar_helper(chan, "INVALID_EXTEN", exten);
00328 exten[0] = 'i';
00329 exten[1] = '\0';
00330 recheck = 1;
00331 }
00332 if (!recheck || ast_exists_extension(chan, args.context, exten, 1, chan->cid.cid_num)) {
00333 ast_playtones_stop(chan);
00334
00335 if (!ast_strlen_zero(args.cid)) {
00336 ast_callerid_split(args.cid, ourcidname, sizeof(ourcidname), ourcidnum, sizeof(ourcidnum));
00337 ast_set_callerid(chan, ourcidnum, ourcidname, ourcidnum);
00338 }
00339
00340 if (!ast_strlen_zero(acctcode))
00341 ast_string_field_set(chan, accountcode, acctcode);
00342
00343 if (special_noanswer) flags.flags = 0;
00344 ast_cdr_reset(chan->cdr, &flags);
00345 ast_explicit_goto(chan, args.context, exten, 1);
00346 return 0;
00347 }
00348 }
00349
00350
00351
00352 reorder:
00353
00354 ast_indicate(chan, AST_CONTROL_CONGESTION);
00355 ast_safe_sleep(chan, 10*1000);
00356
00357 ast_playtones_stop(chan);
00358
00359 return -1;
00360 }
00361
00362 static int unload_module(void)
00363 {
00364 return ast_unregister_application(app);
00365 }
00366
00367 static int load_module(void)
00368 {
00369 return ast_register_application(app, disa_exec, synopsis, descrip);
00370 }
00371
00372 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "DISA (Direct Inward System Access) Application");