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 /*** MODULEINFO
00030    <use type="module">app_cdr</use>
00031    <support_level>core</support_level>
00032  ***/
00033 
00034 #include "asterisk.h"
00035 
00036 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 404295 $")
00037 
00038 #include <math.h>
00039 #include <sys/time.h>
00040 
00041 #include "asterisk/lock.h"
00042 #include "asterisk/file.h"
00043 #include "asterisk/channel.h"
00044 #include "asterisk/app.h"
00045 #include "asterisk/indications.h"
00046 #include "asterisk/pbx.h"
00047 #include "asterisk/module.h"
00048 #include "asterisk/translate.h"
00049 #include "asterisk/ulaw.h"
00050 #include "asterisk/callerid.h"
00051 #include "asterisk/stringfields.h"
00052 
00053 /*** DOCUMENTATION
00054    <application name="DISA" language="en_US">
00055       <synopsis>
00056          Direct Inward System Access.
00057       </synopsis>
00058       <syntax>
00059          <parameter name="passcode|filename" required="true">
00060             <para>If you need to present a DISA dialtone without entering a password,
00061             simply set <replaceable>passcode</replaceable> to <literal>no-password</literal></para>
00062             <para>You may specified a <replaceable>filename</replaceable> instead of a
00063             <replaceable>passcode</replaceable>, this filename must contain individual passcodes</para>
00064          </parameter>
00065          <parameter name="context">
00066             <para>Specifies the dialplan context in which the user-entered extension
00067             will be matched. If no context is specified, the DISA application defaults
00068             to the <literal>disa</literal> context. Presumably a normal system will have a special
00069             context set up for DISA use with some or a lot of restrictions.</para>
00070          </parameter>
00071          <parameter name="cid">
00072             <para>Specifies a new (different) callerid to be used for this call.</para>
00073          </parameter>
00074          <parameter name="mailbox" argsep="@">
00075             <para>Will cause a stutter-dialtone (indication <emphasis>dialrecall</emphasis>)
00076             to be used, if the specified mailbox contains any new messages.</para>
00077             <argument name="mailbox" required="true" />
00078             <argument name="context" required="false" />
00079          </parameter>
00080          <parameter name="options">
00081             <optionlist>
00082                <option name="n">
00083                   <para>The DISA application will not answer initially.</para>
00084                </option>
00085                <option name="p">
00086                   <para>The extension entered will be considered complete when a <literal>#</literal>
00087                   is entered.</para>
00088                </option>
00089             </optionlist>
00090          </parameter>
00091       </syntax>
00092       <description>
00093          <para>The DISA, Direct Inward System Access, application allows someone from
00094          outside the telephone switch (PBX) to obtain an <emphasis>internal</emphasis> system
00095          dialtone and to place calls from it as if they were placing a call from
00096          within the switch.
00097          DISA plays a dialtone. The user enters their numeric passcode, followed by
00098          the pound sign <literal>#</literal>. If the passcode is correct, the user is then given
00099          system dialtone within <replaceable>context</replaceable> on which a call may be placed.
00100          If the user enters an invalid extension and extension <literal>i</literal> exists in the specified
00101          <replaceable>context</replaceable>, it will be used.
00102          </para>
00103          <para>Be aware that using this may compromise the security of your PBX.</para>
00104          <para>The arguments to this application (in <filename>extensions.conf</filename>) allow either
00105          specification of a single global <replaceable>passcode</replaceable> (that everyone uses), or
00106          individual passcodes contained in a file (<replaceable>filename</replaceable>).</para>
00107          <para>The file that contains the passcodes (if used) allows a complete
00108          specification of all of the same arguments available on the command
00109          line, with the sole exception of the options. The file may contain blank
00110          lines, or comments starting with <literal>#</literal> or <literal>;</literal>.</para>
00111       </description>
00112       <see-also>
00113          <ref type="application">Authenticate</ref>
00114          <ref type="application">VMAuthenticate</ref>
00115       </see-also>
00116    </application>
00117  ***/
00118 static const char app[] = "DISA";
00119 
00120 enum {
00121    NOANSWER_FLAG = (1 << 0),
00122    POUND_TO_END_FLAG = (1 << 1),
00123 };
00124 
00125 AST_APP_OPTIONS(app_opts, {
00126    AST_APP_OPTION('n', NOANSWER_FLAG),
00127    AST_APP_OPTION('p', POUND_TO_END_FLAG),
00128 });
00129 
00130 static void play_dialtone(struct ast_channel *chan, char *mailbox)
00131 {
00132    struct ast_tone_zone_sound *ts = NULL;
00133 
00134    if (ast_app_has_voicemail(mailbox, NULL)) {
00135       ts = ast_get_indication_tone(ast_channel_zone(chan), "dialrecall");
00136    } else {
00137       ts = ast_get_indication_tone(ast_channel_zone(chan), "dial");
00138    }
00139 
00140    if (ts) {
00141       ast_playtones_start(chan, 0, ts->data, 0);
00142       ts = ast_tone_zone_sound_unref(ts);
00143    } else {
00144       ast_tonepair_start(chan, 350, 440, 0, 0);
00145    }
00146 }
00147 
00148 static int disa_exec(struct ast_channel *chan, const char *data)
00149 {
00150    int i = 0, j, k = 0, did_ignore = 0, special_noanswer = 0;
00151    int firstdigittimeout = (ast_channel_pbx(chan) ? ast_channel_pbx(chan)->rtimeoutms : 20000);
00152    int digittimeout = (ast_channel_pbx(chan) ? ast_channel_pbx(chan)->dtimeoutms : 10000);
00153    struct ast_flags flags;
00154    char *tmp, exten[AST_MAX_EXTENSION] = "", acctcode[20]="";
00155    char pwline[256];
00156    char ourcidname[256],ourcidnum[256];
00157    struct ast_frame *f;
00158    struct timeval lastdigittime;
00159    int res;
00160    FILE *fp;
00161    AST_DECLARE_APP_ARGS(args,
00162       AST_APP_ARG(passcode);
00163       AST_APP_ARG(context);
00164       AST_APP_ARG(cid);
00165       AST_APP_ARG(mailbox);
00166       AST_APP_ARG(options);
00167    );
00168 
00169    if (ast_strlen_zero(data)) {
00170       ast_log(LOG_WARNING, "DISA requires an argument (passcode/passcode file)\n");
00171       return -1;
00172    }
00173 
00174    ast_debug(1, "Digittimeout: %d\n", digittimeout);
00175    ast_debug(1, "Responsetimeout: %d\n", firstdigittimeout);
00176 
00177    tmp = ast_strdupa(data);
00178 
00179    AST_STANDARD_APP_ARGS(args, tmp);
00180 
00181    if (ast_strlen_zero(args.context))
00182       args.context = "disa";
00183    if (ast_strlen_zero(args.mailbox))
00184       args.mailbox = "";
00185    if (!ast_strlen_zero(args.options)) {
00186       ast_app_parse_options(app_opts, &flags, NULL, args.options);
00187    } else {
00188       /* Coverity - This uninit_use should be ignored since this macro initializes the flags */
00189       ast_clear_flag(&flags, AST_FLAGS_ALL);
00190    }
00191 
00192 
00193    ast_debug(1, "Mailbox: %s\n",args.mailbox);
00194 
00195    if (!ast_test_flag(&flags, NOANSWER_FLAG)) {
00196       if (ast_channel_state(chan) != AST_STATE_UP) {
00197          /* answer */
00198          ast_answer(chan);
00199       }
00200    } else special_noanswer = 1;
00201 
00202    ast_debug(1, "Context: %s\n",args.context);
00203 
00204    if (!strcasecmp(args.passcode, "no-password")) {
00205       k |= 1; /* We have the password */
00206       ast_debug(1, "DISA no-password login success\n");
00207    }
00208 
00209    lastdigittime = ast_tvnow();
00210 
00211    play_dialtone(chan, args.mailbox);
00212 
00213    ast_set_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
00214 
00215    for (;;) {
00216         /* if outa time, give em reorder */
00217       if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > ((k&2) ? digittimeout : firstdigittimeout)) {
00218          ast_debug(1,"DISA %s entry timeout on chan %s\n",
00219             ((k&1) ? "extension" : "password"),ast_channel_name(chan));
00220          break;
00221       }
00222 
00223       if ((res = ast_waitfor(chan, -1)) < 0) {
00224          ast_debug(1, "Waitfor returned %d\n", res);
00225          continue;
00226       }
00227 
00228       if (!(f = ast_read(chan))) {
00229          ast_clear_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
00230          return -1;
00231       }
00232 
00233       if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP)) {
00234          if (f->data.uint32)
00235             ast_channel_hangupcause_set(chan, f->data.uint32);
00236          ast_frfree(f);
00237          ast_clear_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
00238          return -1;
00239       }
00240 
00241       /* If the frame coming in is not DTMF, just drop it and continue */
00242       if (f->frametype != AST_FRAME_DTMF) {
00243          ast_frfree(f);
00244          continue;
00245       }
00246 
00247       j = f->subclass.integer;  /* save digit */
00248       ast_frfree(f);
00249 
00250       if (!i) {
00251          k |= 2; /* We have the first digit */
00252          ast_playtones_stop(chan);
00253       }
00254 
00255       lastdigittime = ast_tvnow();
00256 
00257       /* got a DTMF tone */
00258       if (i < AST_MAX_EXTENSION) { /* if still valid number of digits */
00259          if (!(k&1)) { /* if in password state */
00260             if (j == '#') { /* end of password */
00261                  /* see if this is an integer */
00262                if (sscanf(args.passcode,"%30d",&j) < 1) { /* nope, it must be a filename */
00263                   fp = fopen(args.passcode,"r");
00264                   if (!fp) {
00265                      ast_log(LOG_WARNING,"DISA password file %s not found on chan %s\n",args.passcode,ast_channel_name(chan));
00266                      ast_clear_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
00267                      return -1;
00268                   }
00269                   pwline[0] = 0;
00270                   while(fgets(pwline,sizeof(pwline) - 1,fp)) {
00271                      if (!pwline[0])
00272                         continue;
00273                      if (pwline[strlen(pwline) - 1] == '\n')
00274                         pwline[strlen(pwline) - 1] = 0;
00275                      if (!pwline[0])
00276                         continue;
00277                       /* skip comments */
00278                      if (pwline[0] == '#')
00279                         continue;
00280                      if (pwline[0] == ';')
00281                         continue;
00282 
00283                      AST_STANDARD_APP_ARGS(args, pwline);
00284 
00285                      ast_debug(1, "Mailbox: %s\n",args.mailbox);
00286 
00287                      /* password must be in valid format (numeric) */
00288                      if (sscanf(args.passcode,"%30d", &j) < 1)
00289                         continue;
00290                       /* if we got it */
00291                      if (!strcmp(exten,args.passcode)) {
00292                         if (ast_strlen_zero(args.context))
00293                            args.context = "disa";
00294                         if (ast_strlen_zero(args.mailbox))
00295                            args.mailbox = "";
00296                         break;
00297                      }
00298                   }
00299                   fclose(fp);
00300                }
00301                /* compare the two */
00302                if (strcmp(exten,args.passcode)) {
00303                   ast_log(LOG_WARNING,"DISA on chan %s got bad password %s\n",ast_channel_name(chan),exten);
00304                   goto reorder;
00305 
00306                }
00307                 /* password good, set to dial state */
00308                ast_debug(1,"DISA on chan %s password is good\n",ast_channel_name(chan));
00309                play_dialtone(chan, args.mailbox);
00310 
00311                k|=1; /* In number mode */
00312                i = 0;  /* re-set buffer pointer */
00313                exten[sizeof(acctcode)] = 0;
00314                ast_copy_string(acctcode, exten, sizeof(acctcode));
00315                exten[0] = 0;
00316                ast_debug(1,"Successful DISA log-in on chan %s\n", ast_channel_name(chan));
00317                continue;
00318             }
00319          } else {
00320             if (j == '#') { /* end of extension .. maybe */
00321                if (i == 0
00322                   && (ast_matchmore_extension(chan, args.context, "#", 1,
00323                      S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))
00324                      || ast_exists_extension(chan, args.context, "#", 1,
00325                         S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) ) {
00326                   /* Let the # be the part of, or the entire extension */
00327                } else {
00328                   break;
00329                }
00330             }
00331          }
00332 
00333          exten[i++] = j;  /* save digit */
00334          exten[i] = 0;
00335          if (!(k&1))
00336             continue; /* if getting password, continue doing it */
00337          /* if this exists */
00338 
00339          /* user wants end of number, remove # */
00340          if (ast_test_flag(&flags, POUND_TO_END_FLAG) && j == '#') {
00341             exten[--i] = 0;
00342             break;
00343          }
00344 
00345          if (ast_ignore_pattern(args.context, exten)) {
00346             play_dialtone(chan, "");
00347             did_ignore = 1;
00348          } else
00349             if (did_ignore) {
00350                ast_playtones_stop(chan);
00351                did_ignore = 0;
00352             }
00353 
00354          /* if can do some more, do it */
00355          if (!ast_matchmore_extension(chan, args.context, exten, 1,
00356             S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
00357             break;
00358          }
00359       }
00360    }
00361 
00362    ast_clear_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
00363 
00364    if (k == 3) {
00365       int recheck = 0;
00366       struct ast_app *app_reset_cdr;
00367 
00368       if (!ast_exists_extension(chan, args.context, exten, 1,
00369          S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
00370          pbx_builtin_setvar_helper(chan, "INVALID_EXTEN", exten);
00371          exten[0] = 'i';
00372          exten[1] = '\0';
00373          recheck = 1;
00374       }
00375       if (!recheck
00376          || ast_exists_extension(chan, args.context, exten, 1,
00377             S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
00378          ast_playtones_stop(chan);
00379          /* We're authenticated and have a target extension */
00380          if (!ast_strlen_zero(args.cid)) {
00381             ast_callerid_split(args.cid, ourcidname, sizeof(ourcidname), ourcidnum, sizeof(ourcidnum));
00382             ast_set_callerid(chan, ourcidnum, ourcidname, ourcidnum);
00383          }
00384 
00385          if (!ast_strlen_zero(acctcode)) {
00386             ast_channel_lock(chan);
00387             ast_channel_accountcode_set(chan, acctcode);
00388             ast_channel_unlock(chan);
00389          }
00390 
00391          app_reset_cdr = pbx_findapp("ResetCDR");
00392          if (app_reset_cdr) {
00393             pbx_exec(chan, app_reset_cdr, special_noanswer ? "" : "e");
00394          } else {
00395             ast_log(AST_LOG_NOTICE, "ResetCDR application not found; CDR will not be reset\n");
00396          }
00397          ast_explicit_goto(chan, args.context, exten, 1);
00398          return 0;
00399       }
00400    }
00401 
00402    /* Received invalid, but no "i" extension exists in the given context */
00403 
00404 reorder:
00405    /* Play congestion for a bit */
00406    ast_indicate(chan, AST_CONTROL_CONGESTION);
00407    ast_safe_sleep(chan, 10*1000);
00408 
00409    ast_playtones_stop(chan);
00410 
00411    return -1;
00412 }
00413 
00414 static int unload_module(void)
00415 {
00416    return ast_unregister_application(app);
00417 }
00418 
00419 static int load_module(void)
00420 {
00421    return ast_register_application_xml(app, disa_exec) ?
00422       AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS;
00423 }
00424 
00425 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "DISA (Direct Inward System Access) Application");

Generated on Thu Apr 16 06:27:09 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6