00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034 #include "asterisk.h"
00035
00036 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 152811 $")
00037
00038 #include <unistd.h>
00039 #include <stdlib.h>
00040 #include <string.h>
00041 #include <stdio.h>
00042 #include <signal.h>
00043
00044 #include "asterisk/lock.h"
00045 #include "asterisk/channel.h"
00046 #include "asterisk/cdr.h"
00047 #include "asterisk/logger.h"
00048 #include "asterisk/callerid.h"
00049 #include "asterisk/causes.h"
00050 #include "asterisk/options.h"
00051 #include "asterisk/linkedlists.h"
00052 #include "asterisk/utils.h"
00053 #include "asterisk/sched.h"
00054 #include "asterisk/config.h"
00055 #include "asterisk/cli.h"
00056 #include "asterisk/stringfields.h"
00057
00058
00059 int ast_default_amaflags = AST_CDR_DOCUMENTATION;
00060 char ast_default_accountcode[AST_MAX_ACCOUNT_CODE];
00061
00062 struct ast_cdr_beitem {
00063 char name[20];
00064 char desc[80];
00065 ast_cdrbe be;
00066 AST_LIST_ENTRY(ast_cdr_beitem) list;
00067 };
00068
00069 static AST_LIST_HEAD_STATIC(be_list, ast_cdr_beitem);
00070
00071 struct ast_cdr_batch_item {
00072 struct ast_cdr *cdr;
00073 struct ast_cdr_batch_item *next;
00074 };
00075
00076 static struct ast_cdr_batch {
00077 int size;
00078 struct ast_cdr_batch_item *head;
00079 struct ast_cdr_batch_item *tail;
00080 } *batch = NULL;
00081
00082 static struct sched_context *sched;
00083 static int cdr_sched = -1;
00084 static pthread_t cdr_thread = AST_PTHREADT_NULL;
00085
00086 #define BATCH_SIZE_DEFAULT 100
00087 #define BATCH_TIME_DEFAULT 300
00088 #define BATCH_SCHEDULER_ONLY_DEFAULT 0
00089 #define BATCH_SAFE_SHUTDOWN_DEFAULT 1
00090
00091 static int enabled;
00092 static int unanswered;
00093 static int batchmode;
00094 static int batchsize;
00095 static int batchtime;
00096 static int batchscheduleronly;
00097 static int batchsafeshutdown;
00098
00099 AST_MUTEX_DEFINE_STATIC(cdr_batch_lock);
00100
00101
00102 AST_MUTEX_DEFINE_STATIC(cdr_pending_lock);
00103 static ast_cond_t cdr_pending_cond;
00104
00105
00106
00107
00108
00109 int ast_cdr_register(const char *name, const char *desc, ast_cdrbe be)
00110 {
00111 struct ast_cdr_beitem *i;
00112
00113 if (!name)
00114 return -1;
00115 if (!be) {
00116 ast_log(LOG_WARNING, "CDR engine '%s' lacks backend\n", name);
00117 return -1;
00118 }
00119
00120 AST_LIST_LOCK(&be_list);
00121 AST_LIST_TRAVERSE(&be_list, i, list) {
00122 if (!strcasecmp(name, i->name))
00123 break;
00124 }
00125 AST_LIST_UNLOCK(&be_list);
00126
00127 if (i) {
00128 ast_log(LOG_WARNING, "Already have a CDR backend called '%s'\n", name);
00129 return -1;
00130 }
00131
00132 if (!(i = ast_calloc(1, sizeof(*i))))
00133 return -1;
00134
00135 i->be = be;
00136 ast_copy_string(i->name, name, sizeof(i->name));
00137 ast_copy_string(i->desc, desc, sizeof(i->desc));
00138
00139 AST_LIST_LOCK(&be_list);
00140 AST_LIST_INSERT_HEAD(&be_list, i, list);
00141 AST_LIST_UNLOCK(&be_list);
00142
00143 return 0;
00144 }
00145
00146
00147 void ast_cdr_unregister(const char *name)
00148 {
00149 struct ast_cdr_beitem *i = NULL;
00150
00151 AST_LIST_LOCK(&be_list);
00152 AST_LIST_TRAVERSE_SAFE_BEGIN(&be_list, i, list) {
00153 if (!strcasecmp(name, i->name)) {
00154 AST_LIST_REMOVE_CURRENT(&be_list, list);
00155 if (option_verbose > 1)
00156 ast_verbose(VERBOSE_PREFIX_2 "Unregistered '%s' CDR backend\n", name);
00157 free(i);
00158 break;
00159 }
00160 }
00161 AST_LIST_TRAVERSE_SAFE_END;
00162 AST_LIST_UNLOCK(&be_list);
00163 }
00164
00165 int ast_cdr_isset_unanswered(void)
00166 {
00167 return unanswered;
00168 }
00169
00170
00171
00172
00173 struct ast_cdr *ast_cdr_dup(struct ast_cdr *cdr)
00174 {
00175 struct ast_cdr *newcdr;
00176
00177 if (!cdr)
00178 return NULL;
00179 newcdr = ast_cdr_alloc();
00180 if (!newcdr)
00181 return NULL;
00182
00183 memcpy(newcdr, cdr, sizeof(*newcdr));
00184
00185 memset(&newcdr->varshead, 0, sizeof(newcdr->varshead));
00186 ast_cdr_copy_vars(newcdr, cdr);
00187 newcdr->next = NULL;
00188
00189 return newcdr;
00190 }
00191
00192 static const char *ast_cdr_getvar_internal(struct ast_cdr *cdr, const char *name, int recur)
00193 {
00194 if (ast_strlen_zero(name))
00195 return NULL;
00196
00197 for (; cdr; cdr = recur ? cdr->next : NULL) {
00198 struct ast_var_t *variables;
00199 struct varshead *headp = &cdr->varshead;
00200 AST_LIST_TRAVERSE(headp, variables, entries) {
00201 if (!strcasecmp(name, ast_var_name(variables)))
00202 return ast_var_value(variables);
00203 }
00204 }
00205
00206 return NULL;
00207 }
00208
00209 static void cdr_get_tv(struct timeval tv, const char *fmt, char *buf, int bufsize)
00210 {
00211 if (fmt == NULL) {
00212 snprintf(buf, bufsize, "%ld.%06ld", (long)tv.tv_sec, (long)tv.tv_usec);
00213 } else {
00214 time_t t = tv.tv_sec;
00215 if (t) {
00216 struct tm tm;
00217
00218 ast_localtime(&t, &tm, NULL);
00219 strftime(buf, bufsize, fmt, &tm);
00220 }
00221 }
00222 }
00223
00224
00225 void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret, char *workspace, int workspacelen, int recur, int raw)
00226 {
00227 const char *fmt = "%Y-%m-%d %T";
00228 const char *varbuf;
00229
00230 if (!cdr)
00231 return;
00232
00233 *ret = NULL;
00234
00235
00236
00237 if (!strcasecmp(name, "clid"))
00238 ast_copy_string(workspace, cdr->clid, workspacelen);
00239 else if (!strcasecmp(name, "src"))
00240 ast_copy_string(workspace, cdr->src, workspacelen);
00241 else if (!strcasecmp(name, "dst"))
00242 ast_copy_string(workspace, cdr->dst, workspacelen);
00243 else if (!strcasecmp(name, "dcontext"))
00244 ast_copy_string(workspace, cdr->dcontext, workspacelen);
00245 else if (!strcasecmp(name, "channel"))
00246 ast_copy_string(workspace, cdr->channel, workspacelen);
00247 else if (!strcasecmp(name, "dstchannel"))
00248 ast_copy_string(workspace, cdr->dstchannel, workspacelen);
00249 else if (!strcasecmp(name, "lastapp"))
00250 ast_copy_string(workspace, cdr->lastapp, workspacelen);
00251 else if (!strcasecmp(name, "lastdata"))
00252 ast_copy_string(workspace, cdr->lastdata, workspacelen);
00253 else if (!strcasecmp(name, "start"))
00254 cdr_get_tv(cdr->start, raw ? NULL : fmt, workspace, workspacelen);
00255 else if (!strcasecmp(name, "answer"))
00256 cdr_get_tv(cdr->answer, raw ? NULL : fmt, workspace, workspacelen);
00257 else if (!strcasecmp(name, "end"))
00258 cdr_get_tv(cdr->end, raw ? NULL : fmt, workspace, workspacelen);
00259 else if (!strcasecmp(name, "duration"))
00260 snprintf(workspace, workspacelen, "%ld", cdr->duration);
00261 else if (!strcasecmp(name, "billsec"))
00262 snprintf(workspace, workspacelen, "%ld", cdr->billsec);
00263 else if (!strcasecmp(name, "disposition")) {
00264 if (raw) {
00265 snprintf(workspace, workspacelen, "%ld", cdr->disposition);
00266 } else {
00267 ast_copy_string(workspace, ast_cdr_disp2str(cdr->disposition), workspacelen);
00268 }
00269 } else if (!strcasecmp(name, "amaflags")) {
00270 if (raw) {
00271 snprintf(workspace, workspacelen, "%ld", cdr->amaflags);
00272 } else {
00273 ast_copy_string(workspace, ast_cdr_flags2str(cdr->amaflags), workspacelen);
00274 }
00275 } else if (!strcasecmp(name, "accountcode"))
00276 ast_copy_string(workspace, cdr->accountcode, workspacelen);
00277 else if (!strcasecmp(name, "uniqueid"))
00278 ast_copy_string(workspace, cdr->uniqueid, workspacelen);
00279 else if (!strcasecmp(name, "userfield"))
00280 ast_copy_string(workspace, cdr->userfield, workspacelen);
00281 else if ((varbuf = ast_cdr_getvar_internal(cdr, name, recur)))
00282 ast_copy_string(workspace, varbuf, workspacelen);
00283 else
00284 workspace[0] = '\0';
00285
00286 if (!ast_strlen_zero(workspace))
00287 *ret = workspace;
00288 }
00289
00290
00291 static const char *cdr_readonly_vars[] = { "clid", "src", "dst", "dcontext", "channel", "dstchannel",
00292 "lastapp", "lastdata", "start", "answer", "end", "duration",
00293 "billsec", "disposition", "amaflags", "accountcode", "uniqueid",
00294 "userfield", NULL };
00295
00296
00297
00298 int ast_cdr_setvar(struct ast_cdr *cdr, const char *name, const char *value, int recur)
00299 {
00300 struct ast_var_t *newvariable;
00301 struct varshead *headp;
00302 int x;
00303
00304 if (!cdr)
00305 return -1;
00306
00307 for(x = 0; cdr_readonly_vars[x]; x++) {
00308 if (!strcasecmp(name, cdr_readonly_vars[x])) {
00309 ast_log(LOG_ERROR, "Attempt to set the '%s' read-only variable!.\n", name);
00310 return -1;
00311 }
00312 }
00313
00314 if (!cdr) {
00315 ast_log(LOG_ERROR, "Attempt to set a variable on a nonexistent CDR record.\n");
00316 return -1;
00317 }
00318
00319 for (; cdr; cdr = recur ? cdr->next : NULL) {
00320 if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00321 continue;
00322 headp = &cdr->varshead;
00323 AST_LIST_TRAVERSE_SAFE_BEGIN(headp, newvariable, entries) {
00324 if (!strcasecmp(ast_var_name(newvariable), name)) {
00325
00326 AST_LIST_REMOVE_CURRENT(headp, entries);
00327 ast_var_delete(newvariable);
00328 break;
00329 }
00330 }
00331 AST_LIST_TRAVERSE_SAFE_END;
00332
00333 if (value) {
00334 newvariable = ast_var_assign(name, value);
00335 AST_LIST_INSERT_HEAD(headp, newvariable, entries);
00336 }
00337 }
00338
00339 return 0;
00340 }
00341
00342 int ast_cdr_copy_vars(struct ast_cdr *to_cdr, struct ast_cdr *from_cdr)
00343 {
00344 struct ast_var_t *variables, *newvariable = NULL;
00345 struct varshead *headpa, *headpb;
00346 const char *var, *val;
00347 int x = 0;
00348
00349 if (!to_cdr || !from_cdr)
00350 return 0;
00351
00352 headpa = &from_cdr->varshead;
00353 headpb = &to_cdr->varshead;
00354
00355 AST_LIST_TRAVERSE(headpa,variables,entries) {
00356 if (variables &&
00357 (var = ast_var_name(variables)) && (val = ast_var_value(variables)) &&
00358 !ast_strlen_zero(var) && !ast_strlen_zero(val)) {
00359 newvariable = ast_var_assign(var, val);
00360 AST_LIST_INSERT_HEAD(headpb, newvariable, entries);
00361 x++;
00362 }
00363 }
00364
00365 return x;
00366 }
00367
00368 int ast_cdr_serialize_variables(struct ast_cdr *cdr, char *buf, size_t size, char delim, char sep, int recur)
00369 {
00370 struct ast_var_t *variables;
00371 const char *var, *val;
00372 char *tmp;
00373 char workspace[256];
00374 int total = 0, x = 0, i;
00375
00376 memset(buf, 0, size);
00377
00378 for (; cdr; cdr = recur ? cdr->next : NULL) {
00379 if (++x > 1)
00380 ast_build_string(&buf, &size, "\n");
00381
00382 AST_LIST_TRAVERSE(&cdr->varshead, variables, entries) {
00383 if (variables &&
00384 (var = ast_var_name(variables)) && (val = ast_var_value(variables)) &&
00385 !ast_strlen_zero(var) && !ast_strlen_zero(val)) {
00386 if (ast_build_string(&buf, &size, "level %d: %s%c%s%c", x, var, delim, val, sep)) {
00387 ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
00388 break;
00389 } else
00390 total++;
00391 } else
00392 break;
00393 }
00394
00395 for (i = 0; cdr_readonly_vars[i]; i++) {
00396 workspace[0] = 0;
00397 ast_cdr_getvar(cdr, cdr_readonly_vars[i], &tmp, workspace, sizeof(workspace), 0, 0);
00398 if (!tmp)
00399 continue;
00400
00401 if (ast_build_string(&buf, &size, "level %d: %s%c%s%c", x, cdr_readonly_vars[i], delim, tmp, sep)) {
00402 ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
00403 break;
00404 } else
00405 total++;
00406 }
00407 }
00408
00409 return total;
00410 }
00411
00412
00413 void ast_cdr_free_vars(struct ast_cdr *cdr, int recur)
00414 {
00415
00416
00417 for (; cdr; cdr = recur ? cdr->next : NULL) {
00418 struct ast_var_t *vardata;
00419 struct varshead *headp = &cdr->varshead;
00420 while ((vardata = AST_LIST_REMOVE_HEAD(headp, entries)))
00421 ast_var_delete(vardata);
00422 }
00423 }
00424
00425
00426 static void check_post(struct ast_cdr *cdr)
00427 {
00428 if (!cdr)
00429 return;
00430 if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
00431 ast_log(LOG_NOTICE, "CDR on channel '%s' already posted\n", S_OR(cdr->channel, "<unknown>"));
00432 }
00433
00434 void ast_cdr_free(struct ast_cdr *cdr)
00435 {
00436
00437 while (cdr) {
00438 struct ast_cdr *next = cdr->next;
00439 char *chan = S_OR(cdr->channel, "<unknown>");
00440 if (option_verbose > 1 && !ast_test_flag(cdr, AST_CDR_FLAG_POSTED) && !ast_test_flag(cdr, AST_CDR_FLAG_POST_DISABLED))
00441 ast_verbose(VERBOSE_PREFIX_2 "CDR on channel '%s' not posted\n", chan);
00442 if (option_verbose > 1 && ast_tvzero(cdr->end))
00443 ast_verbose(VERBOSE_PREFIX_2 "CDR on channel '%s' lacks end\n", chan);
00444 if (option_verbose > 1 && ast_tvzero(cdr->start))
00445 ast_verbose(VERBOSE_PREFIX_2 "CDR on channel '%s' lacks start\n", chan);
00446
00447 ast_cdr_free_vars(cdr, 0);
00448 free(cdr);
00449 cdr = next;
00450 }
00451 }
00452
00453
00454 void ast_cdr_discard(struct ast_cdr *cdr)
00455 {
00456 while (cdr) {
00457 struct ast_cdr *next = cdr->next;
00458
00459 ast_cdr_free_vars(cdr, 0);
00460 free(cdr);
00461 cdr = next;
00462 }
00463 }
00464
00465 struct ast_cdr *ast_cdr_alloc(void)
00466 {
00467 struct ast_cdr *x = ast_calloc(1, sizeof(struct ast_cdr));
00468 if (!x)
00469 ast_log(LOG_ERROR,"Allocation Failure for a CDR!\n");
00470 return x;
00471 }
00472
00473 static void cdr_merge_vars(struct ast_cdr *to, struct ast_cdr *from)
00474 {
00475 struct ast_var_t *variablesfrom,*variablesto;
00476 struct varshead *headpfrom = &to->varshead;
00477 struct varshead *headpto = &from->varshead;
00478 AST_LIST_TRAVERSE_SAFE_BEGIN(headpfrom, variablesfrom, entries) {
00479
00480 const char *fromvarname = NULL, *fromvarval = NULL;
00481 const char *tovarname = NULL, *tovarval = NULL;
00482 fromvarname = ast_var_name(variablesfrom);
00483 fromvarval = ast_var_value(variablesfrom);
00484 tovarname = 0;
00485
00486
00487 AST_LIST_TRAVERSE(headpto, variablesto, entries) {
00488
00489
00490 if ( strcasecmp(fromvarname, ast_var_name(variablesto)) == 0 ) {
00491 tovarname = ast_var_name(variablesto);
00492 tovarval = ast_var_value(variablesto);
00493 break;
00494 }
00495 }
00496 if (tovarname && strcasecmp(fromvarval,tovarval) != 0) {
00497 ast_log(LOG_NOTICE, "Merging CDR's: variable %s value %s dropped in favor of value %s\n", tovarname, fromvarval, tovarval);
00498 continue;
00499 } else if (tovarname && strcasecmp(fromvarval,tovarval) == 0)
00500 continue;
00501
00502
00503 AST_LIST_REMOVE_CURRENT(headpfrom, entries);
00504 AST_LIST_INSERT_HEAD(headpto, variablesfrom, entries);
00505 }
00506 AST_LIST_TRAVERSE_SAFE_END;
00507 }
00508
00509 void ast_cdr_merge(struct ast_cdr *to, struct ast_cdr *from)
00510 {
00511 struct ast_cdr *zcdr;
00512 struct ast_cdr *lto = NULL;
00513 struct ast_cdr *lfrom = NULL;
00514 int discard_from = 0;
00515
00516 if (!to || !from)
00517 return;
00518
00519
00520 if (ast_test_flag(to, AST_CDR_FLAG_LOCKED)) {
00521 zcdr = to;
00522 while (to->next) {
00523 lto = to;
00524 to = to->next;
00525 }
00526
00527 if (ast_test_flag(to, AST_CDR_FLAG_LOCKED)) {
00528 ast_log(LOG_WARNING, "Merging into locked CDR... no choice.");
00529 to = zcdr;
00530 lto = NULL;
00531 }
00532 }
00533
00534 if (ast_test_flag(from, AST_CDR_FLAG_LOCKED)) {
00535 discard_from = 1;
00536 if (lto) {
00537 struct ast_cdr *llfrom = NULL;
00538
00539 lto->next = from;
00540 lfrom = from;
00541 while (lfrom && lfrom->next) {
00542 if (!lfrom->next->next)
00543 llfrom = lfrom;
00544 lfrom = lfrom->next;
00545 }
00546
00547 llfrom->next = to;
00548 from = lfrom;
00549 } else {
00550
00551 struct ast_cdr tcdr;
00552 struct ast_cdr *llfrom = NULL;
00553 memcpy(&tcdr, to, sizeof(tcdr));
00554
00555 memcpy(to, from, sizeof(*to));
00556 lfrom = from;
00557 while (lfrom && lfrom->next) {
00558 if (!lfrom->next->next)
00559 llfrom = lfrom;
00560 lfrom = lfrom->next;
00561 }
00562 from->next = NULL;
00563
00564 if (llfrom == from)
00565 to = to->next = ast_cdr_dup(&tcdr);
00566 else
00567 to = llfrom->next = ast_cdr_dup(&tcdr);
00568 from = lfrom;
00569 }
00570 }
00571
00572 if (!ast_tvzero(from->start)) {
00573 if (!ast_tvzero(to->start)) {
00574 if (ast_tvcmp(to->start, from->start) > 0 ) {
00575 to->start = from->start;
00576 from->start = ast_tv(0,0);
00577 }
00578
00579 } else {
00580 to->start = from->start;
00581 from->start = ast_tv(0,0);
00582 }
00583 }
00584 if (!ast_tvzero(from->answer)) {
00585 if (!ast_tvzero(to->answer)) {
00586 if (ast_tvcmp(to->answer, from->answer) > 0 ) {
00587 to->answer = from->answer;
00588 from->answer = ast_tv(0,0);
00589 }
00590
00591 } else {
00592 to->answer = from->answer;
00593 from->answer = ast_tv(0,0);
00594 }
00595 }
00596 if (!ast_tvzero(from->end)) {
00597 if (!ast_tvzero(to->end)) {
00598 if (ast_tvcmp(to->end, from->end) < 0 ) {
00599 to->end = from->end;
00600 from->end = ast_tv(0,0);
00601 to->duration = to->end.tv_sec - to->start.tv_sec;
00602 to->billsec = ast_tvzero(to->answer) ? 0 : to->end.tv_sec - to->answer.tv_sec;
00603 }
00604
00605 } else {
00606 to->end = from->end;
00607 from->end = ast_tv(0,0);
00608 to->duration = to->end.tv_sec - to->start.tv_sec;
00609 to->billsec = ast_tvzero(to->answer) ? 0 : to->end.tv_sec - to->answer.tv_sec;
00610 }
00611 }
00612 if (to->disposition < from->disposition) {
00613 to->disposition = from->disposition;
00614 from->disposition = AST_CDR_NOANSWER;
00615 }
00616 if (ast_strlen_zero(to->lastapp) && !ast_strlen_zero(from->lastapp)) {
00617 ast_copy_string(to->lastapp, from->lastapp, sizeof(to->lastapp));
00618 from->lastapp[0] = 0;
00619 }
00620 if (ast_strlen_zero(to->lastdata) && !ast_strlen_zero(from->lastdata)) {
00621 ast_copy_string(to->lastdata, from->lastdata, sizeof(to->lastdata));
00622 from->lastdata[0] = 0;
00623 }
00624 if (ast_strlen_zero(to->dcontext) && !ast_strlen_zero(from->dcontext)) {
00625 ast_copy_string(to->dcontext, from->dcontext, sizeof(to->dcontext));
00626 from->dcontext[0] = 0;
00627 }
00628 if (ast_strlen_zero(to->dstchannel) && !ast_strlen_zero(from->dstchannel)) {
00629 ast_copy_string(to->dstchannel, from->dstchannel, sizeof(to->dstchannel));
00630 from->dstchannel[0] = 0;
00631 }
00632 if (!ast_strlen_zero(from->channel) && (ast_strlen_zero(to->channel) || !strncasecmp(from->channel, "Agent/", 6))) {
00633 ast_copy_string(to->channel, from->channel, sizeof(to->channel));
00634 from->channel[0] = 0;
00635 }
00636 if (ast_strlen_zero(to->src) && !ast_strlen_zero(from->src)) {
00637 ast_copy_string(to->src, from->src, sizeof(to->src));
00638 from->src[0] = 0;
00639 }
00640 if (ast_strlen_zero(to->clid) && !ast_strlen_zero(from->clid)) {
00641 ast_copy_string(to->clid, from->clid, sizeof(to->clid));
00642 from->clid[0] = 0;
00643 }
00644 if (ast_strlen_zero(to->dst) && !ast_strlen_zero(from->dst)) {
00645 ast_copy_string(to->dst, from->dst, sizeof(to->dst));
00646 from->dst[0] = 0;
00647 }
00648 if (!to->amaflags)
00649 to->amaflags = AST_CDR_DOCUMENTATION;
00650 if (!from->amaflags)
00651 from->amaflags = AST_CDR_DOCUMENTATION;
00652 if (ast_test_flag(from, AST_CDR_FLAG_LOCKED) || (to->amaflags == AST_CDR_DOCUMENTATION && from->amaflags != AST_CDR_DOCUMENTATION)) {
00653 to->amaflags = from->amaflags;
00654 }
00655 if (ast_test_flag(from, AST_CDR_FLAG_LOCKED) || (ast_strlen_zero(to->accountcode) && !ast_strlen_zero(from->accountcode))) {
00656 ast_copy_string(to->accountcode, from->accountcode, sizeof(to->accountcode));
00657 }
00658 if (ast_test_flag(from, AST_CDR_FLAG_LOCKED) || (ast_strlen_zero(to->userfield) && !ast_strlen_zero(from->userfield))) {
00659 ast_copy_string(to->userfield, from->userfield, sizeof(to->userfield));
00660 }
00661
00662 cdr_merge_vars(from, to);
00663
00664 if (ast_test_flag(from, AST_CDR_FLAG_KEEP_VARS))
00665 ast_set_flag(to, AST_CDR_FLAG_KEEP_VARS);
00666 if (ast_test_flag(from, AST_CDR_FLAG_POSTED))
00667 ast_set_flag(to, AST_CDR_FLAG_POSTED);
00668 if (ast_test_flag(from, AST_CDR_FLAG_LOCKED))
00669 ast_set_flag(to, AST_CDR_FLAG_LOCKED);
00670 if (ast_test_flag(from, AST_CDR_FLAG_CHILD))
00671 ast_set_flag(to, AST_CDR_FLAG_CHILD);
00672 if (ast_test_flag(from, AST_CDR_FLAG_POST_DISABLED))
00673 ast_set_flag(to, AST_CDR_FLAG_POST_DISABLED);
00674
00675
00676 while (from->next) {
00677
00678 zcdr = from->next;
00679 from->next = zcdr->next;
00680 zcdr->next = NULL;
00681
00682 ast_cdr_append(to, zcdr);
00683 }
00684 if (discard_from)
00685 ast_cdr_discard(from);
00686 }
00687
00688 void ast_cdr_start(struct ast_cdr *cdr)
00689 {
00690 char *chan;
00691
00692 for (; cdr; cdr = cdr->next) {
00693 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00694 chan = S_OR(cdr->channel, "<unknown>");
00695 check_post(cdr);
00696 cdr->start = ast_tvnow();
00697 }
00698 }
00699 }
00700
00701 void ast_cdr_answer(struct ast_cdr *cdr)
00702 {
00703
00704 for (; cdr; cdr = cdr->next) {
00705 if (ast_test_flag(cdr, AST_CDR_FLAG_ANSLOCKED))
00706 continue;
00707 if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00708 continue;
00709 check_post(cdr);
00710 if (cdr->disposition < AST_CDR_ANSWERED)
00711 cdr->disposition = AST_CDR_ANSWERED;
00712 if (ast_tvzero(cdr->answer))
00713 cdr->answer = ast_tvnow();
00714 }
00715 }
00716
00717 void ast_cdr_busy(struct ast_cdr *cdr)
00718 {
00719
00720 for (; cdr; cdr = cdr->next) {
00721 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00722 check_post(cdr);
00723 if (cdr->disposition < AST_CDR_BUSY)
00724 cdr->disposition = AST_CDR_BUSY;
00725 }
00726 }
00727 }
00728
00729 void ast_cdr_failed(struct ast_cdr *cdr)
00730 {
00731 for (; cdr; cdr = cdr->next) {
00732 check_post(cdr);
00733 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00734 if (cdr->disposition < AST_CDR_FAILED)
00735 cdr->disposition = AST_CDR_FAILED;
00736 }
00737 }
00738 }
00739
00740 void ast_cdr_noanswer(struct ast_cdr *cdr)
00741 {
00742 char *chan;
00743
00744 while (cdr) {
00745 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
00746 if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
00747 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
00748 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00749 if (cdr->disposition < AST_CDR_NOANSWER)
00750 cdr->disposition = AST_CDR_NOANSWER;
00751 }
00752 cdr = cdr->next;
00753 }
00754 }
00755
00756
00757
00758
00759 int ast_cdr_disposition(struct ast_cdr *cdr, int cause)
00760 {
00761 int res = 0;
00762
00763 for (; cdr; cdr = cdr->next) {
00764 switch(cause) {
00765
00766 case AST_CAUSE_BUSY:
00767 ast_cdr_busy(cdr);
00768 break;
00769 case AST_CAUSE_NORMAL:
00770 break;
00771 default:
00772 res = -1;
00773 }
00774 }
00775 return res;
00776 }
00777
00778 void ast_cdr_setdestchan(struct ast_cdr *cdr, const char *chann)
00779 {
00780 for (; cdr; cdr = cdr->next) {
00781 check_post(cdr);
00782 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00783 ast_copy_string(cdr->dstchannel, chann, sizeof(cdr->dstchannel));
00784 }
00785 }
00786
00787 void ast_cdr_setapp(struct ast_cdr *cdr, char *app, char *data)
00788 {
00789
00790 for (; cdr; cdr = cdr->next) {
00791 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00792 check_post(cdr);
00793 ast_copy_string(cdr->lastapp, S_OR(app, ""), sizeof(cdr->lastapp));
00794 ast_copy_string(cdr->lastdata, S_OR(data, ""), sizeof(cdr->lastdata));
00795 }
00796 }
00797 }
00798
00799
00800 static void set_one_cid(struct ast_cdr *cdr, struct ast_channel *c)
00801 {
00802
00803 const char *num = S_OR(c->cid.cid_ani, c->cid.cid_num);
00804 if (!cdr)
00805 return;
00806 if (!ast_strlen_zero(c->cid.cid_name)) {
00807 if (!ast_strlen_zero(num))
00808 snprintf(cdr->clid, sizeof(cdr->clid), "\"%s\" <%s>", c->cid.cid_name, num);
00809 else
00810 ast_copy_string(cdr->clid, c->cid.cid_name, sizeof(cdr->clid));
00811 } else if (!ast_strlen_zero(num)) {
00812 ast_copy_string(cdr->clid, num, sizeof(cdr->clid));
00813 } else {
00814 cdr->clid[0] = '\0';
00815 }
00816 ast_copy_string(cdr->src, S_OR(num, ""), sizeof(cdr->src));
00817
00818 }
00819 int ast_cdr_setcid(struct ast_cdr *cdr, struct ast_channel *c)
00820 {
00821 for (; cdr; cdr = cdr->next) {
00822 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00823 set_one_cid(cdr, c);
00824 }
00825 return 0;
00826 }
00827
00828 int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c)
00829 {
00830 char *chan;
00831
00832 for ( ; cdr ; cdr = cdr->next) {
00833 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00834 chan = S_OR(cdr->channel, "<unknown>");
00835 ast_copy_string(cdr->channel, c->name, sizeof(cdr->channel));
00836 set_one_cid(cdr, c);
00837
00838 cdr->disposition = (c->_state == AST_STATE_UP) ? AST_CDR_ANSWERED : AST_CDR_NULL;
00839 cdr->amaflags = c->amaflags ? c->amaflags : ast_default_amaflags;
00840 ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode));
00841
00842 ast_copy_string(cdr->dst, S_OR(c->macroexten,c->exten), sizeof(cdr->dst));
00843 ast_copy_string(cdr->dcontext, S_OR(c->macrocontext,c->context), sizeof(cdr->dcontext));
00844
00845 ast_copy_string(cdr->uniqueid, c->uniqueid, sizeof(cdr->uniqueid));
00846 }
00847 }
00848 return 0;
00849 }
00850
00851
00852
00853
00854
00855
00856
00857
00858
00859
00860
00861
00862
00863 void ast_cdr_end(struct ast_cdr *cdr)
00864 {
00865 for ( ; cdr ; cdr = cdr->next) {
00866 if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00867 continue;
00868 check_post(cdr);
00869 if (ast_tvzero(cdr->end))
00870 cdr->end = ast_tvnow();
00871 if (ast_tvzero(cdr->start)) {
00872 ast_log(LOG_WARNING, "CDR on channel '%s' has not started\n", S_OR(cdr->channel, "<unknown>"));
00873 cdr->disposition = AST_CDR_FAILED;
00874 } else
00875 cdr->duration = cdr->end.tv_sec - cdr->start.tv_sec;
00876 if (ast_tvzero(cdr->answer)) {
00877 if (cdr->disposition == AST_CDR_ANSWERED) {
00878 ast_log(LOG_WARNING, "CDR on channel '%s' has no answer time but is 'ANSWERED'\n", S_OR(cdr->channel, "<unknown>"));
00879 cdr->disposition = AST_CDR_FAILED;
00880 }
00881 } else
00882 cdr->billsec = cdr->end.tv_sec - cdr->answer.tv_sec;
00883 }
00884 }
00885
00886 char *ast_cdr_disp2str(int disposition)
00887 {
00888 switch (disposition) {
00889 case AST_CDR_NULL:
00890 return "NO ANSWER";
00891 case AST_CDR_NOANSWER:
00892 return "NO ANSWER";
00893 case AST_CDR_FAILED:
00894 return "FAILED";
00895 case AST_CDR_BUSY:
00896 return "BUSY";
00897 case AST_CDR_ANSWERED:
00898 return "ANSWERED";
00899 }
00900 return "UNKNOWN";
00901 }
00902
00903
00904 char *ast_cdr_flags2str(int flag)
00905 {
00906 switch(flag) {
00907