remove almost all of the checks of the result from ast_strdupa() or alloca().
[asterisk/asterisk.git] / apps / app_privacy.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Block all calls without Caller*ID, require phone # to be entered
22  *
23  * \author Mark Spencer <markster@digium.com>
24  * 
25  * \ingroup applications
26  */
27
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31
32 #include "asterisk.h"
33
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
35
36 #include "asterisk/lock.h"
37 #include "asterisk/file.h"
38 #include "asterisk/utils.h"
39 #include "asterisk/logger.h"
40 #include "asterisk/options.h"
41 #include "asterisk/channel.h"
42 #include "asterisk/pbx.h"
43 #include "asterisk/module.h"
44 #include "asterisk/translate.h"
45 #include "asterisk/image.h"
46 #include "asterisk/callerid.h"
47 #include "asterisk/app.h"
48 #include "asterisk/config.h"
49
50 #define PRIV_CONFIG "privacy.conf"
51
52 static char *tdesc = "Require phone number to be entered, if no CallerID sent";
53
54 static char *app = "PrivacyManager";
55
56 static char *synopsis = "Require phone number to be entered, if no CallerID sent";
57
58 static char *descrip =
59   "  PrivacyManager([maxretries[|minlength[|options]]]): If no Caller*ID \n"
60   "is sent, PrivacyManager answers the channel and asks the caller to\n"
61   "enter their phone number. The caller is given 3 attempts to do so.\n"
62   "The application does nothing if Caller*ID was received on the channel.\n"
63   "  Configuration file privacy.conf contains two variables:\n"
64   "   maxretries  default 3  -maximum number of attempts the caller is allowed \n"
65   "               to input a callerid.\n"
66   "   minlength   default 10 -minimum allowable digits in the input callerid number.\n"
67   "If you don't want to use the config file and have an i/o operation with\n"
68   "every call, you can also specify maxretries and minlength as application\n"
69   "parameters. Doing so supercedes any values set in privacy.conf.\n"
70   "The option string may contain the following character: \n"
71   "  'j' -- jump to n+101 priority after <maxretries> failed attempts to collect\n"
72   "         the minlength number of digits.\n"
73   "The application sets the following channel variable upon completion: \n"
74   "PRIVACYMGRSTATUS  The status of the privacy manager's attempt to collect \n"
75   "                  a phone number from the user. A text string that is either:\n" 
76   "          SUCCESS | FAILED \n"
77 ;
78
79 LOCAL_USER_DECL;
80
81 static int privacy_exec (struct ast_channel *chan, void *data)
82 {
83         int res=0;
84         int retries;
85         int maxretries = 3;
86         int minlength = 10;
87         int x = 0;
88         char *s;
89         char phone[30];
90         struct localuser *u;
91         struct ast_config *cfg = NULL;
92         char *parse = NULL;
93         int priority_jump = 0;
94         AST_DECLARE_APP_ARGS(args,
95                 AST_APP_ARG(maxretries);
96                 AST_APP_ARG(minlength);
97                 AST_APP_ARG(options);
98         );
99
100         LOCAL_USER_ADD (u);
101         if (!ast_strlen_zero(chan->cid.cid_num)) {
102                 if (option_verbose > 2)
103                         ast_verbose (VERBOSE_PREFIX_3 "CallerID Present: Skipping\n");
104         } else {
105                 /*Answer the channel if it is not already*/
106                 if (chan->_state != AST_STATE_UP) {
107                         res = ast_answer(chan);
108                         if (res) {
109                                 LOCAL_USER_REMOVE(u);
110                                 return -1;
111                         }
112                 }
113
114                 if (!ast_strlen_zero((char *)data))
115                 {
116                         parse = ast_strdupa(data);
117                         
118                         AST_STANDARD_APP_ARGS(args, parse);
119
120                         if (args.maxretries) {
121                                 if (sscanf(args.maxretries, "%d", &x) == 1)
122                                         maxretries = x;
123                                 else
124                                         ast_log(LOG_WARNING, "Invalid max retries argument\n");
125                         }
126                         if (args.minlength) {
127                                 if (sscanf(args.minlength, "%d", &x) == 1)
128                                         minlength = x;
129                                 else
130                                         ast_log(LOG_WARNING, "Invalid min length argument\n");
131                         }
132                         if (args.options)
133                                 if (strchr(args.options, 'j'))
134                                         priority_jump = 1;
135
136                 }               
137
138                 if (!x)
139                 {
140                         /*Read in the config file*/
141                         cfg = ast_config_load(PRIV_CONFIG);
142                 
143                         if (cfg && (s = ast_variable_retrieve(cfg, "general", "maxretries"))) {
144                                 if (sscanf(s, "%d", &x) == 1) 
145                                         maxretries = x;
146                                 else
147                                         ast_log(LOG_WARNING, "Invalid max retries argument\n");
148                         }
149
150                         if (cfg && (s = ast_variable_retrieve(cfg, "general", "minlength"))) {
151                                 if (sscanf(s, "%d", &x) == 1) 
152                                         minlength = x;
153                                 else
154                                         ast_log(LOG_WARNING, "Invalid min length argument\n");
155                         }
156                 }       
157                 
158                 /*Play unidentified call*/
159                 res = ast_safe_sleep(chan, 1000);
160                 if (!res)
161                         res = ast_streamfile(chan, "privacy-unident", chan->language);
162                 if (!res)
163                         res = ast_waitstream(chan, "");
164
165                 /*Ask for 10 digit number, give 3 attempts*/
166                 for (retries = 0; retries < maxretries; retries++) {
167                         if (!res)
168                                 res = ast_streamfile(chan, "privacy-prompt", chan->language);
169                         if (!res)
170                                 res = ast_waitstream(chan, "");
171
172                         if (!res ) 
173                                 res = ast_readstring(chan, phone, sizeof(phone) - 1, /* digit timeout ms */ 3200, /* first digit timeout */ 5000, "#");
174
175                         if (res < 0)
176                                 break;
177
178                         /*Make sure we get at least digits*/
179                         if (strlen(phone) >= minlength ) 
180                                 break;
181                         else {
182                                 res = ast_streamfile(chan, "privacy-incorrect", chan->language);
183                                 if (!res)
184                                         res = ast_waitstream(chan, "");
185                         }
186                 }
187                 
188                 /*Got a number, play sounds and send them on their way*/
189                 if ((retries < maxretries) && res >= 0 ) {
190                         res = ast_streamfile(chan, "privacy-thankyou", chan->language);
191                         if (!res)
192                                 res = ast_waitstream(chan, "");
193
194                         ast_set_callerid (chan, phone, "Privacy Manager", NULL); 
195
196                         /* Clear the unavailable presence bit so if it came in on PRI
197                          * the caller id will now be passed out to other channels
198                          */
199                         chan->cid.cid_pres &= (AST_PRES_UNAVAILABLE ^ 0xFF);
200
201                         if (option_verbose > 2) {
202                                 ast_verbose (VERBOSE_PREFIX_3 "Changed Caller*ID to %s, callerpres to %d\n",phone,chan->cid.cid_pres);
203                         }
204                         pbx_builtin_setvar_helper(chan, "PRIVACYMGRSTATUS", "SUCCESS");
205                 } else {
206                         if (priority_jump || ast_opt_priority_jumping)  
207                                 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
208                         pbx_builtin_setvar_helper(chan, "PRIVACYMGRSTATUS", "FAILED");
209                 }
210                 if (cfg) 
211                         ast_config_destroy(cfg);
212         }
213
214   LOCAL_USER_REMOVE (u);
215   return 0;
216 }
217
218 static int unload_module(void *mod)
219 {
220         int res;
221
222         res = ast_unregister_application (app);
223
224         STANDARD_HANGUP_LOCALUSERS;
225
226         return res;
227 }
228
229 static int load_module(void *mod)
230 {
231         return ast_register_application (app, privacy_exec, synopsis, descrip);
232 }
233
234 static const char *description(void)
235 {
236         return tdesc;
237 }
238
239 static const char *key(void)
240 {
241         return ASTERISK_GPL_KEY;
242 }
243
244 STD_MOD1;