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: 220288 $")
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,"%30d",&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,"%30d", &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 if (i == 0 &&
00303 (ast_matchmore_extension(chan, args.context, "#", 1, chan->cid.cid_num) ||
00304 ast_exists_extension(chan, args.context, "#", 1, chan->cid.cid_num)) ) {
00305
00306 } else {
00307 break;
00308 }
00309 }
00310 }
00311
00312 exten[i++] = j;
00313 exten[i] = 0;
00314 if (!(k&1))
00315 continue;
00316
00317
00318 if (ast_ignore_pattern(args.context, exten)) {
00319 play_dialtone(chan, "");
00320 did_ignore = 1;
00321 } else
00322 if (did_ignore) {
00323 ast_playtones_stop(chan);
00324 did_ignore = 0;
00325 }
00326
00327
00328 if (!ast_matchmore_extension(chan,args.context,exten,1, chan->cid.cid_num)) {
00329 break;
00330 }
00331 }
00332 }
00333
00334 if (k == 3) {
00335 int recheck = 0;
00336 struct ast_flags flags = { AST_CDR_FLAG_POSTED };
00337
00338 if (!ast_exists_extension(chan, args.context, exten, 1, chan->cid.cid_num)) {
00339 pbx_builtin_setvar_helper(chan, "INVALID_EXTEN", exten);
00340 exten[0] = 'i';
00341 exten[1] = '\0';
00342 recheck = 1;
00343 }
00344 if (!recheck || ast_exists_extension(chan, args.context, exten, 1, chan->cid.cid_num)) {
00345 ast_playtones_stop(chan);
00346
00347 if (!ast_strlen_zero(args.cid)) {
00348 ast_callerid_split(args.cid, ourcidname, sizeof(ourcidname), ourcidnum, sizeof(ourcidnum));
00349 ast_set_callerid(chan, ourcidnum, ourcidname, ourcidnum);
00350 }
00351
00352 if (!ast_strlen_zero(acctcode))
00353 ast_string_field_set(chan, accountcode, acctcode);
00354
00355 if (special_noanswer) flags.flags = 0;
00356 ast_cdr_reset(chan->cdr, &flags);
00357 ast_explicit_goto(chan, args.context, exten, 1);
00358 ast_module_user_remove(u);
00359 return 0;
00360 }
00361 }
00362
00363
00364
00365 reorder:
00366
00367 ast_indicate(chan,AST_CONTROL_CONGESTION);
00368
00369 time(&rstart);
00370 while(time(NULL) < rstart + 10) {
00371 if (ast_waitfor(chan, -1) < 0)
00372 break;
00373 f = ast_read(chan);
00374 if (!f)
00375 break;
00376 ast_frfree(f);
00377 }
00378 ast_playtones_stop(chan);
00379 ast_module_user_remove(u);
00380 return -1;
00381 }
00382
00383 static int unload_module(void)
00384 {
00385 int res;
00386
00387 res = ast_unregister_application(app);
00388
00389 ast_module_user_hangup_all();
00390
00391 return res;
00392 }
00393
00394 static int load_module(void)
00395 {
00396 return ast_register_application(app, disa_exec, synopsis, descrip);
00397 }
00398
00399 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "DISA (Direct Inward System Access) Application");