2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Anthony Minessale anthmct@yahoo.com
5 * Development of this app Sponsered/Funded by TAAN Softworks Corp
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.
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.
20 * \brief Fork CDR application
22 * \author Anthony Minessale anthmct@yahoo.com
24 * \note Development of this app Sponsored/Funded by TAAN Softworks Corp
26 * \ingroup applications
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
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"
41 <application name="ForkCDR" language="en_US">
43 Forks the Call Data Record.
46 <parameter name="options">
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>
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>
60 <para>Copy the disposition forward from the old cdr, after the init.</para>
63 <para>Clear the <literal>dstchannel</literal> on the new CDR after
67 <para>End the original CDR. Do this after all the necc. data.</para>
70 <para>Do <emphasis>NOT</emphasis> reset the new cdr.</para>
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>.
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>
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>
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
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>
136 <ref type="function">CDR</ref>
137 <ref type="application">NoCDR</ref>
138 <ref type="application">ResetCDR</ref>
143 static char *app = "ForkCDR";
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),
159 /* note: this entry _MUST_ be the last one in the enum */
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),
175 static void ast_cdr_fork(struct ast_channel *chan, struct ast_flags optflags, char *set)
178 struct ast_cdr *newcdr;
179 struct ast_flags flags = { AST_CDR_FLAG_KEEP_VARS };
186 if (!(newcdr = ast_cdr_dup(cdr)))
189 ast_cdr_append(cdr, newcdr);
191 if (!ast_test_flag(&optflags, OPT_NORESET))
192 ast_cdr_reset(newcdr, &flags);
194 if (!ast_test_flag(cdr, AST_CDR_FLAG_KEEP_VARS))
195 ast_cdr_free_vars(cdr, 0);
197 if (!ast_strlen_zero(set)) {
198 char *varname = ast_strdupa(set), *varval;
199 varval = strchr(varname,'=');
203 ast_cdr_setvar(cdr, varname, varval, 0);
207 if (ast_test_flag(&optflags, OPT_SETANS) && !ast_tvzero(cdr->answer))
208 newcdr->answer = newcdr->start;
210 if (ast_test_flag(&optflags, OPT_SETDISP))
211 newcdr->disposition = cdr->disposition;
213 if (ast_test_flag(&optflags, OPT_RESETDEST))
214 newcdr->dstchannel[0] = 0;
216 if (ast_test_flag(&optflags, OPT_ENDCDR))
219 if (ast_test_flag(&optflags, OPT_ANSLOCK))
220 ast_set_flag(cdr, AST_CDR_FLAG_ANSLOCKED);
222 if (ast_test_flag(&optflags, OPT_DONTOUCH))
223 ast_set_flag(cdr, AST_CDR_FLAG_DONT_TOUCH);
225 ast_set_flag(cdr, AST_CDR_FLAG_CHILD | AST_CDR_FLAG_LOCKED);
228 static int forkcdr_exec(struct ast_channel *chan, void *data)
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);
239 ast_log(LOG_WARNING, "Channel does not have a CDR\n");
243 argcopy = ast_strdupa(data);
245 AST_STANDARD_APP_ARGS(arglist, argcopy);
247 opts[OPT_ARG_VARSET] = 0;
249 if (!ast_strlen_zero(arglist.options))
250 ast_app_parse_options(forkcdr_exec_options, &flags, opts, arglist.options);
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);
257 ast_cdr_fork(chan, flags, opts[OPT_ARG_VARSET]);
262 static int unload_module(void)
264 return ast_unregister_application(app);
267 static int load_module(void)
269 return ast_register_application_xml(app, forkcdr_exec);
272 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Fork The CDR into 2 separate entities");