Add XML documentation for the ForkCDR() application.
[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 necc. data.</para>
68                                         </option>
69                                         <option name="r">
70                                                 <para>Do <emphasis>NOT</emphasis> reset the new cdr.</para>
71                                         </option>
72                                         <option name="s(name=val)">
73                                                 <para>Set the CDR var <replaceable>name</replaceable> in the original CDR,
74                                                 with value <replaceable>val</replaceable></para>.
75                                         </option>
76                                         <option name="T">
77                                                 <para>Mark the original CDR with a DONT_TOUCH flag. setvar, answer, and end
78                                                 cdr funcs will obey this flag; normally they don't honor the LOCKED flag
79                                                 set on the original CDR record.</para>
80                                                 <note><para>Using this flag may cause CDR's not to have their end times
81                                                 updated! It is suggested that if you specify this flag, you might wish
82                                                 to use the <literal>e</literal> flag as well!.</para></note>
83                                         </option>
84                                         <option name="v">
85                                                 <para>When the new CDR is forked, it gets a copy of the vars attached to
86                                                 the current CDR. The vars attached to the original CDR are removed unless
87                                                 this option is specified.</para>
88                                         </option>
89                                 </optionlist>
90                         </parameter>
91                 </syntax>
92                 <description>
93                         <para> Causes the Call Data Record to fork an additional cdr record starting from the time
94                         of the fork call. This new cdr record will be linked to end of the list of cdr records attached
95                         to the channel. The original CDR has a LOCKED flag set, which forces most cdr operations to skip
96                         it, except for the functions that set the answer and end times, which ignore the LOCKED flag. This
97                         allows all the cdr records in the channel to be 'ended' together when the channel is closed.</para>
98                         <para>The CDR() func (when setting CDR values) normally ignores the LOCKED flag also, but has options
99                         to vary its behavior. The 'T' option (described below), can override this behavior, but beware
100                         the risks.</para>
101                         <para>First, this app finds the last cdr record in the list, and makes a copy of it. This new copy
102                         will be the newly forked cdr record. Next, this new record is linked to the end of the cdr record list.
103                         Next, The new cdr record is RESET (unless you use an option to prevent this)</para>
104                         <para>This means that:</para>
105                         <para>   1. All flags are unset on the cdr record</para>
106                         <para>   2. the start, end, and answer times are all set to zero.</para>
107                         <para>   3. the billsec and duration fields are set to zero.</para>
108                         <para>   4. the start time is set to the current time.</para>
109                         <para>   5. the disposition is set to NULL.</para>
110                         <para>Next, unless you specified the <literal>v</literal> option, all variables will be removed from
111                         the original cdr record. Thus, the <literal>v</literal> option allows any CDR variables to be replicated
112                         to all new forked cdr records. Without the <literal>v</literal> option, the variables on the original
113                         are effectively moved to the new forked cdr record.</para>
114                         <para>Next, if the <literal>s</literal> option is set, the provided variable and value are set on the
115                         original cdr record.</para>
116                         <para>Next, if the <literal>a</literal> option is given, and the original cdr record has an answer time
117                         set, then the new forked cdr record will have its answer time set to its start time. If the old answer
118                         time were carried forward, the answer time would be earlier than the start time, giving strange
119                         duration and billsec times.</para>
120                         <para>If the <literal>d</literal> option was specified, the disposition is copied from
121                         the original cdr record to the new forked cdr. If the <literal>D</literal> option was specified,
122                         the destination channel field in the new forked CDR is erased. If the <literal>e</literal> option
123                         was specified, the 'end' time for the original cdr record is set to the current time. Future hang-up or
124                         ending events will not override this time stamp. If the <literal>A</literal> option is specified,
125                         the original cdr record will have it ANS_LOCKED flag set, which prevent future answer events from updating
126                         the original cdr record's disposition. Normally, an <literal>ANSWERED</literal> event would mark all cdr
127                         records in the chain as <literal>ANSWERED</literal>. If the <literal>T</literal> option is specified,
128                         the original cdr record will have its <literal>DONT_TOUCH</literal> flag set, which will force the
129                         cdr_answer, cdr_end, and cdr_setvar functions to leave that cdr record alone.</para>
130                         <para>And, last but not least, the original cdr record has its LOCKED flag set. Almost all internal
131                         CDR functions (except for the funcs that set the end, and answer times, and set a variable) will honor
132                         this flag and leave a LOCKED cdr record alone. This means that the newly created forked cdr record
133                         will be affected by events transpiring within Asterisk, with the previously noted exceptions.</para>
134                 </description>
135                 <see-also>
136                         <ref type="function">CDR</ref>
137                         <ref type="application">NoCDR</ref>
138                         <ref type="application">ResetCDR</ref>
139                 </see-also>
140         </application>
141  ***/
142
143 static char *app = "ForkCDR";
144
145 enum {
146         OPT_SETANS =            (1 << 0),
147         OPT_SETDISP =           (1 << 1),
148         OPT_RESETDEST =         (1 << 2),
149         OPT_ENDCDR =            (1 << 3),
150         OPT_NORESET =           (1 << 4),
151         OPT_KEEPVARS =          (1 << 5),
152         OPT_VARSET =            (1 << 6),
153         OPT_ANSLOCK =           (1 << 7),
154         OPT_DONTOUCH =          (1 << 8),
155 };
156
157 enum {
158         OPT_ARG_VARSET = 0,
159         /* note: this entry _MUST_ be the last one in the enum */
160         OPT_ARG_ARRAY_SIZE,
161 };
162
163 AST_APP_OPTIONS(forkcdr_exec_options, {
164         AST_APP_OPTION('a', OPT_SETANS),
165         AST_APP_OPTION('A', OPT_ANSLOCK),
166         AST_APP_OPTION('d', OPT_SETDISP),
167         AST_APP_OPTION('D', OPT_RESETDEST),
168         AST_APP_OPTION('e', OPT_ENDCDR),
169         AST_APP_OPTION('R', OPT_NORESET),
170         AST_APP_OPTION_ARG('s', OPT_VARSET, OPT_ARG_VARSET),
171         AST_APP_OPTION('T', OPT_DONTOUCH),
172         AST_APP_OPTION('v', OPT_KEEPVARS),
173 });
174
175 static void ast_cdr_fork(struct ast_channel *chan, struct ast_flags optflags, char *set) 
176 {
177         struct ast_cdr *cdr;
178         struct ast_cdr *newcdr;
179         struct ast_flags flags = { AST_CDR_FLAG_KEEP_VARS };
180
181         cdr = chan->cdr;
182
183         while (cdr->next)
184                 cdr = cdr->next;
185         
186         if (!(newcdr = ast_cdr_dup(cdr)))
187                 return;
188         
189         ast_cdr_append(cdr, newcdr);
190
191         if (!ast_test_flag(&optflags, OPT_NORESET))
192                 ast_cdr_reset(newcdr, &flags);
193                 
194         if (!ast_test_flag(cdr, AST_CDR_FLAG_KEEP_VARS))
195                 ast_cdr_free_vars(cdr, 0);
196         
197         if (!ast_strlen_zero(set)) {
198                 char *varname = ast_strdupa(set), *varval;
199                 varval = strchr(varname,'=');
200                 if (varval) {
201                         *varval = 0;
202                         varval++;
203                         ast_cdr_setvar(cdr, varname, varval, 0);
204                 }
205         }
206         
207         if (ast_test_flag(&optflags, OPT_SETANS) && !ast_tvzero(cdr->answer))
208                 newcdr->answer = newcdr->start;
209
210         if (ast_test_flag(&optflags, OPT_SETDISP))
211                 newcdr->disposition = cdr->disposition;
212         
213         if (ast_test_flag(&optflags, OPT_RESETDEST))
214                 newcdr->dstchannel[0] = 0;
215         
216         if (ast_test_flag(&optflags, OPT_ENDCDR))
217                 ast_cdr_end(cdr);
218
219         if (ast_test_flag(&optflags, OPT_ANSLOCK))
220                 ast_set_flag(cdr, AST_CDR_FLAG_ANSLOCKED);
221         
222         if (ast_test_flag(&optflags, OPT_DONTOUCH))
223                 ast_set_flag(cdr, AST_CDR_FLAG_DONT_TOUCH);
224                 
225         ast_set_flag(cdr, AST_CDR_FLAG_CHILD | AST_CDR_FLAG_LOCKED);
226 }
227
228 static int forkcdr_exec(struct ast_channel *chan, void *data)
229 {
230         int res = 0;
231         char *argcopy = NULL;
232         struct ast_flags flags = {0};
233         char *opts[OPT_ARG_ARRAY_SIZE];
234         AST_DECLARE_APP_ARGS(arglist,
235                 AST_APP_ARG(options);
236         );
237
238         if (!chan->cdr) {
239                 ast_log(LOG_WARNING, "Channel does not have a CDR\n");
240                 return 0;
241         }
242
243         argcopy = ast_strdupa(data);
244
245         AST_STANDARD_APP_ARGS(arglist, argcopy);
246
247         opts[OPT_ARG_VARSET] = 0;
248
249         if (!ast_strlen_zero(arglist.options))
250                 ast_app_parse_options(forkcdr_exec_options, &flags, opts, arglist.options);
251
252         if (!ast_strlen_zero(data)) {
253                 int keepvars = ast_test_flag(&flags, OPT_KEEPVARS) ? 1 : 0;
254                 ast_set2_flag(chan->cdr, keepvars, AST_CDR_FLAG_KEEP_VARS);
255         }
256         
257         ast_cdr_fork(chan, flags, opts[OPT_ARG_VARSET]);
258
259         return res;
260 }
261
262 static int unload_module(void)
263 {
264         return ast_unregister_application(app);
265 }
266
267 static int load_module(void)
268 {
269         return ast_register_application_xml(app, forkcdr_exec);
270 }
271
272 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Fork The CDR into 2 separate entities");