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