Commit documentation changes related to issue #14801.
[asterisk/asterisk.git] / apps / app_forkcdr.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Anthony Minessale anthmct@yahoo.com
5  * Development of this app Sponsered/Funded  by TAAN Softworks Corp
6  *
7  * See http://www.asterisk.org for more information about
8  * the Asterisk project. Please do not directly contact
9  * any of the maintainers of this project for assistance;
10  * the project provides a web site, mailing lists and IRC
11  * channels for your use.
12  *
13  * This program is free software, distributed under the terms of
14  * the GNU General Public License Version 2. See the LICENSE file
15  * at the top of the source tree.
16  */
17
18 /*! \file
19  *
20  * \brief Fork CDR application
21  *
22  * \author Anthony Minessale anthmct@yahoo.com
23  *
24  * \note Development of this app Sponsored/Funded by TAAN Softworks Corp
25  * 
26  * \ingroup applications
27  */
28
29 #include "asterisk.h"
30
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32
33 #include "asterisk/file.h"
34 #include "asterisk/channel.h"
35 #include "asterisk/pbx.h"
36 #include "asterisk/cdr.h"
37 #include "asterisk/app.h"
38 #include "asterisk/module.h"
39
40 /*** DOCUMENTATION
41         <application name="ForkCDR" language="en_US">
42                 <synopsis>
43                         Forks the Call Data Record.
44                 </synopsis>
45                 <syntax>
46                         <parameter name="options">
47                                 <optionlist>
48                                         <option name="a">
49                                                 <para>Update the answer time on the NEW CDR just after it's been inited.
50                                                 The new CDR may have been answered already. The reset that forkcdr does
51                                                 will erase the answer time. This will bring it back, but the answer time
52                                                 will be a copy of the fork/start time. It will only do this if the initial
53                                                 cdr was indeed already answered.</para>
54                                         </option>
55                                         <option name="A">
56                                                 <para>Lock the original CDR against the answer time being updated. This
57                                                 will allow the disposition on the original CDR to remain the same.</para>
58                                         </option>
59                                         <option name="d">
60                                                 <para>Copy the disposition forward from the old cdr, after the init.</para>
61                                         </option>
62                                         <option name="D">
63                                                 <para>Clear the <literal>dstchannel</literal> on the new CDR after
64                                                 reset.</para>
65                                         </option>
66                                         <option name="e">
67                                                 <para>End the original CDR. Do this after all the neccessry data is copied
68                                                 from the original CDR to the new forked CDR.</para>
69                                         </option>
70                                         <option name="r">
71                                                 <para>Do <emphasis>NOT</emphasis> reset the new cdr.</para>
72                                         </option>
73                                         <option name="s(name=val)">
74                                                 <para>Set the CDR var <replaceable>name</replaceable> in the original CDR,
75                                                 with value <replaceable>val</replaceable>.</para>
76                                         </option>
77                                         <option name="T">
78                                                 <para>Mark the original CDR with a DONT_TOUCH flag. setvar, answer, and end
79                                                 cdr funcs will obey this flag; normally they don't honor the LOCKED flag
80                                                 set on the original CDR record.</para>
81                                                 <note><para>Using this flag may cause CDR's not to have their end times
82                                                 updated! It is suggested that if you specify this flag, you might wish
83                                                 to use the <literal>e</literal> flag as well!.</para></note>
84                                         </option>
85                                         <option name="v">
86                                                 <para>When the new CDR is forked, it gets a copy of the vars attached to
87                                                 the current CDR. The vars attached to the original CDR are removed unless
88                                                 this option is specified.</para>
89                                         </option>
90                                 </optionlist>
91                         </parameter>
92                 </syntax>
93                 <description>
94                         <para> Causes the Call Data Record to fork an additional cdr record starting from the time
95                         of the fork call. This new cdr record will be linked to end of the list of cdr records attached
96                         to the channel. The original CDR has a LOCKED flag set, which forces most cdr operations to skip
97                         it, except for the functions that set the answer and end times, which ignore the LOCKED flag. This
98                         allows all the cdr records in the channel to be 'ended' together when the channel is closed.</para>
99                         <para>The CDR() func (when setting CDR values) normally ignores the LOCKED flag also, but has options
100                         to vary its behavior. The 'T' option (described below), can override this behavior, but beware
101                         the risks.</para>
102                         <para>First, this app finds the last cdr record in the list, and makes a copy of it. This new copy
103                         will be the newly forked cdr record. Next, this new record is linked to the end of the cdr record list.
104                         Next, The new cdr record is RESET (unless you use an option to prevent this)</para>
105                         <para>This means that:</para>
106                         <para>   1. All flags are unset on the cdr record</para>
107                         <para>   2. the start, end, and answer times are all set to zero.</para>
108                         <para>   3. the billsec and duration fields are set to zero.</para>
109                         <para>   4. the start time is set to the current time.</para>
110                         <para>   5. the disposition is set to NULL.</para>
111                         <para>Next, unless you specified the <literal>v</literal> option, all variables will be removed from
112                         the original cdr record. Thus, the <literal>v</literal> option allows any CDR variables to be replicated
113                         to all new forked cdr records. Without the <literal>v</literal> option, the variables on the original
114                         are effectively moved to the new forked cdr record.</para>
115                         <para>Next, if the <literal>s</literal> option is set, the provided variable and value are set on the
116                         original cdr record.</para>
117                         <para>Next, if the <literal>a</literal> option is given, and the original cdr record has an answer time
118                         set, then the new forked cdr record will have its answer time set to its start time. If the old answer
119                         time were carried forward, the answer time would be earlier than the start time, giving strange
120                         duration and billsec times.</para>
121                         <para>If the <literal>d</literal> option was specified, the disposition is copied from
122                         the original cdr record to the new forked cdr. If the <literal>D</literal> option was specified,
123                         the destination channel field in the new forked CDR is erased. If the <literal>e</literal> option
124                         was specified, the 'end' time for the original cdr record is set to the current time. Future hang-up or
125                         ending events will not override this time stamp. If the <literal>A</literal> option is specified,
126                         the original cdr record will have it ANS_LOCKED flag set, which prevent future answer events from updating
127                         the original cdr record's disposition. Normally, an <literal>ANSWERED</literal> event would mark all cdr
128                         records in the chain as <literal>ANSWERED</literal>. If the <literal>T</literal> option is specified,
129                         the original cdr record will have its <literal>DONT_TOUCH</literal> flag set, which will force the
130                         cdr_answer, cdr_end, and cdr_setvar functions to leave that cdr record alone.</para>
131                         <para>And, last but not least, the original cdr record has its LOCKED flag set. Almost all internal
132                         CDR functions (except for the funcs that set the end, and answer times, and set a variable) will honor
133                         this flag and leave a LOCKED cdr record alone. This means that the newly created forked cdr record
134                         will be affected by events transpiring within Asterisk, with the previously noted exceptions.</para>
135                 </description>
136                 <see-also>
137                         <ref type="function">CDR</ref>
138                         <ref type="application">NoCDR</ref>
139                         <ref type="application">ResetCDR</ref>
140                 </see-also>
141         </application>
142  ***/
143
144 static char *app = "ForkCDR";
145
146 enum {
147         OPT_SETANS =            (1 << 0),
148         OPT_SETDISP =           (1 << 1),
149         OPT_RESETDEST =         (1 << 2),
150         OPT_ENDCDR =            (1 << 3),
151         OPT_NORESET =           (1 << 4),
152         OPT_KEEPVARS =          (1 << 5),
153         OPT_VARSET =            (1 << 6),
154         OPT_ANSLOCK =           (1 << 7),
155         OPT_DONTOUCH =          (1 << 8),
156 };
157
158 enum {
159         OPT_ARG_VARSET = 0,
160         /* note: this entry _MUST_ be the last one in the enum */
161         OPT_ARG_ARRAY_SIZE,
162 };
163
164 AST_APP_OPTIONS(forkcdr_exec_options, {
165         AST_APP_OPTION('a', OPT_SETANS),
166         AST_APP_OPTION('A', OPT_ANSLOCK),
167         AST_APP_OPTION('d', OPT_SETDISP),
168         AST_APP_OPTION('D', OPT_RESETDEST),
169         AST_APP_OPTION('e', OPT_ENDCDR),
170         AST_APP_OPTION('R', OPT_NORESET),
171         AST_APP_OPTION_ARG('s', OPT_VARSET, OPT_ARG_VARSET),
172         AST_APP_OPTION('T', OPT_DONTOUCH),
173         AST_APP_OPTION('v', OPT_KEEPVARS),
174 });
175
176 static void ast_cdr_fork(struct ast_channel *chan, struct ast_flags optflags, char *set) 
177 {
178         struct ast_cdr *cdr;
179         struct ast_cdr *newcdr;
180         struct ast_flags flags = { AST_CDR_FLAG_KEEP_VARS };
181
182         cdr = chan->cdr;
183
184         while (cdr->next)
185                 cdr = cdr->next;
186         
187         if (!(newcdr = ast_cdr_dup(cdr)))
188                 return;
189         
190         ast_cdr_append(cdr, newcdr);
191
192         if (!ast_test_flag(&optflags, OPT_NORESET))
193                 ast_cdr_reset(newcdr, &flags);
194                 
195         if (!ast_test_flag(cdr, AST_CDR_FLAG_KEEP_VARS))
196                 ast_cdr_free_vars(cdr, 0);
197         
198         if (!ast_strlen_zero(set)) {
199                 char *varname = ast_strdupa(set), *varval;
200                 varval = strchr(varname,'=');
201                 if (varval) {
202                         *varval = 0;
203                         varval++;
204                         ast_cdr_setvar(cdr, varname, varval, 0);
205                 }
206         }
207         
208         if (ast_test_flag(&optflags, OPT_SETANS) && !ast_tvzero(cdr->answer))
209                 newcdr->answer = newcdr->start;
210
211         if (ast_test_flag(&optflags, OPT_SETDISP))
212                 newcdr->disposition = cdr->disposition;
213         
214         if (ast_test_flag(&optflags, OPT_RESETDEST))
215                 newcdr->dstchannel[0] = 0;
216         
217         if (ast_test_flag(&optflags, OPT_ENDCDR))
218                 ast_cdr_end(cdr);
219
220         if (ast_test_flag(&optflags, OPT_ANSLOCK))
221                 ast_set_flag(cdr, AST_CDR_FLAG_ANSLOCKED);
222         
223         if (ast_test_flag(&optflags, OPT_DONTOUCH))
224                 ast_set_flag(cdr, AST_CDR_FLAG_DONT_TOUCH);
225                 
226         ast_set_flag(cdr, AST_CDR_FLAG_CHILD | AST_CDR_FLAG_LOCKED);
227 }
228
229 static int forkcdr_exec(struct ast_channel *chan, void *data)
230 {
231         int res = 0;
232         char *argcopy = NULL;
233         struct ast_flags flags = {0};
234         char *opts[OPT_ARG_ARRAY_SIZE];
235         AST_DECLARE_APP_ARGS(arglist,
236                 AST_APP_ARG(options);
237         );
238
239         if (!chan->cdr) {
240                 ast_log(LOG_WARNING, "Channel does not have a CDR\n");
241                 return 0;
242         }
243
244         argcopy = ast_strdupa(data);
245
246         AST_STANDARD_APP_ARGS(arglist, argcopy);
247
248         opts[OPT_ARG_VARSET] = 0;
249
250         if (!ast_strlen_zero(arglist.options))
251                 ast_app_parse_options(forkcdr_exec_options, &flags, opts, arglist.options);
252
253         if (!ast_strlen_zero(data)) {
254                 int keepvars = ast_test_flag(&flags, OPT_KEEPVARS) ? 1 : 0;
255                 ast_set2_flag(chan->cdr, keepvars, AST_CDR_FLAG_KEEP_VARS);
256         }
257         
258         ast_cdr_fork(chan, flags, opts[OPT_ARG_VARSET]);
259
260         return res;
261 }
262
263 static int unload_module(void)
264 {
265         return ast_unregister_application(app);
266 }
267
268 static int load_module(void)
269 {
270         return ast_register_application_xml(app, forkcdr_exec);
271 }
272
273 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Fork The CDR into 2 separate entities");