o New Option: K/k parking via dtmf tones
5. Dictate() -- optional filename argument added.
6. Directory() -- new option: e - In addition to the name, also read the extension number...
- 7. Meetme() -- new options:
+ 7. ForkCDR() -- new options:
+ o 'a' -- update answer time on new cdr
+ o 'A' -- Lock the orig CDR answer time against changes.
+ o 'D' -- Copy the disposition from the orig to the new CDR.
+ o 'd' -- clear the dstcannel field in the new CDR.
+ o 'e' -- set the end time of the original CDR.
+ o 'R' -- do NOT reset the new CDR.
+ o 's' -- Add/change var in orig CDR.
+ o 'T' -- Force ast_cdr_end, answer to obey LOCKED flag for the orig. CDR.
+ -- ast_cdr_setvar will be forced also (used by the CDR() func in write mode)
+ 8. Meetme() -- new options:
o 'I' -- announce user join/leave without review
o 'l' -- set listen only mode (Listen only, no talking)
o 'o' -- set talker optimization - treats talkers who aren't speaking as...
o '1' -- do not play message when first person enters
- 8. MeetmeAdmin() -- new options:
+ 9. MeetmeAdmin() -- new options:
o 'r' -- Reset one user's volume settings
o 'R' -- Reset all users volume settings
o 's' -- Lower entire conference speaking volume
o 'U' -- Lower all users listen volume
o 'v' -- Lower entire conference listening volume
o 'V' -- Raise entire conference listening volume
- 9. OSPFinish() : now also can return ERROR result.
- 10. OSPLookup() : Sets more variables, also now returns ERROR result.
- 11. Page() -- New option: r - record the page into a file (see 'r' for app_meetme)
- 12. Pickup() -- multiple extensions, PICKUPMARK; read the description!
- 13. Queue()
+ 10. OSPFinish() : now also can return ERROR result.
+ 11. OSPLookup() : Sets more variables, also now returns ERROR result.
+ 12. Page() -- New option: r - record the page into a file (see 'r' for app_meetme)
+ 13. Pickup() -- multiple extensions, PICKUPMARK; read the description!
+ 14. Queue()
o New Argument: AGI
o New option: i
- 14. Random() -- is now deprecated in 1.4
- 15. Read() -- replace 'skip' and 'noanswer' options with 's', 'n', add 'i' option.
- 16. Record() -- New option: 'x' : ignore all terminator keys (DTMF) and keep recording until hangup
- 17. UserEvent() -- slight change in behavior. Read the description.
- 18. VoiceMailMain() -- new a(#) option, goes to folder # directly.
- 19. WaitForSilence() -- new optional 3rd arg, time delay before returning.
+ 15. Random() -- is now deprecated in 1.4
+ 16. Read() -- replace 'skip' and 'noanswer' options with 's', 'n', add 'i' option.
+ 17. Record() -- New option: 'x' : ignore all terminator keys (DTMF) and keep recording until hangup
+ 18. UserEvent() -- slight change in behavior. Read the description.
+ 19. VoiceMailMain() -- new a(#) option, goes to folder # directly.
+ 20. WaitForSilence() -- new optional 3rd arg, time delay before returning.
* Functions that have changes to their interfaces:
- 1. CDR -- new option: u
+ 1. CDR -- new options: u and s
2. LANGUAGE -- Deprecated. Use CHANNEL(language) instead.
3. MUSICCLASS -- Deprecated. Use CHANNEL(musicclass) instead.
* Configuration File Changes:
"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";
OPT_NORESET = (1 << 4),
OPT_KEEPVARS = (1 << 5),
OPT_VARSET = (1 << 6),
+ OPT_ANSLOCK = (1 << 7),
+ OPT_DONTOUCH = (1 << 8),
};
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),
});
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);
}
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),
});
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));
static int cdr_write(struct ast_channel *chan, 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);
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. */
"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"
" 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"
-" 3 = FAILED\n"
-" 4 = ANSWERED\n"
+" 2 = BUSY\n"
+" 3 = FAILED\n"
+" 4 = ANSWERED\n"
" raw values for amaflags:\n"
" 1 = OMIT\n"
" 2 = BILLING\n"
#define _ASTERISK_CDR_H
#include <sys/time.h>
-#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_NULL 0
#define AST_CDR_FAILED (1 << 0)
/*! AMA Flags */
#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"
/*!
* \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);
/*! 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);
/*! 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);
/*! Save the result of the call based on the AST_CAUSE_* */
/*!
* \param cdr the cdr you wish to associate with the call
- * Returns nothing
* \param cause the AST_CAUSE_*
+ * Returns nothing
*/
int ast_cdr_disposition(struct ast_cdr *cdr, int cause);
}
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)) {
{
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;
{
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)
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)) {
if (cdr->disposition < AST_CDR_FAILED)
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;
chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
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();