app_cdr,app_forkcdr,func_cdr: Synchronize with engine when manipulating state
[asterisk/asterisk.git] / funcs / func_cdr.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999-2006, Digium, Inc.
5  *
6  * Portions Copyright (C) 2005, Anthony Minessale II
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  Call Detail Record related dialplan functions
22  *
23  * \author Anthony Minessale II
24  *
25  * \ingroup functions
26  */
27
28 /*** MODULEINFO
29         <support_level>core</support_level>
30  ***/
31
32 #include "asterisk.h"
33
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
35
36 #include "asterisk/module.h"
37 #include "asterisk/channel.h"
38 #include "asterisk/pbx.h"
39 #include "asterisk/utils.h"
40 #include "asterisk/app.h"
41 #include "asterisk/cdr.h"
42 #include "asterisk/stasis.h"
43
44 /*** DOCUMENTATION
45         <function name="CDR" language="en_US">
46                 <synopsis>
47                         Gets or sets a CDR variable.
48                 </synopsis>
49                 <syntax>
50                         <parameter name="name" required="true">
51                                 <para>CDR field name:</para>
52                                 <enumlist>
53                                         <enum name="clid">
54                                                 <para>Caller ID.</para>
55                                         </enum>
56                                         <enum name="lastdata">
57                                                 <para>Last application arguments.</para>
58                                         </enum>
59                                         <enum name="disposition">
60                                                 <para>The final state of the CDR.</para>
61                                                 <enumlist>
62                                                         <enum name="0">
63                                                                 <para><literal>NO ANSWER</literal></para>
64                                                         </enum>
65                                                         <enum name="1">
66                                                                 <para><literal>NO ANSWER</literal> (NULL record)</para>
67                                                         </enum>
68                                                         <enum name="2">
69                                                                 <para><literal>FAILED</literal></para>
70                                                         </enum>
71                                                         <enum name="4">
72                                                                 <para><literal>BUSY</literal></para>
73                                                         </enum>
74                                                         <enum name="8">
75                                                                 <para><literal>ANSWERED</literal></para>
76                                                         </enum>
77                                                         <enum name="16">
78                                                                 <para><literal>CONGESTION</literal></para>
79                                                         </enum>
80                                                 </enumlist>
81                                         </enum>
82                                         <enum name="src">
83                                                 <para>Source.</para>
84                                         </enum>
85                                         <enum name="start">
86                                                 <para>Time the call started.</para>
87                                         </enum>
88                                         <enum name="amaflags">
89                                                 <para>R/W the Automatic Message Accounting (AMA) flags on the channel.
90                                                 When read from a channel, the integer value will always be returned.
91                                                 When written to a channel, both the string format or integer value
92                                                 is accepted.</para>
93                                                 <enumlist>
94                                                         <enum name="1"><para><literal>OMIT</literal></para></enum>
95                                                         <enum name="2"><para><literal>BILLING</literal></para></enum>
96                                                         <enum name="3"><para><literal>DOCUMENTATION</literal></para></enum>
97                                                 </enumlist>
98                                                 <warning><para>Accessing this setting is deprecated in CDR. Please use the CHANNEL function instead.</para></warning>
99                                         </enum>
100                                         <enum name="dst">
101                                                 <para>Destination.</para>
102                                         </enum>
103                                         <enum name="answer">
104                                                 <para>Time the call was answered.</para>
105                                         </enum>
106                                         <enum name="accountcode">
107                                                 <para>The channel's account code.</para>
108                                                 <warning><para>Accessing this setting is deprecated in CDR. Please use the CHANNEL function instead.</para></warning>
109                                         </enum>
110                                         <enum name="dcontext">
111                                                 <para>Destination context.</para>
112                                         </enum>
113                                         <enum name="end">
114                                                 <para>Time the call ended.</para>
115                                         </enum>
116                                         <enum name="uniqueid">
117                                                 <para>The channel's unique id.</para>
118                                         </enum>
119                                         <enum name="dstchannel">
120                                                 <para>Destination channel.</para>
121                                         </enum>
122                                         <enum name="duration">
123                                                 <para>Duration of the call.</para>
124                                         </enum>
125                                         <enum name="userfield">
126                                                 <para>The channel's user specified field.</para>
127                                         </enum>
128                                         <enum name="lastapp">
129                                                 <para>Last application.</para>
130                                         </enum>
131                                         <enum name="billsec">
132                                                 <para>Duration of the call once it was answered.</para>
133                                         </enum>
134                                         <enum name="channel">
135                                                 <para>Channel name.</para>
136                                         </enum>
137                                         <enum name="sequence">
138                                                 <para>CDR sequence number.</para>
139                                         </enum>
140                                 </enumlist>
141                         </parameter>
142                         <parameter name="options" required="false">
143                                 <optionlist>
144                                         <option name="f">
145                                                 <para>Returns billsec or duration fields as floating point values.</para>
146                                         </option>
147                                         <option name="u">
148                                                 <para>Retrieves the raw, unprocessed value.</para>
149                                                 <para>For example, 'start', 'answer', and 'end' will be retrieved as epoch
150                                                 values, when the <literal>u</literal> option is passed, but formatted as YYYY-MM-DD HH:MM:SS
151                                                 otherwise.  Similarly, disposition and amaflags will return their raw
152                                                 integral values.</para>
153                                         </option>
154                                 </optionlist>
155                         </parameter>
156                 </syntax>
157                 <description>
158                         <para>All of the CDR field names are read-only, except for <literal>accountcode</literal>,
159                         <literal>userfield</literal>, and <literal>amaflags</literal>. You may, however, supply
160                         a name not on the above list, and create your own variable, whose value can be changed
161                         with this function, and this variable will be stored on the CDR.</para>
162                         <note><para>CDRs can only be modified before the bridge between two channels is
163                         torn down. For example, CDRs may not be modified after the <literal>Dial</literal>
164                         application has returned.</para></note>
165                         <para>Example: exten => 1,1,Set(CDR(userfield)=test)</para>
166                 </description>
167         </function>
168         <function name="CDR_PROP" language="en_US">
169                 <synopsis>
170                         Set a property on a channel's CDR.
171                 </synopsis>
172                 <syntax>
173                         <parameter name="name" required="true">
174                                 <para>The property to set on the CDR.</para>
175                                 <enumlist>
176                                         <enum name="party_a">
177                                                 <para>Set this channel as the preferred Party A when
178                                                 channels are associated together.</para>
179                                                 <para>Write-Only</para>
180                                         </enum>
181                                         <enum name="disable">
182                                                 <para>Disable CDRs for this channel.</para>
183                                                 <para>Write-Only</para>
184                                         </enum>
185                                 </enumlist>
186                         </parameter>
187                 </syntax>
188                 <description>
189                         <para>This function sets a property on a channel's CDR. Properties
190                         alter the behavior of how the CDR operates for that channel.</para>
191                 </description>
192         </function>
193  ***/
194
195 enum cdr_option_flags {
196         OPT_UNPARSED = (1 << 1),
197         OPT_FLOAT = (1 << 2),
198 };
199
200 AST_APP_OPTIONS(cdr_func_options, {
201         AST_APP_OPTION('f', OPT_FLOAT),
202         AST_APP_OPTION('u', OPT_UNPARSED),
203 });
204
205 struct cdr_func_payload {
206         struct ast_channel *chan;
207         const char *cmd;
208         const char *arguments;
209         const char *value;
210 };
211
212 struct cdr_func_data {
213         char *buf;
214         size_t len;
215 };
216
217 STASIS_MESSAGE_TYPE_DEFN_LOCAL(cdr_read_message_type);
218 STASIS_MESSAGE_TYPE_DEFN_LOCAL(cdr_write_message_type);
219 STASIS_MESSAGE_TYPE_DEFN_LOCAL(cdr_prop_write_message_type);
220
221 static void cdr_read_callback(void *data, struct stasis_subscription *sub, struct stasis_message *message)
222 {
223         struct cdr_func_data *output = data;
224         struct cdr_func_payload *payload = stasis_message_data(message);
225         char *info;
226         char *value = NULL;
227         struct ast_flags flags = { 0 };
228         char tempbuf[512];
229         AST_DECLARE_APP_ARGS(args,
230                 AST_APP_ARG(variable);
231                 AST_APP_ARG(options);
232         );
233
234         if (cdr_read_message_type() != stasis_message_type(message)) {
235                 return;
236         }
237
238         if (!payload || !output) {
239                 return;
240         }
241
242         if (ast_strlen_zero(payload->arguments)) {
243                 ast_log(AST_LOG_WARNING, "%s requires a variable (%s(variable[,option]))\n)",
244                         payload->cmd, payload->cmd);
245                 return;
246         }
247         info = ast_strdupa(payload->arguments);
248         AST_STANDARD_APP_ARGS(args, info);
249
250         if (!ast_strlen_zero(args.options)) {
251                 ast_app_parse_options(cdr_func_options, &flags, NULL, args.options);
252         }
253
254         if (ast_strlen_zero(ast_channel_name(payload->chan))) {
255                 /* Format request on a dummy channel */
256                 ast_cdr_format_var(ast_channel_cdr(payload->chan), args.variable, &value, tempbuf, sizeof(tempbuf), 0);
257                 if (ast_strlen_zero(value)) {
258                         return;
259                 }
260                 ast_copy_string(tempbuf, value, sizeof(tempbuf));
261                 ast_set_flag(&flags, OPT_UNPARSED);
262         } else if (ast_cdr_getvar(ast_channel_name(payload->chan), args.variable, tempbuf, sizeof(tempbuf))) {
263                 return;
264         }
265
266         if (ast_test_flag(&flags, OPT_FLOAT)
267                 && (!strcasecmp("billsec", args.variable) || !strcasecmp("duration", args.variable))) {
268                 long ms;
269                 double dtime;
270
271                 if (sscanf(tempbuf, "%30ld", &ms) != 1) {
272                         ast_log(AST_LOG_WARNING, "Unable to parse %s (%s) from the CDR for channel %s\n",
273                                 args.variable, tempbuf, ast_channel_name(payload->chan));
274                         return;
275                 }
276                 dtime = (double)(ms / 1000.0);
277                 snprintf(tempbuf, sizeof(tempbuf), "%lf", dtime);
278         } else if (!ast_test_flag(&flags, OPT_UNPARSED)) {
279                 if (!strcasecmp("start", args.variable)
280                         || !strcasecmp("end", args.variable)
281                         || !strcasecmp("answer", args.variable)) {
282                         struct timeval fmt_time;
283                         struct ast_tm tm;
284                         /* tv_usec is suseconds_t, which could be int or long */
285                         long int tv_usec;
286
287                         if (sscanf(tempbuf, "%ld.%ld", &fmt_time.tv_sec, &tv_usec) != 2) {
288                                 ast_log(AST_LOG_WARNING, "Unable to parse %s (%s) from the CDR for channel %s\n",
289                                         args.variable, tempbuf, ast_channel_name(payload->chan));
290                                 return;
291                         }
292                         fmt_time.tv_usec = tv_usec;
293                         ast_localtime(&fmt_time, &tm, NULL);
294                         ast_strftime(tempbuf, sizeof(*tempbuf), "%Y-%m-%d %T", &tm);
295                 } else if (!strcasecmp("disposition", args.variable)) {
296                         int disposition;
297
298                         if (sscanf(tempbuf, "%8d", &disposition) != 1) {
299                                 ast_log(AST_LOG_WARNING, "Unable to parse %s (%s) from the CDR for channel %s\n",
300                                         args.variable, tempbuf, ast_channel_name(payload->chan));
301                                 return;
302                         }
303                         snprintf(tempbuf, sizeof(tempbuf), "%s", ast_cdr_disp2str(disposition));
304                 } else if (!strcasecmp("amaflags", args.variable)) {
305                         int amaflags;
306
307                         if (sscanf(tempbuf, "%8d", &amaflags) != 1) {
308                                 ast_log(AST_LOG_WARNING, "Unable to parse %s (%s) from the CDR for channel %s\n",
309                                         args.variable, tempbuf, ast_channel_name(payload->chan));
310                                 return;
311                         }
312                         snprintf(tempbuf, sizeof(tempbuf), "%s", ast_channel_amaflags2string(amaflags));
313                 }
314         }
315
316         ast_copy_string(output->buf, tempbuf, output->len);
317 }
318
319 static void cdr_write_callback(void *data, struct stasis_subscription *sub, struct stasis_message *message)
320 {
321         struct cdr_func_payload *payload = stasis_message_data(message);
322         struct ast_flags flags = { 0 };
323         AST_DECLARE_APP_ARGS(args,
324                 AST_APP_ARG(variable);
325                 AST_APP_ARG(options);
326         );
327         char *parse;
328
329         if (cdr_write_message_type() != stasis_message_type(message)) {
330                 return;
331         }
332
333         if (!payload) {
334                 return;
335         }
336
337         if (ast_strlen_zero(payload->arguments)) {
338                 ast_log(AST_LOG_WARNING, "%s requires a variable (%s(variable)=value)\n)",
339                         payload->cmd, payload->cmd);
340                 return;
341         }
342         if (ast_strlen_zero(payload->value)) {
343                 ast_log(AST_LOG_WARNING, "%s requires a value (%s(variable)=value)\n)",
344                         payload->cmd, payload->cmd);
345                 return;
346         }
347         parse = ast_strdupa(payload->arguments);
348         AST_STANDARD_APP_ARGS(args, parse);
349
350         if (!ast_strlen_zero(args.options)) {
351                 ast_app_parse_options(cdr_func_options, &flags, NULL, args.options);
352         }
353
354         if (!strcasecmp(args.variable, "accountcode")) {
355                 ast_log(AST_LOG_WARNING, "Using the CDR function to set 'accountcode' is deprecated. Please use the CHANNEL function instead.\n");
356                 ast_channel_lock(payload->chan);
357                 ast_channel_accountcode_set(payload->chan, payload->value);
358                 ast_channel_unlock(payload->chan);
359         } else if (!strcasecmp(args.variable, "peeraccount")) {
360                 ast_log(AST_LOG_WARNING, "The 'peeraccount' setting is not supported. Please set the 'accountcode' on the appropriate channel using the CHANNEL function.\n");
361         } else if (!strcasecmp(args.variable, "userfield")) {
362                 ast_cdr_setuserfield(ast_channel_name(payload->chan), payload->value);
363         } else if (!strcasecmp(args.variable, "amaflags")) {
364                 ast_log(AST_LOG_WARNING, "Using the CDR function to set 'amaflags' is deprecated. Please use the CHANNEL function instead.\n");
365                 if (isdigit(*payload->value)) {
366                         int amaflags;
367                         sscanf(payload->value, "%30d", &amaflags);
368                         ast_channel_lock(payload->chan);
369                         ast_channel_amaflags_set(payload->chan, amaflags);
370                         ast_channel_unlock(payload->chan);
371                 } else {
372                         ast_channel_lock(payload->chan);
373                         ast_channel_amaflags_set(payload->chan, ast_channel_string2amaflag(payload->value));
374                         ast_channel_unlock(payload->chan);
375                 }
376         } else {
377                 ast_cdr_setvar(ast_channel_name(payload->chan), args.variable, payload->value);
378         }
379         return;
380 }
381
382 static void cdr_prop_write_callback(void *data, struct stasis_subscription *sub, struct stasis_message *message)
383 {
384         struct cdr_func_payload *payload = stasis_message_data(message);
385         enum ast_cdr_options option;
386         char *parse;
387         AST_DECLARE_APP_ARGS(args,
388                 AST_APP_ARG(variable);
389                 AST_APP_ARG(options);
390         );
391
392         if (cdr_prop_write_message_type() != stasis_message_type(message)) {
393                 return;
394         }
395
396         if (!payload) {
397                 return;
398         }
399
400         if (ast_strlen_zero(payload->arguments)) {
401                 ast_log(AST_LOG_WARNING, "%s requires a variable (%s(variable)=value)\n)",
402                         payload->cmd, payload->cmd);
403                 return;
404         }
405         if (ast_strlen_zero(payload->value)) {
406                 ast_log(AST_LOG_WARNING, "%s requires a value (%s(variable)=value)\n)",
407                         payload->cmd, payload->cmd);
408                 return;
409         }
410         parse = ast_strdupa(payload->arguments);
411         AST_STANDARD_APP_ARGS(args, parse);
412
413         if (!strcasecmp("party_a", args.variable)) {
414                 option = AST_CDR_FLAG_PARTY_A;
415         } else if (!strcasecmp("disable", args.variable)) {
416                 option = AST_CDR_FLAG_DISABLE_ALL;
417         } else {
418                 ast_log(AST_LOG_WARNING, "Unknown option %s used with %s\n", args.variable, payload->cmd);
419                 return;
420         }
421
422         if (ast_true(payload->value)) {
423                 ast_cdr_set_property(ast_channel_name(payload->chan), option);
424         } else {
425                 ast_cdr_clear_property(ast_channel_name(payload->chan), option);
426         }
427 }
428
429
430 static int cdr_read(struct ast_channel *chan, const char *cmd, char *parse,
431                     char *buf, size_t len)
432 {
433         RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
434         RAII_VAR(struct cdr_func_payload *, payload,
435                 ao2_alloc(sizeof(*payload), NULL), ao2_cleanup);
436         struct cdr_func_data output = { 0, };
437
438         if (!payload) {
439                 return -1;
440         }
441         payload->chan = chan;
442         payload->cmd = cmd;
443         payload->arguments = parse;
444
445         buf[0] = '\0';/* Ensure the buffer is initialized. */
446         output.buf = buf;
447         output.len = len;
448
449         message = stasis_message_create(cdr_read_message_type(), payload);
450         if (!message) {
451                 ast_log(AST_LOG_WARNING, "Failed to manipulate CDR for channel %s: unable to create message\n",
452                         ast_channel_name(chan));
453                 return -1;
454         }
455
456         /* If this is a request on a dummy channel, we're doing post-processing on an
457          * already dispatched CDR. Simply call the callback to calculate the value and
458          * return, instead of posting to Stasis as we would for a running channel.
459          */
460         if (ast_strlen_zero(ast_channel_name(chan))) {
461                 cdr_read_callback(NULL, NULL, message);
462         } else {
463                 RAII_VAR(struct stasis_subscription *, subscription, NULL, ao2_cleanup);
464
465                 subscription = stasis_subscribe(ast_channel_topic(chan), cdr_read_callback, &output);
466                 if (!subscription) {
467                         ast_log(AST_LOG_WARNING, "Failed to manipulate CDR for channel %s: unable to create subscription\n",
468                                 ast_channel_name(chan));
469                         return -1;
470                 }
471
472                 stasis_publish(ast_channel_topic(chan), message);
473
474                 subscription = stasis_unsubscribe_and_join(subscription);
475         }
476
477         return 0;
478 }
479
480 static int cdr_write(struct ast_channel *chan, const char *cmd, char *parse,
481                      const char *value)
482 {
483         RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
484         RAII_VAR(struct cdr_func_payload *, payload,
485                 ao2_alloc(sizeof(*payload), NULL), ao2_cleanup);
486         RAII_VAR(struct stasis_subscription *, subscription, NULL, ao2_cleanup);
487
488         if (!payload) {
489                 return -1;
490         }
491         payload->chan = chan;
492         payload->cmd = cmd;
493         payload->arguments = parse;
494         payload->value = value;
495
496         message = stasis_message_create(cdr_write_message_type(), payload);
497         if (!message) {
498                 ast_log(AST_LOG_WARNING, "Failed to manipulate CDR for channel %s: unable to create message\n",
499                         ast_channel_name(chan));
500                 return -1;
501         }
502
503         subscription = stasis_subscribe(ast_channel_topic(chan), cdr_write_callback, NULL);
504         if (!subscription) {
505                 ast_log(AST_LOG_WARNING, "Failed to manipulate CDR for channel %s: unable to create subscription\n",
506                         ast_channel_name(chan));
507                 return -1;
508         }
509
510         stasis_publish(ast_channel_topic(chan), message);
511
512         subscription = stasis_unsubscribe_and_join(subscription);
513
514         return 0;
515 }
516
517 static int cdr_prop_write(struct ast_channel *chan, const char *cmd, char *parse,
518                      const char *value)
519 {
520         RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
521         RAII_VAR(struct cdr_func_payload *, payload,
522                 ao2_alloc(sizeof(*payload), NULL), ao2_cleanup);
523         RAII_VAR(struct stasis_subscription *, subscription, NULL, ao2_cleanup);
524
525         if (!payload) {
526                 return -1;
527         }
528         payload->chan = chan;
529         payload->cmd = cmd;
530         payload->arguments = parse;
531         payload->value = value;
532
533         message = stasis_message_create(cdr_prop_write_message_type(), payload);
534         if (!message) {
535                 ast_log(AST_LOG_WARNING, "Failed to manipulate CDR for channel %s: unable to create message\n",
536                         ast_channel_name(chan));
537                 return -1;
538         }
539
540         subscription = stasis_subscribe(ast_channel_topic(chan), cdr_prop_write_callback, NULL);
541         if (!subscription) {
542                 ast_log(AST_LOG_WARNING, "Failed to manipulate CDR for channel %s: unable to create subscription\n",
543                         ast_channel_name(chan));
544                 return -1;
545         }
546
547         stasis_publish(ast_channel_topic(chan), message);
548
549         subscription = stasis_unsubscribe_and_join(subscription);
550
551         return 0;
552 }
553
554 static struct ast_custom_function cdr_function = {
555         .name = "CDR",
556         .read = cdr_read,
557         .write = cdr_write,
558 };
559
560 static struct ast_custom_function cdr_prop_function = {
561         .name = "CDR_PROP",
562         .read = NULL,
563         .write = cdr_prop_write,
564 };
565
566 static int unload_module(void)
567 {
568         int res = 0;
569
570         STASIS_MESSAGE_TYPE_CLEANUP(cdr_read_message_type);
571         STASIS_MESSAGE_TYPE_CLEANUP(cdr_write_message_type);
572         STASIS_MESSAGE_TYPE_CLEANUP(cdr_prop_write_message_type);
573         res |= ast_custom_function_unregister(&cdr_function);
574         res |= ast_custom_function_unregister(&cdr_prop_function);
575
576         return res;
577 }
578
579 static int load_module(void)
580 {
581         int res = 0;
582
583         res |= STASIS_MESSAGE_TYPE_INIT(cdr_read_message_type);
584         res |= STASIS_MESSAGE_TYPE_INIT(cdr_write_message_type);
585         res |= STASIS_MESSAGE_TYPE_INIT(cdr_prop_write_message_type);
586         res |= ast_custom_function_register(&cdr_function);
587         res |= ast_custom_function_register(&cdr_prop_function);
588
589         return res;
590 }
591
592 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Call Detail Record (CDR) dialplan functions");