mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-03 11:25:35 +00:00
Merge anthm's CDR updates (bug #3595)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@5068 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -25,8 +25,10 @@ static char *app = "ForkCDR";
|
||||
static char *synopsis =
|
||||
"Forks the Call Data Record";
|
||||
static char *descrip =
|
||||
" ForkCDR(): Causes the Call Data Record to fork an additional\n"
|
||||
"cdr record starting from the time of the fork call\n";
|
||||
" ForkCDR([options]): Causes the Call Data Record to fork an additional\n"
|
||||
"cdr record starting from the time of the fork call\n"
|
||||
"If the option 'v' is passed all cdr variables will be passed along also.\n"
|
||||
"";
|
||||
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
@@ -34,17 +36,24 @@ STANDARD_LOCAL_USER;
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
|
||||
static void ast_cdr_clone(struct ast_cdr *cdr) {
|
||||
static void ast_cdr_clone(struct ast_cdr *cdr)
|
||||
{
|
||||
struct ast_cdr *newcdr = ast_cdr_alloc();
|
||||
memcpy(newcdr,cdr,sizeof(struct ast_cdr));
|
||||
ast_cdr_append(cdr,newcdr);
|
||||
gettimeofday(&newcdr->start, NULL);
|
||||
memset(&newcdr->answer, 0, sizeof(newcdr->answer));
|
||||
memset(&newcdr->varshead, 0, sizeof(newcdr->varshead));
|
||||
ast_cdr_copy_vars(newcdr, cdr);
|
||||
if (!ast_test_flag(cdr, AST_CDR_FLAG_KEEP_VARS)) {
|
||||
ast_cdr_free_vars(cdr, 0);
|
||||
}
|
||||
newcdr->disposition = AST_CDR_NOANSWER;
|
||||
ast_set_flag(cdr, AST_CDR_FLAG_CHILD|AST_CDR_FLAG_LOCKED);
|
||||
}
|
||||
|
||||
static void ast_cdr_fork(struct ast_channel *chan) {
|
||||
static void ast_cdr_fork(struct ast_channel *chan)
|
||||
{
|
||||
if(chan && chan->cdr) {
|
||||
ast_cdr_clone(chan->cdr);
|
||||
}
|
||||
@@ -55,7 +64,8 @@ static int forkcdr_exec(struct ast_channel *chan, void *data)
|
||||
int res=0;
|
||||
struct localuser *u;
|
||||
LOCAL_USER_ADD(u);
|
||||
|
||||
ast_set2_flag(chan->cdr, strchr((char *)data, 'v'), AST_CDR_FLAG_KEEP_VARS);
|
||||
|
||||
ast_cdr_fork(chan);
|
||||
|
||||
LOCAL_USER_REMOVE(u);
|
||||
|
275
cdr.c
275
cdr.c
@@ -103,10 +103,258 @@ void ast_cdr_unregister(char *name)
|
||||
free(i);
|
||||
}
|
||||
|
||||
static const char *ast_cdr_getvar_internal(struct ast_cdr *cdr, const char *name, int recur)
|
||||
{
|
||||
struct ast_var_t *variables;
|
||||
struct varshead *headp;
|
||||
|
||||
while(cdr) {
|
||||
headp = &cdr->varshead;
|
||||
if (name) {
|
||||
AST_LIST_TRAVERSE(headp,variables,entries) {
|
||||
if (!strcmp(name, ast_var_name(variables)))
|
||||
return ast_var_value(variables);
|
||||
}
|
||||
}
|
||||
if (!recur) {
|
||||
break;
|
||||
}
|
||||
cdr = cdr->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define ast_val_or_null(val) do { \
|
||||
if (val[0]) { \
|
||||
strncpy(workspace, val, workspacelen - 1);\
|
||||
*ret = workspace; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret, char *workspace, int workspacelen, int recur)
|
||||
{
|
||||
struct tm tm;
|
||||
time_t t;
|
||||
const char *fmt = "%Y-%m-%d %T";
|
||||
|
||||
*ret = NULL;
|
||||
/* special vars (the ones from the struct ast_cdr when requested by name)
|
||||
I'd almost say we should convert all the stringed vals to vars */
|
||||
|
||||
if (!strcasecmp(name, "clid")) {
|
||||
ast_val_or_null(cdr->clid);
|
||||
} else if (!strcasecmp(name, "src")) {
|
||||
ast_val_or_null(cdr->src);
|
||||
} else if (!strcasecmp(name, "dst")) {
|
||||
ast_val_or_null(cdr->dst);
|
||||
} else if (!strcasecmp(name, "dcontext")) {
|
||||
ast_val_or_null(cdr->dcontext);
|
||||
} else if (!strcasecmp(name, "channel")) {
|
||||
ast_val_or_null(cdr->channel);
|
||||
} else if (!strcasecmp(name, "dstchannel")) {
|
||||
ast_val_or_null(cdr->dstchannel);
|
||||
} else if (!strcasecmp(name, "lastapp")) {
|
||||
ast_val_or_null(cdr->lastapp);
|
||||
} else if (!strcasecmp(name, "lastdata")) {
|
||||
ast_val_or_null(cdr->lastdata);
|
||||
} else if (!strcasecmp(name, "start")) {
|
||||
t = cdr->start.tv_sec;
|
||||
if (t) {
|
||||
localtime_r(&t,&tm);
|
||||
strftime(workspace, workspacelen, fmt, &tm);
|
||||
*ret = workspace;
|
||||
}
|
||||
} else if (!strcasecmp(name, "answer")) {
|
||||
t = cdr->start.tv_sec;
|
||||
if (t) {
|
||||
localtime_r(&t,&tm);
|
||||
strftime(workspace, workspacelen, fmt, &tm);
|
||||
*ret = workspace;
|
||||
}
|
||||
} else if (!strcasecmp(name, "end")) {
|
||||
t = cdr->start.tv_sec;
|
||||
if (t) {
|
||||
localtime_r(&t,&tm);
|
||||
strftime(workspace, workspacelen, fmt, &tm);
|
||||
*ret = workspace;
|
||||
}
|
||||
} else if (!strcasecmp(name, "duration")) {
|
||||
snprintf(workspace, workspacelen, "%d", cdr->duration);
|
||||
*ret = workspace;
|
||||
} else if (!strcasecmp(name, "billsec")) {
|
||||
snprintf(workspace, workspacelen, "%d", cdr->billsec);
|
||||
*ret = workspace;
|
||||
} else if (!strcasecmp(name, "disposition")) {
|
||||
strncpy(workspace, ast_cdr_disp2str(cdr->disposition), workspacelen - 1);
|
||||
*ret = workspace;
|
||||
} else if (!strcasecmp(name, "amaflags")) {
|
||||
strncpy(workspace, ast_cdr_flags2str(cdr->amaflags), workspacelen - 1);
|
||||
*ret = workspace;
|
||||
} else if (!strcasecmp(name, "accountcode")) {
|
||||
ast_val_or_null(cdr->accountcode);
|
||||
} else if (!strcasecmp(name, "uniqueid")) {
|
||||
ast_val_or_null(cdr->uniqueid);
|
||||
} else if (!strcasecmp(name, "userfield")) {
|
||||
ast_val_or_null(cdr->userfield);
|
||||
} else {
|
||||
if ((*ret = (char *)ast_cdr_getvar_internal(cdr, name, recur))) {
|
||||
strncpy(workspace, *ret, workspacelen - 1);
|
||||
*ret = workspace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ast_cdr_setvar(struct ast_cdr *cdr, const char *name, char *value, int recur)
|
||||
{
|
||||
struct ast_var_t *newvariable;
|
||||
struct varshead *headp;
|
||||
|
||||
if (!cdr) {
|
||||
ast_log(LOG_ERROR, "Attempt to set a variable on a nonexistant CDR record.\n");
|
||||
return -1;
|
||||
}
|
||||
while (cdr) {
|
||||
headp = &cdr->varshead;
|
||||
AST_LIST_TRAVERSE (headp, newvariable, entries) {
|
||||
if (strcasecmp(ast_var_name(newvariable), name) == 0) {
|
||||
/* there is already such a variable, delete it */
|
||||
AST_LIST_REMOVE(headp, newvariable, entries);
|
||||
ast_var_delete(newvariable);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (value) {
|
||||
newvariable = ast_var_assign(name, value);
|
||||
AST_LIST_INSERT_HEAD(headp, newvariable, entries);
|
||||
}
|
||||
|
||||
if (!recur) {
|
||||
break;
|
||||
}
|
||||
cdr = cdr->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ast_cdr_copy_vars(struct ast_cdr *to_cdr, struct ast_cdr *from_cdr)
|
||||
{
|
||||
struct ast_var_t *variables, *newvariable = NULL;
|
||||
struct varshead *headpa, *headpb;
|
||||
char *var, *val;
|
||||
int x = 0;
|
||||
|
||||
headpa=&from_cdr->varshead;
|
||||
headpb=&to_cdr->varshead;
|
||||
|
||||
AST_LIST_TRAVERSE(headpa,variables,entries) {
|
||||
if (variables && (var=ast_var_name(variables)) && (val=ast_var_value(variables)) && !ast_strlen_zero(var) && !ast_strlen_zero(val)) {
|
||||
newvariable = ast_var_assign(var, val);
|
||||
AST_LIST_INSERT_HEAD(headpb, newvariable, entries);
|
||||
x++;
|
||||
}
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
#define CDR_CLEN 18
|
||||
int ast_cdr_serialize_variables(struct ast_cdr *cdr, char *buf, size_t size, char delim, char sep, int recur)
|
||||
{
|
||||
struct ast_var_t *variables;
|
||||
struct varshead *headp;
|
||||
char *var=NULL ,*val=NULL;
|
||||
char *tmp = NULL;
|
||||
char workspace[256];
|
||||
int workspacelen;
|
||||
int total = 0, x = 0, i = 0;
|
||||
const char *cdrcols[CDR_CLEN] = {
|
||||
"clid",
|
||||
"src",
|
||||
"dst",
|
||||
"dcontext",
|
||||
"channel",
|
||||
"dstchannel",
|
||||
"lastapp",
|
||||
"lastdata",
|
||||
"start",
|
||||
"answer",
|
||||
"end",
|
||||
"duration",
|
||||
"billsec",
|
||||
"disposition",
|
||||
"amaflags",
|
||||
"accountcode",
|
||||
"uniqueid",
|
||||
"userfield"
|
||||
};
|
||||
|
||||
|
||||
memset(buf,0,size);
|
||||
while (cdr) {
|
||||
x++;
|
||||
if (x > 1) {
|
||||
strncat(buf, "\n", size);
|
||||
}
|
||||
headp=&cdr->varshead;
|
||||
AST_LIST_TRAVERSE(headp,variables,entries) {
|
||||
if (cdr && variables && (var=ast_var_name(variables)) && (val=ast_var_value(variables)) && !ast_strlen_zero(var) && !ast_strlen_zero(val)) {
|
||||
snprintf(buf + strlen(buf), size - strlen(buf), "level %d: %s%c%s%c", x, var, delim, val, sep);
|
||||
if (strlen(buf) >= size) {
|
||||
ast_log(LOG_ERROR,"Data Buffer Size Exceeded!\n");
|
||||
break;
|
||||
}
|
||||
total++;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
for (i = 0 ; i < CDR_CLEN; i++) {
|
||||
workspacelen = sizeof(workspace);
|
||||
ast_cdr_getvar(cdr, cdrcols[i], &tmp, workspace, workspacelen, 0);
|
||||
if (!tmp)
|
||||
continue;
|
||||
|
||||
snprintf(buf + strlen(buf), size - strlen(buf), "level %d: %s%c%s%c", x, cdrcols[i], delim, tmp, sep);
|
||||
if (strlen(buf) >= size) {
|
||||
ast_log(LOG_ERROR,"Data Buffer Size Exceeded!\n");
|
||||
break;
|
||||
}
|
||||
total++;
|
||||
}
|
||||
|
||||
if (!recur) {
|
||||
break;
|
||||
}
|
||||
cdr = cdr->next;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
void ast_cdr_free_vars(struct ast_cdr *cdr, int recur)
|
||||
{
|
||||
struct varshead *headp;
|
||||
struct ast_var_t *vardata;
|
||||
|
||||
/* clear variables */
|
||||
while(cdr) {
|
||||
headp = &cdr->varshead;
|
||||
while (!AST_LIST_EMPTY(headp)) {
|
||||
vardata = AST_LIST_REMOVE_HEAD(headp, entries);
|
||||
ast_var_delete(vardata);
|
||||
}
|
||||
if (!recur) {
|
||||
break;
|
||||
}
|
||||
cdr = cdr->next;
|
||||
}
|
||||
}
|
||||
|
||||
void ast_cdr_free(struct ast_cdr *cdr)
|
||||
{
|
||||
char *chan;
|
||||
struct ast_cdr *next;
|
||||
|
||||
while (cdr) {
|
||||
next = cdr->next;
|
||||
chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
|
||||
@@ -116,6 +364,8 @@ void ast_cdr_free(struct ast_cdr *cdr)
|
||||
ast_log(LOG_WARNING, "CDR on channel '%s' lacks end\n", chan);
|
||||
if (!cdr->start.tv_sec && !cdr->start.tv_usec)
|
||||
ast_log(LOG_WARNING, "CDR on channel '%s' lacks start\n", chan);
|
||||
|
||||
ast_cdr_free_vars(cdr, 0);
|
||||
free(cdr);
|
||||
cdr = next;
|
||||
}
|
||||
@@ -185,7 +435,7 @@ void ast_cdr_failed(struct ast_cdr *cdr)
|
||||
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);
|
||||
if(!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
|
||||
if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
|
||||
cdr->disposition = AST_CDR_FAILED;
|
||||
cdr = cdr->next;
|
||||
}
|
||||
@@ -223,7 +473,7 @@ void ast_cdr_setdestchan(struct ast_cdr *cdr, char *chann)
|
||||
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);
|
||||
if(!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
|
||||
if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
|
||||
strncpy(cdr->dstchannel, chann, sizeof(cdr->dstchannel) - 1);
|
||||
cdr = cdr->next;
|
||||
}
|
||||
@@ -233,7 +483,7 @@ void ast_cdr_setapp(struct ast_cdr *cdr, char *app, char *data)
|
||||
{
|
||||
char *chan;
|
||||
while (cdr) {
|
||||
if(!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
|
||||
if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
|
||||
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);
|
||||
@@ -253,7 +503,7 @@ int ast_cdr_setcid(struct ast_cdr *cdr, struct ast_channel *c)
|
||||
char tmp[AST_MAX_EXTENSION] = "";
|
||||
char *num;
|
||||
while (cdr) {
|
||||
if(!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
|
||||
if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
|
||||
/* Grab source from ANI or normal Caller*ID */
|
||||
if (c->cid.cid_ani)
|
||||
num = c->cid.cid_ani;
|
||||
@@ -279,13 +529,14 @@ int ast_cdr_setcid(struct ast_cdr *cdr, struct ast_channel *c)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c)
|
||||
{
|
||||
char *chan;
|
||||
char *num;
|
||||
char tmp[AST_MAX_EXTENSION] = "";
|
||||
while (cdr) {
|
||||
if(!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
|
||||
if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
|
||||
chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
|
||||
if (!ast_strlen_zero(cdr->channel))
|
||||
ast_log(LOG_WARNING, "CDR already initialized on '%s'\n", chan);
|
||||
@@ -380,7 +631,7 @@ int ast_cdr_setaccount(struct ast_channel *chan, const char *account)
|
||||
|
||||
strncpy(chan->accountcode, account, sizeof(chan->accountcode) - 1);
|
||||
while (cdr) {
|
||||
if(!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
|
||||
if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
|
||||
strncpy(cdr->accountcode, chan->accountcode, sizeof(cdr->accountcode) - 1);
|
||||
cdr = cdr->next;
|
||||
}
|
||||
@@ -404,7 +655,7 @@ int ast_cdr_setuserfield(struct ast_channel *chan, const char *userfield)
|
||||
struct ast_cdr *cdr = chan->cdr;
|
||||
|
||||
while (cdr) {
|
||||
if(!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
|
||||
if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
|
||||
strncpy(cdr->userfield, userfield, sizeof(cdr->userfield) - 1);
|
||||
cdr = cdr->next;
|
||||
}
|
||||
@@ -419,7 +670,7 @@ int ast_cdr_appenduserfield(struct ast_channel *chan, const char *userfield)
|
||||
{
|
||||
|
||||
int len = strlen(cdr->userfield);
|
||||
if(!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
|
||||
if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
|
||||
strncpy(cdr->userfield+len, userfield, sizeof(cdr->userfield) - len - 1);
|
||||
cdr = cdr->next;
|
||||
}
|
||||
@@ -433,7 +684,7 @@ int ast_cdr_update(struct ast_channel *c)
|
||||
char tmp[AST_MAX_EXTENSION] = "";
|
||||
/* Grab source from ANI or normal Caller*ID */
|
||||
while (cdr) {
|
||||
if(!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
|
||||
if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
|
||||
/* Grab source from ANI or normal Caller*ID */
|
||||
if (c->cid.cid_ani)
|
||||
num = c->cid.cid_ani;
|
||||
@@ -524,6 +775,12 @@ void ast_cdr_reset(struct ast_cdr *cdr, int flags)
|
||||
ast_cdr_end(cdr);
|
||||
ast_cdr_post(cdr);
|
||||
}
|
||||
|
||||
/* clear variables */
|
||||
if (! ast_test_flag(&tmp, AST_CDR_FLAG_KEEP_VARS)) {
|
||||
ast_cdr_free_vars(cdr, 0);
|
||||
}
|
||||
|
||||
/* Reset to initial state */
|
||||
ast_clear_flag(cdr, AST_FLAGS_ALL);
|
||||
memset(&cdr->start, 0, sizeof(cdr->start));
|
||||
|
9
cli.c
9
cli.c
@@ -651,10 +651,11 @@ static int handle_showchan(int fd, int argc, char *argv[])
|
||||
{
|
||||
struct ast_channel *c=NULL;
|
||||
struct timeval now;
|
||||
char buf[1024];
|
||||
char buf[2048];
|
||||
char cdrtime[256];
|
||||
long elapsed_seconds=0;
|
||||
int hour=0, min=0, sec=0;
|
||||
|
||||
if (argc != 3)
|
||||
return RESULT_SHOWUSAGE;
|
||||
gettimeofday(&now, NULL);
|
||||
@@ -709,9 +710,11 @@ static int handle_showchan(int fd, int argc, char *argv[])
|
||||
( c-> data ? (!ast_strlen_zero(c->data) ? c->data : "(Empty)") : "(None)"),
|
||||
(ast_test_flag(c, AST_FLAG_BLOCKING) ? c->blockproc : "(Not Blocking)"));
|
||||
if(pbx_builtin_serialize_variables(c,buf,sizeof(buf)))
|
||||
ast_cli(fd,"Variables:\n%s\n",buf);
|
||||
ast_cli(fd," Variables:\n%s\n",buf);
|
||||
if(c->cdr && ast_cdr_serialize_variables(c->cdr,buf, sizeof(buf), '=', '\n', 1))
|
||||
ast_cli(fd," CDR Variables:\n%s\n",buf);
|
||||
|
||||
ast_mutex_unlock(&c->lock);
|
||||
ast_mutex_unlock(&c->lock);
|
||||
break;
|
||||
}
|
||||
ast_mutex_unlock(&c->lock);
|
||||
|
@@ -22,14 +22,15 @@ MySQL.
|
||||
Applications
|
||||
------------
|
||||
|
||||
* SetAccount Set account code for billing
|
||||
* SetAMAFlags Sets AMA flags
|
||||
* NoCDR Make sure no CDR is saved for a specific call
|
||||
* ResetCDR Reset CDR
|
||||
* ForkCDR Save current CDR and start a new CDR for this call
|
||||
* Authenticate Authenticates and sets the account code
|
||||
* SetCDRUserField Set CDR user field
|
||||
* AppendCDRUserField Append data to CDR User field
|
||||
* SetAccount Set account code for billing
|
||||
* SetAMAFlags Sets AMA flags
|
||||
* NoCDR Make sure no CDR is saved for a specific call
|
||||
* ResetCDR Reset CDR
|
||||
* ForkCDR Save current CDR and start a new CDR for this call
|
||||
* Authenticate Authenticates and sets the account code
|
||||
* SetCDRUserField Set CDR user field
|
||||
* AppendCDRUserField Append data to CDR User field
|
||||
* SetVarCDR Set CDR Vars
|
||||
|
||||
For more information, use the "show application" command.
|
||||
You can set default account codes and AMA flags for devices in
|
||||
@@ -72,4 +73,53 @@ will report a short call time. If you want detailed records you must
|
||||
turn off IAX transfer, but unless your servers are very close together, you
|
||||
will definitely get a latency hit from doing so.
|
||||
|
||||
____________________________________
|
||||
CDR Variables
|
||||
------------------------------------
|
||||
|
||||
If the channel has a cdr, that cdr record has it's own set of variables which
|
||||
can be accessed just like channel variables. The following builtin variables
|
||||
are available.
|
||||
|
||||
${CDR(clid)} Caller ID
|
||||
${CDR(src)} Source
|
||||
${CDR(dst)} Destination
|
||||
${CDR(dcontext)} Destination context
|
||||
${CDR(channel)} Channel name
|
||||
${CDR(dstchannel)} Destination channel
|
||||
${CDR(lastapp)} Last app executed
|
||||
${CDR(lastdata)} Last app's arguments
|
||||
${CDR(start)} Time the call started.
|
||||
${CDR(answer)} Time the call was answered.
|
||||
${CDR(end)} Time the call ended.
|
||||
${CDR(duration)} Duration of the call.
|
||||
${CDR(billsec)} Duration of the call once it was answered.
|
||||
${CDR(disposition)} ANSWERED, NO ANSWER, BUSY
|
||||
${CDR(amaflags)} DOCUMENTATION, BILL, IGNORE etc
|
||||
${CDR(accountcode)} The channel's account code.
|
||||
${CDR(uniqueid)} The channel's unique id.
|
||||
${CDR(userfield)} The channels uses specified field.
|
||||
|
||||
|
||||
In addition, you can set your own extra variables with the application SetVarCDR(var=val)
|
||||
or a traditional SetVAR(CDR(var=val) to anything you want.
|
||||
|
||||
SetVar(CDR(var)=val) will set the var to all cdr in a stack of cdrs.
|
||||
|
||||
______________________________
|
||||
cdr_csv2
|
||||
------------------------------
|
||||
|
||||
This module is an experimental new cdr module to demonstrate the cdr vars.
|
||||
usage(
|
||||
|
||||
*) Create a file called cdr.conf and place it in your /etc/asterisk (or wherever your config files are) in the [cdr_csv2] section.
|
||||
*) Add an entry called format to indicate any format you want for the output.
|
||||
|
||||
The following format string will emulate the regular cdr file format:
|
||||
[cdr_csv2]
|
||||
|
||||
format => "${CDR(clid)}","${CDR(src)}","${CDR(dst)}","${CDR(dcontext)}","${CDR(channel)}","${CDR(dstchannel)}","${CDR(lastapp)}","${CDR(lastdata)}","${CDR(start)}","${CDR(answer)}","${CDR(end)}","${CDR(duration)}","${CDR(billsec)}","${CDR(disposition)}","${CDR(amaflags)}","${CDR(accountcode)}","${CDR(uniqueid)}","${CDR(userfield)}"
|
||||
|
||||
You can put anything you want as the value of format incuding new cdr vars you make up or any global variables.
|
||||
|
||||
|
@@ -456,3 +456,33 @@ ${OSPDEST} OSP Destination from Library
|
||||
${OSPTOKEN} OSP Token to use for call from Library
|
||||
${OSPRESULTS} Number of OSP results
|
||||
|
||||
____________________________________
|
||||
CDR Variables
|
||||
------------------------------------
|
||||
|
||||
If the channel has a cdr, that cdr record has it's own set of variables which
|
||||
can be accessed just like channel variables. The following builtin variables
|
||||
are available.
|
||||
|
||||
${CDR(clid)} Caller ID
|
||||
${CDR(src)} Source
|
||||
${CDR(dst)} Destination
|
||||
${CDR(dcontext)} Destination context
|
||||
${CDR(channel)} Channel name
|
||||
${CDR(dstchannel)} Destination channel
|
||||
${CDR(lastapp)} Last app executed
|
||||
${CDR(lastdata)} Last app's arguments
|
||||
${CDR(start)} Time the call started.
|
||||
${CDR(answer)} Time the call was answered.
|
||||
${CDR(end)} Time the call ended.
|
||||
${CDR(duration)} Duration of the call.
|
||||
${CDR(billsec)} Duration of the call once it was answered.
|
||||
${CDR(disposition)} ANSWERED, NO ANSWER, BUSY
|
||||
${CDR(amaflags)} DOCUMENTATION, BILL, IGNORE etc
|
||||
${CDR(accountcode)} The channel's account code.
|
||||
${CDR(uniqueid)} The channel's unique id.
|
||||
${CDR(userfield)} The channels uses specified field.
|
||||
|
||||
|
||||
In addition, you can set your own extra variables with a traditional
|
||||
SetVAR(CDR(var)=val) to anything you want.
|
||||
|
@@ -19,10 +19,12 @@
|
||||
|
||||
#include <asterisk/channel.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#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_SETVAR (1 << 4)
|
||||
#define AST_CDR_FLAG_RECUR (1 << 5)
|
||||
|
||||
#define AST_CDR_NOANSWER (1 << 0)
|
||||
#define AST_CDR_BUSY (1 << 1)
|
||||
@@ -37,6 +39,7 @@
|
||||
#define AST_MAX_USER_FIELD 256
|
||||
|
||||
struct ast_channel;
|
||||
AST_LIST_HEAD(varshead,ast_var_t);
|
||||
|
||||
/*! Responsible for call detail data */
|
||||
struct ast_cdr {
|
||||
@@ -78,9 +81,19 @@ struct ast_cdr {
|
||||
char uniqueid[32];
|
||||
/* User field */
|
||||
char userfield[AST_MAX_USER_FIELD];
|
||||
|
||||
/* A linked list for variables */
|
||||
struct varshead varshead;
|
||||
|
||||
struct ast_cdr *next;
|
||||
};
|
||||
|
||||
extern void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret, char *workspace, int workspacelen, int recur);
|
||||
extern int ast_cdr_setvar(struct ast_cdr *cdr, const char *name, char *value, int recur);
|
||||
extern int ast_cdr_serialize_variables(struct ast_cdr *cdr, char *buf, size_t size, char delim, char sep, int recur);
|
||||
extern void ast_cdr_free_vars(struct ast_cdr *cdr, int recur);
|
||||
extern int ast_cdr_copy_vars(struct ast_cdr *to_cdr, struct ast_cdr *from_cdr);
|
||||
|
||||
typedef int (*ast_cdrbe)(struct ast_cdr *cdr);
|
||||
|
||||
/*! Allocate a record */
|
||||
|
@@ -211,7 +211,7 @@ struct ast_channel {
|
||||
int hangupcause;
|
||||
|
||||
/* A linked list for variables */
|
||||
AST_LIST_HEAD(varshead,ast_var_t) varshead;
|
||||
struct varshead varshead;
|
||||
|
||||
unsigned int callgroup;
|
||||
unsigned int pickupgroup;
|
||||
|
97
pbx.c
97
pbx.c
@@ -321,8 +321,11 @@ static struct pbx_builtin {
|
||||
{ "ResetCDR", pbx_builtin_resetcdr,
|
||||
"Resets the Call Data Record",
|
||||
" ResetCDR([options]): Causes the Call Data Record to be reset, optionally\n"
|
||||
"storing the current CDR before zeroing it out (if 'w' option is specifed).\n"
|
||||
"record WILL be stored.\nAlways returns 0.\n"
|
||||
"storing the current CDR before zeroing it out\b"
|
||||
"(if 'w' option is specifed) record will be stored.\n"
|
||||
"(if 'a' option is specifed) any stacked records will be stored.\n"
|
||||
"(if 'v' option is specifed) any variables will be saved.\n"
|
||||
"Always returns 0.\n"
|
||||
},
|
||||
|
||||
{ "ResponseTimeout", pbx_builtin_rtimeout,
|
||||
@@ -393,7 +396,14 @@ static struct pbx_builtin {
|
||||
|
||||
{ "SetVar", pbx_builtin_setvar,
|
||||
"Set variable to value",
|
||||
" SetVar(#n=value): Sets variable n to value. If prefixed with _, single\n"
|
||||
" SetVar(#n1=value|#n2=value|..[|options]) Set a variables to a CDR.\n"
|
||||
"You can specify an endless list of name / value pairs to be set as channel variables.\n"
|
||||
"The last arg (if it doesn't contain an '=' ) is intrepreted as a string of\n"
|
||||
"options. Valid Options:\n"
|
||||
" - c - CDR, if set set the var as a CDR variable also.\n"
|
||||
" - r - Recursive CDR, if there are any stacked CDRs, also apply to all as a cdr var.\n"
|
||||
" - g - Set a global variable not a channel variable.\n"
|
||||
" #n=value: Sets variable n to value. If prefixed with _, single\n"
|
||||
"inheritance assumed. If prefixed with __, infinite inheritance is assumed.\n" },
|
||||
|
||||
{ "ImportVar", pbx_builtin_importvar,
|
||||
@@ -820,8 +830,17 @@ void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, c
|
||||
if (c)
|
||||
headp=&c->varshead;
|
||||
*ret=NULL;
|
||||
/* Now we have the variable name on cp3 */
|
||||
if (!strncasecmp(var,"LEN(",4)) { /* ${LEN(<string>)} */
|
||||
if (c && c->cdr && !strncasecmp(var, "CDR(", 4)) { /* ${CDR(NEET)} */
|
||||
char *vtmp, *nt;
|
||||
|
||||
if ((vtmp = ast_strdupa((char *) var + 4)) && (nt = strchr(vtmp, ')'))) {
|
||||
*nt = '\0';
|
||||
ast_cdr_getvar(c->cdr, vtmp, ret, workspace, workspacelen, 1);
|
||||
} else
|
||||
ast_log(LOG_WARNING, "Invalid CDR variable.\n");
|
||||
return;
|
||||
} else if (!strncasecmp(var,"LEN(",4)) { /* ${LEN(<string>)} */
|
||||
/* Now we have the variable name on cp3 */
|
||||
int len=strlen(var);
|
||||
int len_len=4;
|
||||
if (strrchr(var,')')) {
|
||||
@@ -4900,6 +4919,8 @@ static int pbx_builtin_resetcdr(struct ast_channel *chan, void *data)
|
||||
flags |= AST_CDR_FLAG_POSTED;
|
||||
if(strchr((char *)data, 'a'))
|
||||
flags |= AST_CDR_FLAG_LOCKED;
|
||||
if(strchr((char *)data, 'v'))
|
||||
flags |= AST_CDR_FLAG_KEEP_VARS;
|
||||
}
|
||||
|
||||
ast_cdr_reset(chan->cdr, flags);
|
||||
@@ -5202,8 +5223,25 @@ void pbx_builtin_setvar_helper(struct ast_channel *chan, char *name, char *value
|
||||
struct ast_var_t *newvariable;
|
||||
struct varshead *headp;
|
||||
|
||||
if (chan)
|
||||
|
||||
if (chan) {
|
||||
headp = &chan->varshead;
|
||||
if (!strncasecmp(name, "CDR(", 4)) { /* CDR VARS */
|
||||
char *vtmp, *nt;
|
||||
int recur = 0;
|
||||
if ((vtmp = ast_strdupa((char *) name + 4)) && (nt = strchr(vtmp, ')'))) {
|
||||
*nt = '\0';
|
||||
if(vtmp[0] == '-') {
|
||||
vtmp++;
|
||||
recur = 1;
|
||||
}
|
||||
ast_cdr_setvar(chan->cdr, vtmp, value, recur);
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Invalid CDR variable.\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
headp = &globals;
|
||||
|
||||
@@ -5226,21 +5264,44 @@ void pbx_builtin_setvar_helper(struct ast_channel *chan, char *name, char *value
|
||||
|
||||
int pbx_builtin_setvar(struct ast_channel *chan, void *data)
|
||||
{
|
||||
char *name;
|
||||
char *value;
|
||||
char *stringp=NULL;
|
||||
char *name, *value, *mydata, *next, *fstr = NULL;
|
||||
struct ast_flags flags = {0};
|
||||
|
||||
if (!data || ast_strlen_zero(data)) {
|
||||
if (data && !ast_strlen_zero(data) && chan->cdr && (mydata = ast_strdupa(data))) {
|
||||
next = mydata;
|
||||
while(next) {
|
||||
name = next;
|
||||
if ((next = strchr(next, '|'))) {
|
||||
*next = '\0';
|
||||
next++;
|
||||
}
|
||||
|
||||
if ((value = strchr(name, '='))) {
|
||||
*value = '\0';
|
||||
value++;
|
||||
if( fstr && strchr(fstr, 'g') ) {
|
||||
pbx_builtin_setvar_helper(NULL, name, value);
|
||||
}
|
||||
else {
|
||||
pbx_builtin_setvar_helper(chan, name, value);
|
||||
if (ast_test_flag(&flags, AST_CDR_FLAG_SETVAR)) {
|
||||
ast_cdr_setvar(chan->cdr, name, value, ast_test_flag(&flags, AST_CDR_FLAG_RECUR));
|
||||
}
|
||||
}
|
||||
} else if (!next) {
|
||||
name++;
|
||||
if (strchr(name, 'c') ) {
|
||||
ast_set_flag(&flags, AST_CDR_FLAG_SETVAR);
|
||||
} else if (strchr(name, 'r') ) {
|
||||
ast_set_flag(&flags, AST_CDR_FLAG_RECUR | AST_CDR_FLAG_SETVAR);
|
||||
}
|
||||
} else
|
||||
ast_log(LOG_WARNING, "Ignoring entry '%s' with no = (and not last 'options' entry)\n", name);
|
||||
}
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Ignoring, since there is no variable to set\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
stringp = ast_strdupa(data);
|
||||
name = strsep(&stringp,"=");
|
||||
value = strsep(&stringp,"\0");
|
||||
|
||||
pbx_builtin_setvar_helper(chan, name, value);
|
||||
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user