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

Generated on Fri Jul 24 00:40:38 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7