Sat Aug 6 00:39:20 2011

Asterisk developer's documentation


app_disa.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  *
00007  * Made only slightly more sane by Mark Spencer <markster@digium.com>
00008  *
00009  * See http://www.asterisk.org for more information about
00010  * the Asterisk project. Please do not directly contact
00011  * any of the maintainers of this project for assistance;
00012  * the project provides a web site, mailing lists and IRC
00013  * channels for your use.
00014  *
00015  * This program is free software, distributed under the terms of
00016  * the GNU General Public License Version 2. See the LICENSE file
00017  * at the top of the source tree.
00018  */
00019 
00020 /*! \file
00021  *
00022  * \brief DISA -- Direct Inward System Access Application
00023  *
00024  * \author Jim Dixon <jim@lambdatel.com>
00025  * 
00026  * \ingroup applications
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          /* answer */
00177          ast_answer(chan);
00178       }
00179    } else special_noanswer = 1;
00180    i = k = x = 0; /* k is 0 for pswd entry, 1 for ext entry */
00181    did_ignore = 0;
00182    exten[0] = 0;
00183    acctcode[0] = 0;
00184    /* can we access DISA without password? */ 
00185 
00186    ast_log(LOG_DEBUG, "Context: %s\n",args.context);
00187 
00188    if (!strcasecmp(args.passcode, "no-password")) {
00189       k |= 1; /* We have the password */
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         /* if outa time, give em reorder */
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       /* if not DTMF, just do it again */
00226       if (f->frametype != AST_FRAME_DTMF) {
00227          ast_frfree(f);
00228          continue;
00229       }
00230 
00231       j = f->subclass;  /* save digit */
00232       ast_frfree(f);
00233       if (i == 0) {
00234          k|=2; /* We have the first digit */ 
00235          ast_playtones_stop(chan);
00236       }
00237       lastdigittime = ast_tvnow();
00238         /* got a DTMF tone */
00239       if (i < AST_MAX_EXTENSION) { /* if still valid number of digits */
00240          if (!(k&1)) { /* if in password state */
00241             if (j == '#') { /* end of password */
00242                  /* see if this is an integer */
00243                if (sscanf(args.passcode,"%30d",&j) < 1) { /* nope, it must be a filename */
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                       /* skip comments */
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                      /* password must be in valid format (numeric) */
00269                      if (sscanf(args.passcode,"%30d", &j) < 1)
00270                         continue;
00271                       /* if we got it */
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                /* compare the two */
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                 /* password good, set to dial state */
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; /* In number mode */
00293                i = 0;  /* re-set buffer pointer */
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 == '#') { /* end of extension .. maybe */
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                   /* Let the # be the part of, or the entire extension */
00306                } else {
00307                   break;
00308                }
00309             }
00310          }
00311 
00312          exten[i++] = j;  /* save digit */
00313          exten[i] = 0;
00314          if (!(k&1))
00315             continue; /* if getting password, continue doing it */
00316          /* if this exists */
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          /* if can do some more, do it */
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          /* We're authenticated and have a target extension */
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    /* Received invalid, but no "i" extension exists in the given context */
00364 
00365 reorder:
00366 
00367    ast_indicate(chan,AST_CONTROL_CONGESTION);
00368    /* something is invalid, give em reorder for several seconds */
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");

Generated on Sat Aug 6 00:39:20 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7