diff --git a/CHANGES b/CHANGES index 9b4f755d64..69834642ef 100644 --- a/CHANGES +++ b/CHANGES @@ -93,6 +93,13 @@ Application Changes * PrivacyManager now takes an option where you can specify a context where the given number will be matched. This way you have more control over who is allowed and it stops the people who blindly enter 10 digits. + * ForkCDR has new options: 'a' updates the answer time on the new CDR; 'A' locks + answer times, disposition, on orig CDR against updates; 'D' Copies the disposition + from the orig CDR to the new CDR after reset; 'e' sets the 'end' time on the + original CDR; 'R' prevents the new CDR from being reset; 's(var=val)' adds/changes + the 'var' variable on the original CDR; 'T' forces ast_cdr_end(), ast_cdr_answer(), + _busy(), failed(), etc, to all obey the LOCKED flag on cdr's in the chain, and + also the ast_cdr_setvar() func. SIP Changes ----------- diff --git a/apps/app_forkcdr.c b/apps/app_forkcdr.c index 8d01a829c4..4c6e620fb7 100644 --- a/apps/app_forkcdr.c +++ b/apps/app_forkcdr.c @@ -42,21 +42,82 @@ static char *synopsis = "Forks the Call Data Record"; static char *descrip = " ForkCDR([options]): Causes the Call Data Record to fork an additional\n" -"cdr record starting from the time of the fork call\n" +"cdr record starting from the time of the fork call. This new cdr record will\n" +"be linked to end of the list of cdr records attached to the channel. The original CDR is\n" +"has a LOCKED flag set, which forces most cdr operations to skip it, except\n" +"for the functions that set the answer and end times, which ignore the LOCKED\n" +"flag. This allows all the cdr records in the channel to be 'ended' together\n" +"when the channel is closed.\n" +"The CDR() func (when setting CDR values) normally ignores the LOCKED flag also,\n" +"but has options to vary its behavior. The 'T' option (described below), can\n" +"override this behavior, but beware the risks.\n" +"\n" +"Detailed Behavior Description:\n" +"First, this app finds the last cdr record in the list, and makes\n" +"a copy of it. This new copy will be the newly forked cdr record.\n" +"Next, this new record is linked to the end of the cdr record list.\n" +"Next, The new cdr record is RESET (unless you use an option to prevent this)\n" +"This means that:\n" +" 1. All flags are unset on the cdr record\n" +" 2. the start, end, and answer times are all set to zero.\n" +" 3. the billsec and duration fields are set to zero.\n" +" 4. the start time is set to the current time.\n" +" 5. the disposition is set to NULL.\n" +"Next, unless you specified the 'v' option, all variables will be\n" +"removed from the original cdr record. Thus, the 'v' option allows\n" +"any CDR variables to be replicated to all new forked cdr records.\n" +"Without the 'v' option, the variables on the original are effectively\n" +"moved to the new forked cdr record.\n" +"Next, if the 's' option is set, the provided variable and value\n" +"are set on the original cdr record.\n" +"Next, if the 'a' option is given, and the original cdr record has an\n" +"answer time set, then the new forked cdr record will have its answer\n" +"time set to its start time. If the old answer time were carried forward,\n" +"the answer time would be earlier than the start time, giving strange\n" +"duration and billsec times.\n" +"Next, if the 'd' option was specified, the disposition is copied from\n" +"the original cdr record to the new forked cdr.\n" +"Next, if the 'D' option was specified, the destination channel field\n" +"in the new forked CDR is erased.\n" +"Next, if the 'e' option was specified, the 'end' time for the original\n" +"cdr record is set to the current time. Future hang-up or ending events\n" +"will not override this time stamp.\n" +"Next, If the 'A' option is specified, the original cdr record will have\n" +"it ANS_LOCKED flag set, which prevent future call dispostion events\n" +"from updating the original cdr record's disposition. Normally, an\n" +"'ANSWERED' event would mark all cdr records in the chain as 'ANSWERED'.\n" +"Next, if the 'T' option is specified, the original cdr record will have\n" +"its 'DONT_TOUCH' flag set, which will force the cdr_answer, cdr_end, and\n" +"cdr_setvar functions to leave that cdr record alone.\n" +"And, last but not least, the original cdr record has its LOCKED flag\n" +"set. Almost all internal CDR functions (except for the funcs that set\n" +"the end, and answer times, and set a variable) will honor this flag\n" +"and leave a LOCKED cdr record alone.\n" +"This means that the newly created forked cdr record will affected\n" +"by events transpiring within Asterisk, with the previously noted\n" +"exceptions.\n" " Options:\n" -" a - update the answer time on the NEW CDR just after it's been inited..\n" +" a - update the answer time on the NEW CDR just after it's been inited..\n" " The new CDR may have been answered already, the reset that forkcdr.\n" -" does will erase the answer time. This will bring it back, but.\n" +" does will erase the answer time. This will bring it back, but\n" " the answer time will be a copy of the fork/start time. It will.\n" " only do this if the initial cdr was indeed already answered..\n" -" D - Copy the disposition forward from the old cdr, after the .\n" +" A - Lock the original CDR against the answer time being updated.\n" +" This will allow the disposition on the original CDR to remain the same.\n" +" d - Copy the disposition forward from the old cdr, after the .\n" " init..\n" -" d - Clear the dstchannel on the new CDR after reset..\n" -" e - end the original CDR. Do this after all the necc. data.\n" +" D - Clear the dstchannel on the new CDR after reset..\n" +" e - end the original CDR. Do this after all the necc. data.\n" " is copied from the original CDR to the new forked CDR..\n" " R - do NOT reset the new cdr..\n" " s(name=val) - Set the CDR var 'name' in the original CDR, with value.\n" " 'val'.\n" +" T - Mark the original CDR with a DONT_TOUCH flag. setvar, answer, and end\n" +" cdr funcs will obey this flag; normally they don't honor the LOCKED\n" +" flag set on the original CDR record.\n" +" Beware-- using this flag may cause CDR's not to have their end times\n" +" updated! It is suggested that if you specify this flag, you might\n" +" wish to use the 'e' flag as well!\n" " v - When the new CDR is forked, it gets a copy of the vars attached\n" " to the current CDR. The vars attached to the original CDR are removed\n" " unless this option is specified.\n"; @@ -70,6 +131,8 @@ enum { OPT_NORESET = (1 << 4), OPT_KEEPVARS = (1 << 5), OPT_VARSET = (1 << 6), + OPT_ANSLOCK = (1 << 7), + OPT_DONTOUCH = (1 << 8), }; enum { @@ -80,11 +143,13 @@ enum { AST_APP_OPTIONS(forkcdr_exec_options, { AST_APP_OPTION('a', OPT_SETANS), + AST_APP_OPTION('A', OPT_ANSLOCK), AST_APP_OPTION('d', OPT_SETDISP), AST_APP_OPTION('D', OPT_RESETDEST), AST_APP_OPTION('e', OPT_ENDCDR), AST_APP_OPTION('R', OPT_NORESET), AST_APP_OPTION_ARG('s', OPT_VARSET, OPT_ARG_VARSET), + AST_APP_OPTION('T', OPT_DONTOUCH), AST_APP_OPTION('v', OPT_KEEPVARS), }); @@ -132,6 +197,12 @@ static void ast_cdr_fork(struct ast_channel *chan, struct ast_flags optflags, ch if (ast_test_flag(&optflags, OPT_ENDCDR)) ast_cdr_end(cdr); + if (ast_test_flag(&optflags, OPT_ANSLOCK)) + ast_set_flag(cdr, AST_CDR_FLAG_ANSLOCKED); + + if (ast_test_flag(&optflags, OPT_DONTOUCH)) + ast_set_flag(cdr, AST_CDR_FLAG_DONT_TOUCH); + ast_set_flag(cdr, AST_CDR_FLAG_CHILD | AST_CDR_FLAG_LOCKED); } diff --git a/funcs/func_cdr.c b/funcs/func_cdr.c index a1004e679c..3ea1a30083 100644 --- a/funcs/func_cdr.c +++ b/funcs/func_cdr.c @@ -40,11 +40,13 @@ enum { OPT_RECURSIVE = (1 << 0), OPT_UNPARSED = (1 << 1), OPT_LAST = (1 << 2), + OPT_SKIPLOCKED = (1 << 3), } cdr_option_flags; AST_APP_OPTIONS(cdr_func_options, { AST_APP_OPTION('l', OPT_LAST), AST_APP_OPTION('r', OPT_RECURSIVE), + AST_APP_OPTION('s', OPT_SKIPLOCKED), AST_APP_OPTION('u', OPT_UNPARSED), }); @@ -74,6 +76,10 @@ static int cdr_read(struct ast_channel *chan, const char *cmd, char *parse, while (cdr->next) cdr = cdr->next; + if (ast_test_flag(&flags, OPT_SKIPLOCKED)) + while (ast_test_flag(cdr, AST_CDR_FLAG_LOCKED) && cdr->next) + cdr = cdr->next; + ast_cdr_getvar(cdr, args.variable, &ret, buf, len, ast_test_flag(&flags, OPT_RECURSIVE), ast_test_flag(&flags, OPT_UNPARSED)); @@ -84,6 +90,7 @@ static int cdr_read(struct ast_channel *chan, const char *cmd, char *parse, static int cdr_write(struct ast_channel *chan, const char *cmd, char *parse, const char *value) { + struct ast_cdr *cdr = chan ? chan->cdr : NULL; struct ast_flags flags = { 0 }; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(variable); @@ -93,19 +100,26 @@ static int cdr_write(struct ast_channel *chan, const char *cmd, char *parse, if (ast_strlen_zero(parse) || !value || !chan) return -1; + if (!cdr) + return -1; + AST_STANDARD_APP_ARGS(args, parse); if (!ast_strlen_zero(args.options)) ast_app_parse_options(cdr_func_options, &flags, NULL, args.options); - if (!strcasecmp(args.variable, "accountcode")) + if (ast_test_flag(&flags, OPT_LAST)) + while (cdr->next) + cdr = cdr->next; + + if (!strcasecmp(args.variable, "accountcode")) /* the 'l' flag doesn't apply to setting the accountcode, userfield, or amaflags */ ast_cdr_setaccount(chan, value); else if (!strcasecmp(args.variable, "userfield")) ast_cdr_setuserfield(chan, value); else if (!strcasecmp(args.variable, "amaflags")) ast_cdr_setamaflags(chan, value); - else if (chan->cdr) - ast_cdr_setvar(chan->cdr, args.variable, value, ast_test_flag(&flags, OPT_RECURSIVE)); + else + ast_cdr_setvar(cdr, args.variable, value, ast_test_flag(&flags, OPT_RECURSIVE)); /* No need to worry about the u flag, as all fields for which setting * 'u' would do anything are marked as readonly. */ @@ -122,6 +136,8 @@ static struct ast_custom_function cdr_function = { "Options:\n" " 'l' uses the most recent CDR on a channel with multiple records\n" " 'r' searches the entire stack of CDRs on the channel\n" +" 's' skips any CDR's that are marked 'LOCKED' due to forkCDR() calls.\n" +" (on setting/writing CDR vars only)\n" " 'u' retrieves the raw, unprocessed value\n" " For example, 'start', 'answer', and 'end' will be retrieved as epoch\n" " values, when the 'u' option is passed, but formatted as YYYY-MM-DD HH:MM:SS\n" @@ -139,6 +155,8 @@ static struct ast_custom_function cdr_function = { " a name not on the above list, and create your own\n" " variable, whose value can be changed with this function,\n" " and this variable will be stored on the cdr.\n" +" For setting CDR values, the 'l' flag does not apply to\n" +" setting the accountcode, userfield, or amaflags.\n" " raw values for disposition:\n" " 1 = NO ANSWER\n" " 2 = BUSY\n" diff --git a/include/asterisk/cdr.h b/include/asterisk/cdr.h index fbab3f53fe..addef04eaa 100644 --- a/include/asterisk/cdr.h +++ b/include/asterisk/cdr.h @@ -24,11 +24,13 @@ #define _ASTERISK_CDR_H #include -#define AST_CDR_FLAG_KEEP_VARS (1 << 0) +#define AST_CDR_FLAG_KEEP_VARS (1 << 0) #define AST_CDR_FLAG_POSTED (1 << 1) #define AST_CDR_FLAG_LOCKED (1 << 2) #define AST_CDR_FLAG_CHILD (1 << 3) -#define AST_CDR_FLAG_POST_DISABLED (1 << 4) +#define AST_CDR_FLAG_POST_DISABLED (1 << 4) +#define AST_CDR_FLAG_ANSLOCKED (1 << 5) +#define AST_CDR_FLAG_DONT_TOUCH (1 << 6) #define AST_CDR_FLAG_POST_ENABLE (1 << 5) /*! \name CDR Flags */ @@ -44,11 +46,11 @@ /*@{ */ #define AST_CDR_OMIT (1) #define AST_CDR_BILLING (2) -#define AST_CDR_DOCUMENTATION (3) +#define AST_CDR_DOCUMENTATION (3) /*@} */ #define AST_MAX_USER_FIELD 256 -#define AST_MAX_ACCOUNT_CODE 20 +#define AST_MAX_ACCOUNT_CODE 20 /* Include channel.h after relevant declarations it will need */ #include "asterisk/channel.h" @@ -196,12 +198,17 @@ void ast_cdr_answer(struct ast_cdr *cdr); * \brief A call wasn't answered * \param cdr the cdr you wish to associate with the call * Marks the channel disposition as "NO ANSWER" + * Will skip CDR's in chain with ANS_LOCK bit set. (see + * forkCDR() application. */ extern void ast_cdr_noanswer(struct ast_cdr *cdr); /*! * \brief Busy a call * \param cdr the cdr you wish to associate with the call + * Marks the channel disposition as "BUSY" + * Will skip CDR's in chain with ANS_LOCK bit set. (see + * forkCDR() application. * Returns nothing */ void ast_cdr_busy(struct ast_cdr *cdr); @@ -209,6 +216,9 @@ void ast_cdr_busy(struct ast_cdr *cdr); /*! * \brief Fail a call * \param cdr the cdr you wish to associate with the call + * Marks the channel disposition as "FAILED" + * Will skip CDR's in chain with ANS_LOCK bit set. (see + * forkCDR() application. * Returns nothing */ void ast_cdr_failed(struct ast_cdr *cdr); diff --git a/main/cdr.c b/main/cdr.c index 0a91f74fd5..bbc0c6ec45 100644 --- a/main/cdr.c +++ b/main/cdr.c @@ -306,6 +306,8 @@ int ast_cdr_setvar(struct ast_cdr *cdr, const char *name, const char *value, int } for (; cdr; cdr = recur ? cdr->next : NULL) { + if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) + continue; headp = &cdr->varshead; AST_LIST_TRAVERSE_SAFE_BEGIN(headp, newvariable, entries) { if (!strcasecmp(ast_var_name(newvariable), name)) { @@ -689,6 +691,10 @@ void ast_cdr_answer(struct ast_cdr *cdr) { for (; cdr; cdr = cdr->next) { + if (ast_test_flag(cdr, AST_CDR_FLAG_ANSLOCKED)) + continue; + if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) + continue; check_post(cdr); if (cdr->disposition < AST_CDR_ANSWERED) cdr->disposition = AST_CDR_ANSWERED; @@ -701,6 +707,10 @@ void ast_cdr_busy(struct ast_cdr *cdr) { for (; cdr; cdr = cdr->next) { + if (ast_test_flag(cdr, AST_CDR_FLAG_ANSLOCKED)) + continue; + if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) + continue; if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) { check_post(cdr); if (cdr->disposition < AST_CDR_BUSY) @@ -712,6 +722,11 @@ void ast_cdr_busy(struct ast_cdr *cdr) void ast_cdr_failed(struct ast_cdr *cdr) { for (; cdr; cdr = cdr->next) { + if (ast_test_flag(cdr, AST_CDR_FLAG_ANSLOCKED)) + continue; + if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) + continue; + check_post(cdr); if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) { check_post(cdr); if (cdr->disposition < AST_CDR_FAILED) @@ -725,6 +740,10 @@ void ast_cdr_noanswer(struct ast_cdr *cdr) char *chan; while (cdr) { + if (ast_test_flag(cdr, AST_CDR_FLAG_ANSLOCKED)) + continue; + if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) + continue; if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) { chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : ""; if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED)) @@ -847,6 +866,8 @@ int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c) void ast_cdr_end(struct ast_cdr *cdr) { for ( ; cdr ; cdr = cdr->next) { + if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) + continue; check_post(cdr); if (ast_tvzero(cdr->end)) cdr->end = ast_tvnow();