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