Mon Mar 19 11:30:20 2012

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: 328209 $")
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 
00187    ast_debug(1, "Mailbox: %s\n",args.mailbox);
00188 
00189    if (!ast_test_flag(&flags, NOANSWER_FLAG)) {
00190       if (chan->_state != AST_STATE_UP) {
00191          /* answer */
00192          ast_answer(chan);
00193       }
00194    } else special_noanswer = 1;
00195 
00196    ast_debug(1, "Context: %s\n",args.context);
00197 
00198    if (!strcasecmp(args.passcode, "no-password")) {
00199       k |= 1; /* We have the password */
00200       ast_debug(1, "DISA no-password login success\n");
00201    }
00202 
00203    lastdigittime = ast_tvnow();
00204 
00205    play_dialtone(chan, args.mailbox);
00206 
00207    ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
00208 
00209    for (;;) {
00210         /* if outa time, give em reorder */
00211       if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > ((k&2) ? digittimeout : firstdigittimeout)) {
00212          ast_debug(1,"DISA %s entry timeout on chan %s\n",
00213             ((k&1) ? "extension" : "password"),chan->name);
00214          break;
00215       }
00216 
00217       if ((res = ast_waitfor(chan, -1) < 0)) {
00218          ast_debug(1, "Waitfor returned %d\n", res);
00219          continue;
00220       }
00221 
00222       if (!(f = ast_read(chan))) {
00223          ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
00224          return -1;
00225       }
00226 
00227       if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP)) {
00228          if (f->data.uint32)
00229             chan->hangupcause = f->data.uint32;
00230          ast_frfree(f);
00231          ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
00232          return -1;
00233       }
00234 
00235       /* If the frame coming in is not DTMF, just drop it and continue */
00236       if (f->frametype != AST_FRAME_DTMF) {
00237          ast_frfree(f);
00238          continue;
00239       }
00240 
00241       j = f->subclass.integer;  /* save digit */
00242       ast_frfree(f);
00243 
00244       if (!i) {
00245          k |= 2; /* We have the first digit */
00246          ast_playtones_stop(chan);
00247       }
00248 
00249       lastdigittime = ast_tvnow();
00250 
00251       /* got a DTMF tone */
00252       if (i < AST_MAX_EXTENSION) { /* if still valid number of digits */
00253          if (!(k&1)) { /* if in password state */
00254             if (j == '#') { /* end of password */
00255                  /* see if this is an integer */
00256                if (sscanf(args.passcode,"%30d",&j) < 1) { /* nope, it must be a filename */
00257                   fp = fopen(args.passcode,"r");
00258                   if (!fp) {
00259                      ast_log(LOG_WARNING,"DISA password file %s not found on chan %s\n",args.passcode,chan->name);
00260                      ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
00261                      return -1;
00262                   }
00263                   pwline[0] = 0;
00264                   while(fgets(pwline,sizeof(pwline) - 1,fp)) {
00265                      if (!pwline[0])
00266                         continue;
00267                      if (pwline[strlen(pwline) - 1] == '\n')
00268                         pwline[strlen(pwline) - 1] = 0;
00269                      if (!pwline[0])
00270                         continue;
00271                       /* skip comments */
00272                      if (pwline[0] == '#')
00273                         continue;
00274                      if (pwline[0] == ';')
00275                         continue;
00276 
00277                      AST_STANDARD_APP_ARGS(args, pwline);
00278 
00279                      ast_debug(1, "Mailbox: %s\n",args.mailbox);
00280 
00281                      /* password must be in valid format (numeric) */
00282                      if (sscanf(args.passcode,"%30d", &j) < 1)
00283                         continue;
00284                       /* if we got it */
00285                      if (!strcmp(exten,args.passcode)) {
00286                         if (ast_strlen_zero(args.context))
00287                            args.context = "disa";
00288                         if (ast_strlen_zero(args.mailbox))
00289                            args.mailbox = "";
00290                         break;
00291                      }
00292                   }
00293                   fclose(fp);
00294                }
00295                /* compare the two */
00296                if (strcmp(exten,args.passcode)) {
00297                   ast_log(LOG_WARNING,"DISA on chan %s got bad password %s\n",chan->name,exten);
00298                   goto reorder;
00299 
00300                }
00301                 /* password good, set to dial state */
00302                ast_debug(1,"DISA on chan %s password is good\n",chan->name);
00303                play_dialtone(chan, args.mailbox);
00304 
00305                k|=1; /* In number mode */
00306                i = 0;  /* re-set buffer pointer */
00307                exten[sizeof(acctcode)] = 0;
00308                ast_copy_string(acctcode, exten, sizeof(acctcode));
00309                exten[0] = 0;
00310                ast_debug(1,"Successful DISA log-in on chan %s\n", chan->name);
00311                continue;
00312             }
00313          } else {
00314             if (j == '#') { /* end of extension .. maybe */
00315                if (i == 0
00316                   && (ast_matchmore_extension(chan, args.context, "#", 1,
00317                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))
00318                      || ast_exists_extension(chan, args.context, "#", 1,
00319                         S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) ) {
00320                   /* Let the # be the part of, or the entire extension */
00321                } else {
00322                   break;
00323                }
00324             }
00325          }
00326 
00327          exten[i++] = j;  /* save digit */
00328          exten[i] = 0;
00329          if (!(k&1))
00330             continue; /* if getting password, continue doing it */
00331          /* if this exists */
00332 
00333          /* user wants end of number, remove # */
00334          if (ast_test_flag(&flags, POUND_TO_END_FLAG) && j == '#') {
00335             exten[--i] = 0;
00336             break;
00337          }
00338 
00339          if (ast_ignore_pattern(args.context, exten)) {
00340             play_dialtone(chan, "");
00341             did_ignore = 1;
00342          } else
00343             if (did_ignore) {
00344                ast_playtones_stop(chan);
00345                did_ignore = 0;
00346             }
00347 
00348          /* if can do some more, do it */
00349          if (!ast_matchmore_extension(chan, args.context, exten, 1,
00350             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
00351             break;
00352          }
00353       }
00354    }
00355 
00356    ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
00357 
00358    if (k == 3) {
00359       int recheck = 0;
00360       struct ast_flags cdr_flags = { AST_CDR_FLAG_POSTED };
00361 
00362       if (!ast_exists_extension(chan, args.context, exten, 1,
00363          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
00364          pbx_builtin_setvar_helper(chan, "INVALID_EXTEN", exten);
00365          exten[0] = 'i';
00366          exten[1] = '\0';
00367          recheck = 1;
00368       }
00369       if (!recheck
00370          || ast_exists_extension(chan, args.context, exten, 1,
00371             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
00372          ast_playtones_stop(chan);
00373          /* We're authenticated and have a target extension */
00374          if (!ast_strlen_zero(args.cid)) {
00375             ast_callerid_split(args.cid, ourcidname, sizeof(ourcidname), ourcidnum, sizeof(ourcidnum));
00376             ast_set_callerid(chan, ourcidnum, ourcidname, ourcidnum);
00377          }
00378 
00379          if (!ast_strlen_zero(acctcode))
00380             ast_string_field_set(chan, accountcode, acctcode);
00381 
00382          if (special_noanswer) cdr_flags.flags = 0;
00383          ast_cdr_reset(chan->cdr, &cdr_flags);
00384          ast_explicit_goto(chan, args.context, exten, 1);
00385          return 0;
00386       }
00387    }
00388 
00389    /* Received invalid, but no "i" extension exists in the given context */
00390 
00391 reorder:
00392    /* Play congestion for a bit */
00393    ast_indicate(chan, AST_CONTROL_CONGESTION);
00394    ast_safe_sleep(chan, 10*1000);
00395 
00396    ast_playtones_stop(chan);
00397 
00398    return -1;
00399 }
00400 
00401 static int unload_module(void)
00402 {
00403    return ast_unregister_application(app);
00404 }
00405 
00406 static int load_module(void)
00407 {
00408    return ast_register_application_xml(app, disa_exec) ?
00409       AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS;
00410 }
00411 
00412 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "DISA (Direct Inward System Access) Application");

Generated on Mon Mar 19 11:30:20 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7