Thu Sep 7 01:02:50 2017

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

Generated on 7 Sep 2017 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1