Sat Sep 16 07:28:05 2006

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 <string.h>
00030 #include <stdlib.h>
00031 #include <stdio.h>
00032 #include <math.h>
00033 #include <sys/time.h>
00034 
00035 #include "asterisk.h"
00036 
00037 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 7221 $")
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 
00051 static char *tdesc = "DISA (Direct Inward System Access) Application";
00052 
00053 static char *app = "DISA";
00054 
00055 static char *synopsis = "DISA (Direct Inward System Access)";
00056 
00057 static char *descrip = 
00058    "DISA(<numeric passcode>[|<context>]) or disa(<filename>)\n"
00059    "The DISA, Direct Inward System Access, application allows someone from \n"
00060    "outside the telephone switch (PBX) to obtain an \"internal\" system \n"
00061    "dialtone and to place calls from it as if they were placing a call from \n"
00062    "within the switch.\n"
00063    "DISA plays a dialtone. The user enters their numeric passcode, followed by\n"
00064    "the pound sign (#). If the passcode is correct, the user is then given\n"
00065    "system dialtone on which a call may be placed. Obviously, this type\n"
00066    "of access has SERIOUS security implications, and GREAT care must be\n"
00067    "taken NOT to compromise your security.\n\n"
00068    "There is a possibility of accessing DISA without password. Simply\n"
00069    "exchange your password with \"no-password\".\n\n"
00070    "    Example: exten => s,1,DISA(no-password|local)\n\n"
00071    "Be aware that using this compromises the security of your PBX.\n\n"
00072    "The arguments to this application (in extensions.conf) allow either\n"
00073    "specification of a single global passcode (that everyone uses), or\n"
00074    "individual passcodes contained in a file. It also allow specification\n"
00075    "of the context on which the user will be dialing. If no context is\n"
00076    "specified, the DISA application defaults the context to \"disa\".\n"
00077    "Presumably a normal system will have a special context set up\n"
00078    "for DISA use with some or a lot of restrictions. \n\n"
00079    "The file that contains the passcodes (if used) allows specification\n"
00080    "of either just a passcode (defaulting to the \"disa\" context, or\n"
00081    "passcode|context on each line of the file. The file may contain blank\n"
00082    "lines, or comments starting with \"#\" or \";\". In addition, the\n"
00083    "above arguments may have |new-callerid-string appended to them, to\n"
00084    "specify a new (different) callerid to be used for this call, for\n"
00085    "example: numeric-passcode|context|\"My Phone\" <(234) 123-4567> or \n"
00086    "full-pathname-of-passcode-file|\"My Phone\" <(234) 123-4567>.  Last\n"
00087    "but not least, |mailbox[@context] may be appended, which will cause\n"
00088    "a stutter-dialtone (indication \"dialrecall\") to be used, if the\n"
00089    "specified mailbox contains any new messages, for example:\n"
00090    "numeric-passcode|context||1234 (w/a changing callerid).  Note that\n"
00091    "in the case of specifying the numeric-passcode, the context must be\n"
00092    "specified if the callerid is specified also.\n\n"
00093    "If login is successful, the application looks up the dialed number in\n"
00094    "the specified (or default) context, and executes it if found.\n"
00095    "If the user enters an invalid extension and extension \"i\" (invalid) \n"
00096    "exists in the context, it will be used.\n";
00097 
00098 STANDARD_LOCAL_USER;
00099 
00100 LOCAL_USER_DECL;
00101 
00102 static void play_dialtone(struct ast_channel *chan, char *mailbox)
00103 {
00104    const struct tone_zone_sound *ts = NULL;
00105    if(ast_app_has_voicemail(mailbox, NULL))
00106       ts = ast_get_indication_tone(chan->zone, "dialrecall");
00107    else
00108       ts = ast_get_indication_tone(chan->zone, "dial");
00109    if (ts)
00110       ast_playtones_start(chan, 0, ts->data, 0);
00111    else
00112       ast_tonepair_start(chan, 350, 440, 0, 0);
00113 }
00114 
00115 static int disa_exec(struct ast_channel *chan, void *data)
00116 {
00117    int i,j,k,x,did_ignore;
00118    int firstdigittimeout = 20000;
00119    int digittimeout = 10000;
00120    struct localuser *u;
00121    char *tmp, exten[AST_MAX_EXTENSION],acctcode[20]="";
00122    char pwline[256];
00123    char ourcidname[256],ourcidnum[256];
00124    struct ast_frame *f;
00125    struct timeval lastdigittime;
00126    int res;
00127    time_t rstart;
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    );
00135 
00136    if (ast_strlen_zero(data)) {
00137       ast_log(LOG_WARNING, "disa requires an argument (passcode/passcode file)\n");
00138       return -1;
00139    }
00140 
00141    LOCAL_USER_ADD(u);
00142    
00143    if (chan->pbx) {
00144       firstdigittimeout = chan->pbx->rtimeout*1000;
00145       digittimeout = chan->pbx->dtimeout*1000;
00146    }
00147    
00148    if (ast_set_write_format(chan,AST_FORMAT_ULAW)) {
00149       ast_log(LOG_WARNING, "Unable to set write format to Mu-law on %s\n",chan->name);
00150       LOCAL_USER_REMOVE(u);
00151       return -1;
00152    }
00153    if (ast_set_read_format(chan,AST_FORMAT_ULAW)) {
00154       ast_log(LOG_WARNING, "Unable to set read format to Mu-law on %s\n",chan->name);
00155       LOCAL_USER_REMOVE(u);
00156       return -1;
00157    }
00158    
00159    ast_log(LOG_DEBUG, "Digittimeout: %d\n", digittimeout);
00160    ast_log(LOG_DEBUG, "Responsetimeout: %d\n", firstdigittimeout);
00161 
00162    tmp = ast_strdupa(data);
00163    if (!tmp) {
00164       ast_log(LOG_ERROR, "Out of memory\n");
00165       LOCAL_USER_REMOVE(u);
00166       return -1;
00167    }  
00168 
00169    AST_STANDARD_APP_ARGS(args, tmp);
00170 
00171    if (ast_strlen_zero(args.context)) 
00172       args.context = "disa";  
00173    if (ast_strlen_zero(args.mailbox))
00174       args.mailbox = "";
00175 
00176    ast_log(LOG_DEBUG, "Mailbox: %s\n",args.mailbox);
00177    
00178    if (chan->_state != AST_STATE_UP) {
00179       /* answer */
00180       ast_answer(chan);
00181    }
00182    i = k = x = 0; /* k is 0 for pswd entry, 1 for ext entry */
00183    did_ignore = 0;
00184    exten[0] = 0;
00185    acctcode[0] = 0;
00186    /* can we access DISA without password? */ 
00187 
00188    ast_log(LOG_DEBUG, "Context: %s\n",args.context);
00189 
00190    if (!strcasecmp(args.passcode, "no-password")) {
00191       k |= 1; /* We have the password */
00192       ast_log(LOG_DEBUG, "DISA no-password login success\n");
00193    }
00194    lastdigittime = ast_tvnow();
00195 
00196    play_dialtone(chan, args.mailbox);
00197 
00198    for (;;) {
00199         /* if outa time, give em reorder */
00200       if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > 
00201           ((k&2) ? digittimeout : firstdigittimeout))
00202       {
00203          ast_log(LOG_DEBUG,"DISA %s entry timeout on chan %s\n",
00204             ((k&1) ? "extension" : "password"),chan->name);
00205          break;
00206       }
00207       if ((res = ast_waitfor(chan, -1) < 0)) {
00208          ast_log(LOG_DEBUG, "Waitfor returned %d\n", res);
00209          continue;
00210       }
00211          
00212       f = ast_read(chan);
00213       if (f == NULL) 
00214       {
00215          LOCAL_USER_REMOVE(u);
00216          return -1;
00217       }
00218       if ((f->frametype == AST_FRAME_CONTROL) &&
00219           (f->subclass == AST_CONTROL_HANGUP))
00220       {
00221          ast_frfree(f);
00222          LOCAL_USER_REMOVE(u);
00223          return -1;
00224       }
00225       if (f->frametype == AST_FRAME_VOICE) {
00226          ast_frfree(f);
00227          continue;
00228       }
00229         /* if not DTMF, just do it again */
00230       if (f->frametype != AST_FRAME_DTMF) 
00231       {
00232          ast_frfree(f);
00233          continue;
00234       }
00235 
00236       j = f->subclass;  /* save digit */
00237       ast_frfree(f);
00238       if (i == 0) 
00239       {
00240          k|=2; /* We have the first digit */ 
00241          ast_playtones_stop(chan);
00242       }
00243       lastdigittime = ast_tvnow();
00244         /* got a DTMF tone */
00245       if (i < AST_MAX_EXTENSION) /* if still valid number of digits */
00246       {
00247          if (!(k&1)) /* if in password state */
00248          {
00249             if (j == '#') /* end of password */
00250             {
00251                  /* see if this is an integer */
00252                if (sscanf(args.passcode,"%d",&j) < 1)
00253                   { /* nope, it must be a filename */
00254                   fp = fopen(args.passcode,"r");
00255                   if (!fp)
00256                      {
00257                      ast_log(LOG_WARNING,"DISA password file %s not found on chan %s\n",args.passcode,chan->name);
00258                      LOCAL_USER_REMOVE(u);
00259                      return -1;
00260                      }
00261                   pwline[0] = 0;
00262                   while(fgets(pwline,sizeof(pwline) - 1,fp))
00263                      {
00264                      if (!pwline[0]) continue;
00265                      if (pwline[strlen(pwline) - 1] == '\n') 
00266                         pwline[strlen(pwline) - 1] = 0;
00267                      if (!pwline[0]) continue;
00268                        /* skip comments */
00269                      if (pwline[0] == '#') continue;
00270                      if (pwline[0] == ';') continue;
00271 
00272                      AST_STANDARD_APP_ARGS(args, pwline);
00273          
00274                      ast_log(LOG_DEBUG, "Mailbox: %s\n",args.mailbox);
00275 
00276                        /* password must be in valid format (numeric) */
00277                      if (sscanf(args.passcode,"%d",&j) < 1) continue;
00278                        /* if we got it */
00279                      if (!strcmp(exten,args.passcode)) {
00280                         if (ast_strlen_zero(args.context))
00281                            args.context = "disa";
00282                         if (ast_strlen_zero(args.mailbox))
00283                            args.mailbox = "";
00284                         break;
00285                      }
00286                      }
00287                   fclose(fp);
00288                   }
00289                  /* compare the two */
00290                if (strcmp(exten,args.passcode))
00291                {
00292                   ast_log(LOG_WARNING,"DISA on chan %s got bad password %s\n",chan->name,exten);
00293                   goto reorder;
00294 
00295                }
00296                 /* password good, set to dial state */
00297                ast_log(LOG_DEBUG,"DISA on chan %s password is good\n",chan->name);
00298                play_dialtone(chan, args.mailbox);
00299 
00300                k|=1; /* In number mode */
00301                i = 0;  /* re-set buffer pointer */
00302                exten[sizeof(acctcode)] = 0;
00303                ast_copy_string(acctcode, exten, sizeof(acctcode));
00304                exten[0] = 0;
00305                ast_log(LOG_DEBUG,"Successful DISA log-in on chan %s\n",chan->name);
00306                continue;
00307             }
00308          }
00309 
00310          exten[i++] = j;  /* save digit */
00311          exten[i] = 0;
00312          if (!(k&1)) continue; /* if getting password, continue doing it */
00313            /* if this exists */
00314 
00315          if (ast_ignore_pattern(args.context, exten)) {
00316             play_dialtone(chan, "");
00317             did_ignore = 1;
00318          } else
00319             if (did_ignore) {
00320                ast_playtones_stop(chan);
00321                did_ignore = 0;
00322             }
00323 
00324            /* if can do some more, do it */
00325          if (!ast_matchmore_extension(chan,args.context,exten,1, chan->cid.cid_num)) {
00326             break;
00327          }
00328       }
00329    }
00330 
00331    if (k == 3) {
00332       int recheck = 0;
00333       struct ast_flags flags = { AST_CDR_FLAG_POSTED };
00334 
00335       if (!ast_exists_extension(chan, args.context, exten, 1, chan->cid.cid_num)) {
00336          pbx_builtin_setvar_helper(chan, "INVALID_EXTEN", exten);
00337          exten[0] = 'i';
00338          exten[1] = '\0';
00339          recheck = 1;
00340       }
00341       if (!recheck || ast_exists_extension(chan, args.context, exten, 1, chan->cid.cid_num)) {
00342          ast_playtones_stop(chan);
00343          /* We're authenticated and have a target extension */
00344          if (!ast_strlen_zero(args.cid))
00345          {
00346             ast_callerid_split(args.cid, ourcidname, sizeof(ourcidname), ourcidnum, sizeof(ourcidnum));
00347             ast_set_callerid(chan, ourcidnum, ourcidname, ourcidnum);
00348          }
00349 
00350          if (!ast_strlen_zero(acctcode))
00351             ast_copy_string(chan->accountcode, acctcode, sizeof(chan->accountcode));
00352 
00353          ast_cdr_reset(chan->cdr, &flags);
00354          ast_explicit_goto(chan, args.context, exten, 1);
00355          LOCAL_USER_REMOVE(u);
00356          return 0;
00357       }
00358    }
00359 
00360    /* Received invalid, but no "i" extension exists in the given context */
00361 
00362 reorder:
00363 
00364    ast_indicate(chan,AST_CONTROL_CONGESTION);
00365    /* something is invalid, give em reorder for several seconds */
00366    time(&rstart);
00367    while(time(NULL) < rstart + 10)
00368    {
00369       if (ast_waitfor(chan, -1) < 0)
00370          break;
00371       f = ast_read(chan);
00372       if (!f)
00373          break;
00374       ast_frfree(f);
00375    }
00376    ast_playtones_stop(chan);
00377    LOCAL_USER_REMOVE(u);
00378    return -1;
00379 }
00380 
00381 int unload_module(void)
00382 {
00383    int res;
00384 
00385    res = ast_unregister_application(app);
00386 
00387    STANDARD_HANGUP_LOCALUSERS;
00388 
00389    return res;
00390 }
00391 
00392 int load_module(void)
00393 {
00394    return ast_register_application(app, disa_exec, synopsis, descrip);
00395 }
00396 
00397 char *description(void)
00398 {
00399    return tdesc;
00400 }
00401 
00402 int usecount(void)
00403 {
00404    int res;
00405    STANDARD_USECOUNT(res);
00406    return res;
00407 }
00408 
00409 char *key(void)
00410 {
00411    return ASTERISK_GPL_KEY;
00412 }

Generated on Sat Sep 16 07:28:05 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.7