Wed Oct 28 11:45:25 2009

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: 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          /* answer */
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; /* We have the password */
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         /* if outa time, give em reorder */
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       /* If the frame coming in is not DTMF, just drop it and continue */
00203       if (f->frametype != AST_FRAME_DTMF) {
00204          ast_frfree(f);
00205          continue;
00206       }
00207 
00208       j = f->subclass;  /* save digit */
00209       ast_frfree(f);
00210 
00211       if (!i) {
00212          k |= 2; /* We have the first digit */
00213          ast_playtones_stop(chan);
00214       }
00215 
00216       lastdigittime = ast_tvnow();
00217 
00218       /* got a DTMF tone */
00219       if (i < AST_MAX_EXTENSION) { /* if still valid number of digits */
00220          if (!(k&1)) { /* if in password state */
00221             if (j == '#') { /* end of password */
00222                  /* see if this is an integer */
00223                if (sscanf(args.passcode,"%30d",&j) < 1) { /* nope, it must be a filename */
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                       /* skip comments */
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                      /* password must be in valid format (numeric) */
00249                      if (sscanf(args.passcode,"%30d", &j) < 1)
00250                         continue;
00251                       /* if we got it */
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                /* compare the two */
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                 /* password good, set to dial state */
00269                ast_debug(1,"DISA on chan %s password is good\n",chan->name);
00270                play_dialtone(chan, args.mailbox);
00271 
00272                k|=1; /* In number mode */
00273                i = 0;  /* re-set buffer pointer */
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 == '#') { /* end of extension .. maybe */
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                   /* Let the # be the part of, or the entire extension */
00286                } else {
00287                   break;
00288                }
00289             }
00290          }
00291 
00292          exten[i++] = j;  /* save digit */
00293          exten[i] = 0;
00294          if (!(k&1))
00295             continue; /* if getting password, continue doing it */
00296          /* if this exists */
00297 
00298          /* user wants end of number, remove # */
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          /* if can do some more, do it */
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          /* We're authenticated and have a target extension */
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    /* Received invalid, but no "i" extension exists in the given context */
00351 
00352 reorder:
00353    /* Play congestion for a bit */
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");

Generated on Wed Oct 28 11:45:25 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6