codec_dahdi: Cannot use struct ast_translator.core_{src,src}_codec.
[asterisk/asterisk.git] / apps / app_disa.c
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 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
37
38 #include <math.h>
39 #include <sys/time.h>
40
41 #include "asterisk/lock.h"
42 #include "asterisk/file.h"
43 #include "asterisk/channel.h"
44 #include "asterisk/app.h"
45 #include "asterisk/indications.h"
46 #include "asterisk/pbx.h"
47 #include "asterisk/module.h"
48 #include "asterisk/translate.h"
49 #include "asterisk/ulaw.h"
50 #include "asterisk/callerid.h"
51 #include "asterisk/stringfields.h"
52
53 /*** DOCUMENTATION
54         <application name="DISA" language="en_US">
55                 <synopsis>
56                         Direct Inward System Access.
57                 </synopsis>
58                 <syntax>
59                         <parameter name="passcode|filename" required="true">
60                                 <para>If you need to present a DISA dialtone without entering a password,
61                                 simply set <replaceable>passcode</replaceable> to <literal>no-password</literal></para>
62                                 <para>You may specified a <replaceable>filename</replaceable> instead of a
63                                 <replaceable>passcode</replaceable>, this filename must contain individual passcodes</para>
64                         </parameter>
65                         <parameter name="context">
66                                 <para>Specifies the dialplan context in which the user-entered extension
67                                 will be matched. If no context is specified, the DISA application defaults
68                                 to the <literal>disa</literal> context. Presumably a normal system will have a special
69                                 context set up for DISA use with some or a lot of restrictions.</para>
70                         </parameter>
71                         <parameter name="cid">
72                                 <para>Specifies a new (different) callerid to be used for this call.</para>
73                         </parameter>
74                         <parameter name="mailbox" argsep="@">
75                                 <para>Will cause a stutter-dialtone (indication <emphasis>dialrecall</emphasis>)
76                                 to be used, if the specified mailbox contains any new messages.</para>
77                                 <argument name="mailbox" required="true" />
78                                 <argument name="context" required="false" />
79                         </parameter>
80                         <parameter name="options">
81                                 <optionlist>
82                                         <option name="n">
83                                                 <para>The DISA application will not answer initially.</para>
84                                         </option>
85                                         <option name="p">
86                                                 <para>The extension entered will be considered complete when a <literal>#</literal>
87                                                 is entered.</para>
88                                         </option>
89                                 </optionlist>
90                         </parameter>
91                 </syntax>
92                 <description>
93                         <para>The DISA, Direct Inward System Access, application allows someone from
94                         outside the telephone switch (PBX) to obtain an <emphasis>internal</emphasis> system
95                         dialtone and to place calls from it as if they were placing a call from
96                         within the switch.
97                         DISA plays a dialtone. The user enters their numeric passcode, followed by
98                         the pound sign <literal>#</literal>. If the passcode is correct, the user is then given
99                         system dialtone within <replaceable>context</replaceable> on which a call may be placed.
100                         If the user enters an invalid extension and extension <literal>i</literal> exists in the specified
101                         <replaceable>context</replaceable>, it will be used.
102                         </para>
103                         <para>Be aware that using this may compromise the security of your PBX.</para>
104                         <para>The arguments to this application (in <filename>extensions.conf</filename>) allow either
105                         specification of a single global <replaceable>passcode</replaceable> (that everyone uses), or
106                         individual passcodes contained in a file (<replaceable>filename</replaceable>).</para>
107                         <para>The file that contains the passcodes (if used) allows a complete
108                         specification of all of the same arguments available on the command
109                         line, with the sole exception of the options. The file may contain blank
110                         lines, or comments starting with <literal>#</literal> or <literal>;</literal>.</para>
111                 </description>
112                 <see-also>
113                         <ref type="application">Authenticate</ref>
114                         <ref type="application">VMAuthenticate</ref>
115                 </see-also>
116         </application>
117  ***/
118 static const char app[] = "DISA";
119
120 enum {
121         NOANSWER_FLAG = (1 << 0),
122         POUND_TO_END_FLAG = (1 << 1),
123 };
124
125 AST_APP_OPTIONS(app_opts, {
126         AST_APP_OPTION('n', NOANSWER_FLAG),
127         AST_APP_OPTION('p', POUND_TO_END_FLAG),
128 });
129
130 static void play_dialtone(struct ast_channel *chan, char *mailbox)
131 {
132         struct ast_tone_zone_sound *ts = NULL;
133
134         if (ast_app_has_voicemail(mailbox, NULL)) {
135                 ts = ast_get_indication_tone(ast_channel_zone(chan), "dialrecall");
136         } else {
137                 ts = ast_get_indication_tone(ast_channel_zone(chan), "dial");
138         }
139
140         if (ts) {
141                 ast_playtones_start(chan, 0, ts->data, 0);
142                 ts = ast_tone_zone_sound_unref(ts);
143         } else {
144                 ast_tonepair_start(chan, 350, 440, 0, 0);
145         }
146 }
147
148 static int disa_exec(struct ast_channel *chan, const char *data)
149 {
150         int i = 0, j, k = 0, did_ignore = 0, special_noanswer = 0;
151         int firstdigittimeout = (ast_channel_pbx(chan) ? ast_channel_pbx(chan)->rtimeoutms : 20000);
152         int digittimeout = (ast_channel_pbx(chan) ? ast_channel_pbx(chan)->dtimeoutms : 10000);
153         struct ast_flags flags;
154         char *tmp, exten[AST_MAX_EXTENSION] = "", acctcode[20]="";
155         char pwline[256];
156         char ourcidname[256],ourcidnum[256];
157         struct ast_frame *f;
158         struct timeval lastdigittime;
159         int res;
160         FILE *fp;
161         AST_DECLARE_APP_ARGS(args,
162                 AST_APP_ARG(passcode);
163                 AST_APP_ARG(context);
164                 AST_APP_ARG(cid);
165                 AST_APP_ARG(mailbox);
166                 AST_APP_ARG(options);
167         );
168
169         if (ast_strlen_zero(data)) {
170                 ast_log(LOG_WARNING, "DISA requires an argument (passcode/passcode file)\n");
171                 return -1;
172         }
173
174         ast_debug(1, "Digittimeout: %d\n", digittimeout);
175         ast_debug(1, "Responsetimeout: %d\n", firstdigittimeout);
176
177         tmp = ast_strdupa(data);
178
179         AST_STANDARD_APP_ARGS(args, tmp);
180
181         if (ast_strlen_zero(args.context))
182                 args.context = "disa";
183         if (ast_strlen_zero(args.mailbox))
184                 args.mailbox = "";
185         if (!ast_strlen_zero(args.options)) {
186                 ast_app_parse_options(app_opts, &flags, NULL, args.options);
187         } else {
188                 /* Coverity - This uninit_use should be ignored since this macro initializes the flags */
189                 ast_clear_flag(&flags, AST_FLAGS_ALL);
190         }
191
192
193         ast_debug(1, "Mailbox: %s\n",args.mailbox);
194
195         if (!ast_test_flag(&flags, NOANSWER_FLAG)) {
196                 if (ast_channel_state(chan) != AST_STATE_UP) {
197                         /* answer */
198                         ast_answer(chan);
199                 }
200         } else special_noanswer = 1;
201
202         ast_debug(1, "Context: %s\n",args.context);
203
204         if (!strcasecmp(args.passcode, "no-password")) {
205                 k |= 1; /* We have the password */
206                 ast_debug(1, "DISA no-password login success\n");
207         }
208
209         lastdigittime = ast_tvnow();
210
211         play_dialtone(chan, args.mailbox);
212
213         ast_set_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
214
215         for (;;) {
216                   /* if outa time, give em reorder */
217                 if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > ((k&2) ? digittimeout : firstdigittimeout)) {
218                         ast_debug(1,"DISA %s entry timeout on chan %s\n",
219                                 ((k&1) ? "extension" : "password"),ast_channel_name(chan));
220                         break;
221                 }
222
223                 if ((res = ast_waitfor(chan, -1)) < 0) {
224                         ast_debug(1, "Waitfor returned %d\n", res);
225                         continue;
226                 }
227
228                 if (!(f = ast_read(chan))) {
229                         ast_clear_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
230                         return -1;
231                 }
232
233                 if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP)) {
234                         if (f->data.uint32)
235                                 ast_channel_hangupcause_set(chan, f->data.uint32);
236                         ast_frfree(f);
237                         ast_clear_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
238                         return -1;
239                 }
240
241                 /* If the frame coming in is not DTMF, just drop it and continue */
242                 if (f->frametype != AST_FRAME_DTMF) {
243                         ast_frfree(f);
244                         continue;
245                 }
246
247                 j = f->subclass.integer;  /* save digit */
248                 ast_frfree(f);
249
250                 if (!i) {
251                         k |= 2; /* We have the first digit */
252                         ast_playtones_stop(chan);
253                 }
254
255                 lastdigittime = ast_tvnow();
256
257                 /* got a DTMF tone */
258                 if (i < AST_MAX_EXTENSION) { /* if still valid number of digits */
259                         if (!(k&1)) { /* if in password state */
260                                 if (j == '#') { /* end of password */
261                                           /* see if this is an integer */
262                                         if (sscanf(args.passcode,"%30d",&j) < 1) { /* nope, it must be a filename */
263                                                 fp = fopen(args.passcode,"r");
264                                                 if (!fp) {
265                                                         ast_log(LOG_WARNING,"DISA password file %s not found on chan %s\n",args.passcode,ast_channel_name(chan));
266                                                         ast_clear_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
267                                                         return -1;
268                                                 }
269                                                 pwline[0] = 0;
270                                                 while(fgets(pwline,sizeof(pwline) - 1,fp)) {
271                                                         if (!pwline[0])
272                                                                 continue;
273                                                         if (pwline[strlen(pwline) - 1] == '\n')
274                                                                 pwline[strlen(pwline) - 1] = 0;
275                                                         if (!pwline[0])
276                                                                 continue;
277                                                          /* skip comments */
278                                                         if (pwline[0] == '#')
279                                                                 continue;
280                                                         if (pwline[0] == ';')
281                                                                 continue;
282
283                                                         AST_STANDARD_APP_ARGS(args, pwline);
284
285                                                         ast_debug(1, "Mailbox: %s\n",args.mailbox);
286
287                                                         /* password must be in valid format (numeric) */
288                                                         if (sscanf(args.passcode,"%30d", &j) < 1)
289                                                                 continue;
290                                                          /* if we got it */
291                                                         if (!strcmp(exten,args.passcode)) {
292                                                                 if (ast_strlen_zero(args.context))
293                                                                         args.context = "disa";
294                                                                 if (ast_strlen_zero(args.mailbox))
295                                                                         args.mailbox = "";
296                                                                 break;
297                                                         }
298                                                 }
299                                                 fclose(fp);
300                                         }
301                                         /* compare the two */
302                                         if (strcmp(exten,args.passcode)) {
303                                                 ast_log(LOG_WARNING,"DISA on chan %s got bad password %s\n",ast_channel_name(chan),exten);
304                                                 goto reorder;
305
306                                         }
307                                          /* password good, set to dial state */
308                                         ast_debug(1,"DISA on chan %s password is good\n",ast_channel_name(chan));
309                                         play_dialtone(chan, args.mailbox);
310
311                                         k|=1; /* In number mode */
312                                         i = 0;  /* re-set buffer pointer */
313                                         exten[sizeof(acctcode)] = 0;
314                                         ast_copy_string(acctcode, exten, sizeof(acctcode));
315                                         exten[0] = 0;
316                                         ast_debug(1,"Successful DISA log-in on chan %s\n", ast_channel_name(chan));
317                                         continue;
318                                 }
319                         } else {
320                                 if (j == '#') { /* end of extension .. maybe */
321                                         if (i == 0
322                                                 && (ast_matchmore_extension(chan, args.context, "#", 1,
323                                                         S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))
324                                                         || ast_exists_extension(chan, args.context, "#", 1,
325                                                                 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) ) {
326                                                 /* Let the # be the part of, or the entire extension */
327                                         } else {
328                                                 break;
329                                         }
330                                 }
331                         }
332
333                         exten[i++] = j;  /* save digit */
334                         exten[i] = 0;
335                         if (!(k&1))
336                                 continue; /* if getting password, continue doing it */
337                         /* if this exists */
338
339                         /* user wants end of number, remove # */
340                         if (ast_test_flag(&flags, POUND_TO_END_FLAG) && j == '#') {
341                                 exten[--i] = 0;
342                                 break;
343                         }
344
345                         if (ast_ignore_pattern(args.context, exten)) {
346                                 play_dialtone(chan, "");
347                                 did_ignore = 1;
348                         } else
349                                 if (did_ignore) {
350                                         ast_playtones_stop(chan);
351                                         did_ignore = 0;
352                                 }
353
354                         /* if can do some more, do it */
355                         if (!ast_matchmore_extension(chan, args.context, exten, 1,
356                                 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
357                                 break;
358                         }
359                 }
360         }
361
362         ast_clear_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
363
364         if (k == 3) {
365                 int recheck = 0;
366                 struct ast_app *app_reset_cdr;
367
368                 if (!ast_exists_extension(chan, args.context, exten, 1,
369                         S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
370                         pbx_builtin_setvar_helper(chan, "INVALID_EXTEN", exten);
371                         exten[0] = 'i';
372                         exten[1] = '\0';
373                         recheck = 1;
374                 }
375                 if (!recheck
376                         || ast_exists_extension(chan, args.context, exten, 1,
377                                 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
378                         ast_playtones_stop(chan);
379                         /* We're authenticated and have a target extension */
380                         if (!ast_strlen_zero(args.cid)) {
381                                 ast_callerid_split(args.cid, ourcidname, sizeof(ourcidname), ourcidnum, sizeof(ourcidnum));
382                                 ast_set_callerid(chan, ourcidnum, ourcidname, ourcidnum);
383                         }
384
385                         if (!ast_strlen_zero(acctcode)) {
386                                 ast_channel_lock(chan);
387                                 ast_channel_accountcode_set(chan, acctcode);
388                                 ast_channel_unlock(chan);
389                         }
390
391                         app_reset_cdr = pbx_findapp("ResetCDR");
392                         if (app_reset_cdr) {
393                                 pbx_exec(chan, app_reset_cdr, special_noanswer ? "" : "e");
394                         } else {
395                                 ast_log(AST_LOG_NOTICE, "ResetCDR application not found; CDR will not be reset\n");
396                         }
397                         ast_explicit_goto(chan, args.context, exten, 1);
398                         return 0;
399                 }
400         }
401
402         /* Received invalid, but no "i" extension exists in the given context */
403
404 reorder:
405         /* Play congestion for a bit */
406         ast_indicate(chan, AST_CONTROL_CONGESTION);
407         ast_safe_sleep(chan, 10*1000);
408
409         ast_playtones_stop(chan);
410
411         return -1;
412 }
413
414 static int unload_module(void)
415 {
416         return ast_unregister_application(app);
417 }
418
419 static int load_module(void)
420 {
421         return ast_register_application_xml(app, disa_exec) ?
422                 AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS;
423 }
424
425 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "DISA (Direct Inward System Access) Application");