diff --git a/apps/app_forkcdr.c b/apps/app_forkcdr.c index c2ef00b46918945c7995767bf0a47dd5011e455f..64679b551a81a7795c9eb71a38abdf93003d3912 100755 --- a/apps/app_forkcdr.c +++ b/apps/app_forkcdr.c @@ -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); diff --git a/cdr.c b/cdr.c index 428b7790151fd9cabef6701feb3546b6db35d66a..d23011364a5c75dfc99784c42d56b4165a4a7f3f 100755 --- a/cdr.c +++ b/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)); diff --git a/cli.c b/cli.c index c011730b5fdc8267c4fd455d762dd132ebe366e8..8dd57b34c9d8ae6a671c1b2c807d44ad98f8561f 100755 --- a/cli.c +++ b/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); diff --git a/doc/README.cdr b/doc/README.cdr index 4d77c9da2beda459fc6c14a668b45b37449242b0..8dadf22b4b9521f508a50835fec3156cd3878287 100755 --- a/doc/README.cdr +++ b/doc/README.cdr @@ -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. diff --git a/doc/README.variables b/doc/README.variables index fb9e6f913da8a6ce850473ce657d21c54da0ae03..4195f299bb77a6454454268797e6b42a0c2677b3 100755 --- a/doc/README.variables +++ b/doc/README.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. diff --git a/include/asterisk/cdr.h b/include/asterisk/cdr.h index 8e712bc766ba566d6f41ea1c5dfde9f038abd605..0f99e2d3f3a971685cc70eeb1f451eb32af1900b 100755 --- a/include/asterisk/cdr.h +++ b/include/asterisk/cdr.h @@ -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 */ diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 563684d7943a2f51387a9a422c1a94e59a3cdd2d..695e5a761a0e711f835f4450f6a78f5481f87f7c 100755 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -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; diff --git a/pbx.c b/pbx.c index cdd772a59d853c7732037da49e00f82b4a2d2df0..7b876035da5abfd5afd15baf07baba69434d03a7 100755 --- a/pbx.c +++ b/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); }