Asterisk - The Open Source Telephony Project  21.4.1
app_disa.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  *
7  * Made only slightly more sane by Mark Spencer <markster@digium.com>
8  *
9  * See http://www.asterisk.org for more information about
10  * the Asterisk project. Please do not directly contact
11  * any of the maintainers of this project for assistance;
12  * the project provides a web site, mailing lists and IRC
13  * channels for your use.
14  *
15  * This program is free software, distributed under the terms of
16  * the GNU General Public License Version 2. See the LICENSE file
17  * at the top of the source tree.
18  */
19 
20 /*! \file
21  *
22  * \brief DISA -- Direct Inward System Access Application
23  *
24  * \author Jim Dixon <jim@lambdatel.com>
25  *
26  * \ingroup applications
27  */
28 
29 /*** MODULEINFO
30  <use type="module">app_cdr</use>
31  <support_level>core</support_level>
32  ***/
33 
34 #include "asterisk.h"
35 
36 #include <math.h>
37 #include <sys/time.h>
38 
39 #include "asterisk/lock.h"
40 #include "asterisk/file.h"
41 #include "asterisk/channel.h"
42 #include "asterisk/app.h"
43 #include "asterisk/indications.h"
44 #include "asterisk/pbx.h"
45 #include "asterisk/module.h"
46 #include "asterisk/translate.h"
47 #include "asterisk/ulaw.h"
48 #include "asterisk/callerid.h"
49 #include "asterisk/stringfields.h"
50 
51 /*** DOCUMENTATION
52  <application name="DISA" language="en_US">
53  <synopsis>
54  Direct Inward System Access.
55  </synopsis>
56  <syntax>
57  <parameter name="passcode|filename" required="true">
58  <para>If you need to present a DISA dialtone without entering a password,
59  simply set <replaceable>passcode</replaceable> to <literal>no-password</literal></para>
60  <para>You may specified a <replaceable>filename</replaceable> instead of a
61  <replaceable>passcode</replaceable>, this filename must contain individual passcodes</para>
62  </parameter>
63  <parameter name="context">
64  <para>Specifies the dialplan context in which the user-entered extension
65  will be matched. If no context is specified, the DISA application defaults
66  to the <literal>disa</literal> context. Presumably a normal system will have a special
67  context set up for DISA use with some or a lot of restrictions.</para>
68  </parameter>
69  <parameter name="cid">
70  <para>Specifies a new (different) callerid to be used for this call.</para>
71  </parameter>
72  <parameter name="mailbox" argsep="@">
73  <para>Will cause a stutter-dialtone (indication <emphasis>dialrecall</emphasis>)
74  to be used, if the specified mailbox contains any new messages.</para>
75  <argument name="mailbox" required="true" />
76  <argument name="context" required="false" />
77  </parameter>
78  <parameter name="options">
79  <optionlist>
80  <option name="n">
81  <para>The DISA application will not answer initially.</para>
82  </option>
83  <option name="p">
84  <para>The extension entered will be considered complete when a <literal>#</literal>
85  is entered.</para>
86  </option>
87  </optionlist>
88  </parameter>
89  </syntax>
90  <description>
91  <para>The DISA, Direct Inward System Access, application allows someone from
92  outside the telephone switch (PBX) to obtain an <emphasis>internal</emphasis> system
93  dialtone and to place calls from it as if they were placing a call from
94  within the switch.
95  DISA plays a dialtone. The user enters their numeric passcode, followed by
96  the pound sign <literal>#</literal>. If the passcode is correct, the user is then given
97  system dialtone within <replaceable>context</replaceable> on which a call may be placed.
98  If the user enters an invalid extension and extension <literal>i</literal> exists in the specified
99  <replaceable>context</replaceable>, it will be used.
100  </para>
101  <para>Be aware that using this may compromise the security of your PBX.</para>
102  <para>The arguments to this application (in <filename>extensions.conf</filename>) allow either
103  specification of a single global <replaceable>passcode</replaceable> (that everyone uses), or
104  individual passcodes contained in a file (<replaceable>filename</replaceable>).</para>
105  <para>The file that contains the passcodes (if used) allows a complete
106  specification of all of the same arguments available on the command
107  line, with the sole exception of the options. The file may contain blank
108  lines, or comments starting with <literal>#</literal> or <literal>;</literal>.</para>
109  </description>
110  <see-also>
111  <ref type="application">Authenticate</ref>
112  <ref type="application">VMAuthenticate</ref>
113  </see-also>
114  </application>
115  ***/
116 static const char app[] = "DISA";
117 
118 enum {
119  NOANSWER_FLAG = (1 << 0),
120  POUND_TO_END_FLAG = (1 << 1),
121 };
122 
123 AST_APP_OPTIONS(app_opts, {
124  AST_APP_OPTION('n', NOANSWER_FLAG),
125  AST_APP_OPTION('p', POUND_TO_END_FLAG),
126 });
127 
128 static void play_dialtone(struct ast_channel *chan, char *mailbox)
129 {
130  struct ast_tone_zone_sound *ts = NULL;
131 
132  if (ast_app_has_voicemail(mailbox, NULL)) {
133  ts = ast_get_indication_tone(ast_channel_zone(chan), "dialrecall");
134  } else {
135  ts = ast_get_indication_tone(ast_channel_zone(chan), "dial");
136  }
137 
138  if (ts) {
139  ast_playtones_start(chan, 0, ts->data, 0);
140  ts = ast_tone_zone_sound_unref(ts);
141  } else {
142  ast_tonepair_start(chan, 350, 440, 0, 0);
143  }
144 }
145 
146 static int disa_exec(struct ast_channel *chan, const char *data)
147 {
148  int i = 0, j, k = 0, did_ignore = 0, special_noanswer = 0;
149  int firstdigittimeout = (ast_channel_pbx(chan) ? ast_channel_pbx(chan)->rtimeoutms : 20000);
150  int digittimeout = (ast_channel_pbx(chan) ? ast_channel_pbx(chan)->dtimeoutms : 10000);
151  struct ast_flags flags;
152  char *tmp, exten[AST_MAX_EXTENSION] = "", acctcode[20]="";
153  char pwline[256];
154  char ourcidname[256],ourcidnum[256];
155  struct ast_frame *f;
156  struct timeval lastdigittime;
157  int res;
158  FILE *fp;
160  AST_APP_ARG(passcode);
161  AST_APP_ARG(context);
162  AST_APP_ARG(cid);
163  AST_APP_ARG(mailbox);
164  AST_APP_ARG(options);
165  );
166 
167  if (ast_strlen_zero(data)) {
168  ast_log(LOG_WARNING, "DISA requires an argument (passcode/passcode file)\n");
169  return -1;
170  }
171 
172  ast_debug(1, "Digittimeout: %d\n", digittimeout);
173  ast_debug(1, "Responsetimeout: %d\n", firstdigittimeout);
174 
175  tmp = ast_strdupa(data);
176 
177  AST_STANDARD_APP_ARGS(args, tmp);
178 
179  if (ast_strlen_zero(args.context))
180  args.context = "disa";
181  if (ast_strlen_zero(args.mailbox))
182  args.mailbox = "";
183  if (!ast_strlen_zero(args.options)) {
184  ast_app_parse_options(app_opts, &flags, NULL, args.options);
185  } else {
186  /* Coverity - This uninit_use should be ignored since this macro initializes the flags */
187  ast_clear_flag(&flags, AST_FLAGS_ALL);
188  }
189 
190 
191  ast_debug(1, "Mailbox: %s\n",args.mailbox);
192 
193  if (!ast_test_flag(&flags, NOANSWER_FLAG)) {
194  if (ast_channel_state(chan) != AST_STATE_UP) {
195  /* answer */
196  ast_answer(chan);
197  }
198  } else special_noanswer = 1;
199 
200  ast_debug(1, "Context: %s\n",args.context);
201 
202  if (!strcasecmp(args.passcode, "no-password")) {
203  k |= 1; /* We have the password */
204  ast_debug(1, "DISA no-password login success\n");
205  }
206 
207  lastdigittime = ast_tvnow();
208 
209  play_dialtone(chan, args.mailbox);
210 
212 
213  for (;;) {
214  /* if outa time, give em reorder */
215  if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > ((k&2) ? digittimeout : firstdigittimeout)) {
216  ast_debug(1,"DISA %s entry timeout on chan %s\n",
217  ((k&1) ? "extension" : "password"),ast_channel_name(chan));
218  break;
219  }
220 
221  if ((res = ast_waitfor(chan, -1)) < 0) {
222  ast_debug(1, "Waitfor returned %d\n", res);
223  continue;
224  }
225 
226  if (!(f = ast_read(chan))) {
228  return -1;
229  }
230 
232  if (f->data.uint32)
233  ast_channel_hangupcause_set(chan, f->data.uint32);
234  ast_frfree(f);
236  return -1;
237  }
238 
239  /* If the frame coming in is not DTMF, just drop it and continue */
240  if (f->frametype != AST_FRAME_DTMF) {
241  ast_frfree(f);
242  continue;
243  }
244 
245  j = f->subclass.integer; /* save digit */
246  ast_frfree(f);
247 
248  if (!i) {
249  k |= 2; /* We have the first digit */
250  ast_playtones_stop(chan);
251  }
252 
253  lastdigittime = ast_tvnow();
254 
255  /* got a DTMF tone */
256  if (i < AST_MAX_EXTENSION) { /* if still valid number of digits */
257  if (!(k&1)) { /* if in password state */
258  if (j == '#') { /* end of password */
259  /* see if this is an integer */
260  if (sscanf(args.passcode,"%30d",&j) < 1) { /* nope, it must be a filename */
261  fp = fopen(args.passcode,"r");
262  if (!fp) {
263  ast_log(LOG_WARNING,"DISA password file %s not found on chan %s\n",args.passcode,ast_channel_name(chan));
265  return -1;
266  }
267  pwline[0] = 0;
268  while(fgets(pwline,sizeof(pwline) - 1,fp)) {
269  if (!pwline[0])
270  continue;
271  if (pwline[strlen(pwline) - 1] == '\n')
272  pwline[strlen(pwline) - 1] = 0;
273  if (!pwline[0])
274  continue;
275  /* skip comments */
276  if (pwline[0] == '#')
277  continue;
278  if (pwline[0] == ';')
279  continue;
280 
281  AST_STANDARD_APP_ARGS(args, pwline);
282 
283  ast_debug(1, "Mailbox: %s\n",args.mailbox);
284 
285  /* password must be in valid format (numeric) */
286  if (sscanf(args.passcode,"%30d", &j) < 1)
287  continue;
288  /* if we got it */
289  if (!strcmp(exten,args.passcode)) {
290  if (ast_strlen_zero(args.context))
291  args.context = "disa";
292  if (ast_strlen_zero(args.mailbox))
293  args.mailbox = "";
294  break;
295  }
296  }
297  fclose(fp);
298  }
299  /* compare the two */
300  if (strcmp(exten,args.passcode)) {
301  ast_log(LOG_WARNING,"DISA on chan %s got bad password %s\n",ast_channel_name(chan),exten);
302  goto reorder;
303 
304  }
305  /* password good, set to dial state */
306  ast_debug(1,"DISA on chan %s password is good\n",ast_channel_name(chan));
307  play_dialtone(chan, args.mailbox);
308 
309  k|=1; /* In number mode */
310  i = 0; /* re-set buffer pointer */
311  exten[sizeof(acctcode)] = 0;
312  ast_copy_string(acctcode, exten, sizeof(acctcode));
313  exten[0] = 0;
314  ast_debug(1,"Successful DISA log-in on chan %s\n", ast_channel_name(chan));
315  continue;
316  }
317  } else {
318  if (j == '#') { /* end of extension .. maybe */
319  if (i == 0
320  && (ast_matchmore_extension(chan, args.context, "#", 1,
321  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))
322  || ast_exists_extension(chan, args.context, "#", 1,
323  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) ) {
324  /* Let the # be the part of, or the entire extension */
325  } else {
326  break;
327  }
328  }
329  }
330 
331  exten[i++] = j; /* save digit */
332  exten[i] = 0;
333  if (!(k&1))
334  continue; /* if getting password, continue doing it */
335  /* if this exists */
336 
337  /* user wants end of number, remove # */
338  if (ast_test_flag(&flags, POUND_TO_END_FLAG) && j == '#') {
339  exten[--i] = 0;
340  break;
341  }
342 
343  if (ast_ignore_pattern(args.context, exten)) {
344  play_dialtone(chan, "");
345  did_ignore = 1;
346  } else
347  if (did_ignore) {
348  ast_playtones_stop(chan);
349  did_ignore = 0;
350  }
351 
352  /* if can do some more, do it */
353  if (!ast_matchmore_extension(chan, args.context, exten, 1,
354  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
355  break;
356  }
357  }
358  }
359 
361 
362  if (k == 3) {
363  int recheck = 0;
364 
365  if (!ast_exists_extension(chan, args.context, exten, 1,
366  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
367  pbx_builtin_setvar_helper(chan, "INVALID_EXTEN", exten);
368  exten[0] = 'i';
369  exten[1] = '\0';
370  recheck = 1;
371  }
372  if (!recheck
373  || ast_exists_extension(chan, args.context, exten, 1,
374  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
375  ast_playtones_stop(chan);
376  /* We're authenticated and have a target extension */
377  if (!ast_strlen_zero(args.cid)) {
378  ast_callerid_split(args.cid, ourcidname, sizeof(ourcidname), ourcidnum, sizeof(ourcidnum));
379  ast_set_callerid(chan, ourcidnum, ourcidname, ourcidnum);
380  }
381 
382  if (!ast_strlen_zero(acctcode)) {
383  ast_channel_lock(chan);
384  ast_channel_accountcode_set(chan, acctcode);
385  ast_channel_unlock(chan);
386  }
387 
388  if (ast_pbx_exec_application(chan, "ResetCDR", special_noanswer ? "" : "e")) {
389  ast_log(AST_LOG_NOTICE, "ResetCDR application not found; CDR will not be reset\n");
390  }
391  ast_explicit_goto(chan, args.context, exten, 1);
392  return 0;
393  }
394  }
395 
396  /* Received invalid, but no "i" extension exists in the given context */
397 
398 reorder:
399  /* Play congestion for a bit */
401  ast_safe_sleep(chan, 10*1000);
402 
403  ast_playtones_stop(chan);
404 
405  return -1;
406 }
407 
408 static int unload_module(void)
409 {
410  return ast_unregister_application(app);
411 }
412 
413 static int load_module(void)
414 {
415  return ast_register_application_xml(app, disa_exec) ?
417 }
418 
419 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "DISA (Direct Inward System Access) Application");
int ast_safe_sleep(struct ast_channel *chan, int ms)
Wait for a specified amount of time, looking for hangups.
Definition: channel.c:1574
Tone Indication Support.
void ast_set_callerid(struct ast_channel *chan, const char *cid_num, const char *cid_name, const char *cid_ani)
Set caller ID number, name and ANI and generate AMI event.
Definition: channel.c:7334
int ast_matchmore_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Looks to see if adding anything to this extension might match something. (exists ^ canmatch) ...
Definition: pbx.c:4195
Main Channel structure associated with a channel.
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
CallerID (and other GR30) management and generation Includes code and algorithms from the Zapata libr...
Support for translation of data formats. translate.c.
int ast_indicate(struct ast_channel *chan, int condition)
Indicates condition of channel.
Definition: channel.c:4277
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
void ast_channel_clear_flag(struct ast_channel *chan, unsigned int flag)
Clear a flag on a channel.
Definition: channel.c:11034
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4257
int ast_ignore_pattern(const char *context, const char *pattern)
Checks to see if a number should be ignored.
Definition: pbx.c:6879
ast_channel_state
ast_channel states
Definition: channelstate.h:35
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
int ast_explicit_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
Definition: pbx.c:6945
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:107
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
void ast_playtones_stop(struct ast_channel *chan)
Stop playing tones on a channel.
Definition: indications.c:393
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
struct ast_frame_subclass subclass
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
Number structure.
Definition: app_followme.c:154
u-Law to Signed linear conversion
General Asterisk PBX channel definitions.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define AST_MAX_EXTENSION
Definition: channel.h:134
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
Definition: strings.h:87
static struct ast_tone_zone_sound * ast_tone_zone_sound_unref(struct ast_tone_zone_sound *ts)
Release a reference to an ast_tone_zone_sound.
Definition: indications.h:227
#define ast_debug(level,...)
Log a DEBUG message.
int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Determine whether an extension exists.
Definition: pbx.c:4175
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:3066
Core PBX routines and definitions.
int ast_pbx_exec_application(struct ast_channel *chan, const char *app_name, const char *app_args)
Execute an application.
Definition: pbx_app.c:501
void ast_channel_set_flag(struct ast_channel *chan, unsigned int flag)
Set a flag on a channel.
Definition: channel.c:11027
Description of a tone.
Definition: indications.h:35
struct ast_tone_zone_sound * ast_get_indication_tone(const struct ast_tone_zone *zone, const char *indication)
Locate a tone zone sound.
Definition: indications.c:461
union ast_frame::@224 data
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
Structure used to handle boolean flags.
Definition: utils.h:199
int ast_app_has_voicemail(const char *mailboxes, const char *folder)
Determine if a given mailbox has any voicemail If folder is NULL, defaults to "INBOX". If folder is "INBOX", includes the number of messages in the "Urgent" folder.
Definition: main/app.c:582
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
int ast_waitfor(struct ast_channel *chan, int ms)
Wait for input on a channel.
Definition: channel.c:3162
int dtimeoutms
Definition: pbx.h:215
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2805
Data structure associated with a single frame of data.
int ast_playtones_start(struct ast_channel *chan, int vol, const char *tonelist, int interruptible)
Start playing a list of tones on a channel.
Definition: indications.c:302
int ast_tonepair_start(struct ast_channel *chan, int freq1, int freq2, int duration, int vol)
Definition: channel.c:7582
const char * data
Description of a tone.
Definition: indications.h:52
enum ast_frame_type frametype
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
#define AST_APP_ARG(name)
Define an application argument.
int rtimeoutms
Definition: pbx.h:216