2 * Asterisk -- A telephony toolkit for Linux.
4 * Call Detail Record API
6 * Copyright (C) 1999, Mark Spencer
8 * Mark Spencer <markster@linux-support.net>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License.
13 * Includes code and algorithms from the Zapata library.
17 #include <asterisk/lock.h>
18 #include <asterisk/channel.h>
19 #include <asterisk/cdr.h>
20 #include <asterisk/logger.h>
21 #include <asterisk/callerid.h>
22 #include <asterisk/causes.h>
23 #include <asterisk/options.h>
29 int ast_default_amaflags = AST_CDR_DOCUMENTATION;
30 char ast_default_accountcode[20] = "";
32 static ast_mutex_t cdrlock = AST_MUTEX_INITIALIZER;
34 static struct ast_cdr_beitem {
38 struct ast_cdr_beitem *next;
42 * We do a lot of checking here in the CDR code to try to be sure we don't ever let a CDR slip
43 * through our fingers somehow. If someone allocates a CDR, it must be completely handled normally
44 * or a WARNING shall be logged, so that we can best keep track of any escape condition where the CDR
45 * isn't properly generated and posted.
48 int ast_cdr_register(char *name, char *desc, ast_cdrbe be)
50 struct ast_cdr_beitem *i;
54 ast_log(LOG_WARNING, "CDR engine '%s' lacks backend\n", name);
57 ast_mutex_lock(&cdrlock);
60 if (!strcasecmp(name, i->name))
64 ast_mutex_unlock(&cdrlock);
66 ast_log(LOG_WARNING, "Already have a CDR backend called '%s'\n", name);
69 i = malloc(sizeof(struct ast_cdr_beitem));
72 memset(i, 0, sizeof(struct ast_cdr_beitem));
73 strncpy(i->name, name, sizeof(i->name) - 1);
74 strncpy(i->desc, desc, sizeof(i->desc) - 1);
76 ast_mutex_lock(&cdrlock);
79 ast_mutex_unlock(&cdrlock);
83 void ast_cdr_unregister(char *name)
85 struct ast_cdr_beitem *i, *prev = NULL;
86 ast_mutex_lock(&cdrlock);
89 if (!strcasecmp(name, i->name)) {
98 if (option_verbose > 1)
99 ast_verbose(VERBOSE_PREFIX_2 "Unregistered '%s' CDR backend\n", name);
100 ast_mutex_unlock(&cdrlock);
105 void ast_cdr_free(struct ast_cdr *cdr)
109 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
111 ast_log(LOG_WARNING, "CDR on channel '%s' not posted\n", chan);
112 if (!cdr->end.tv_sec && !cdr->end.tv_usec)
113 ast_log(LOG_WARNING, "CDR on channel '%s' lacks end\n", chan);
114 if (!cdr->start.tv_sec && !cdr->start.tv_usec)
115 ast_log(LOG_WARNING, "CDR on channel '%s' lacks start\n", chan);
120 struct ast_cdr *ast_cdr_alloc(void)
123 cdr = malloc(sizeof(struct ast_cdr));
125 memset(cdr, 0, sizeof(struct ast_cdr));
130 void ast_cdr_start(struct ast_cdr *cdr)
134 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
136 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
137 if (cdr->start.tv_sec || cdr->start.tv_usec)
138 ast_log(LOG_WARNING, "CDR on channel '%s' already started\n", chan);
139 gettimeofday(&cdr->start, NULL);
143 void ast_cdr_answer(struct ast_cdr *cdr)
147 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
149 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
150 if (cdr->disposition < AST_CDR_ANSWERED)
151 cdr->disposition = AST_CDR_ANSWERED;
152 if (!cdr->answer.tv_sec && !cdr->answer.tv_usec) {
153 gettimeofday(&cdr->answer, NULL);
158 void ast_cdr_busy(struct ast_cdr *cdr)
162 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
164 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
165 if (cdr->disposition < AST_CDR_BUSY)
166 cdr->disposition = AST_CDR_BUSY;
170 void ast_cdr_failed(struct ast_cdr *cdr)
174 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
176 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
177 cdr->disposition = AST_CDR_FAILED;
181 int ast_cdr_disposition(struct ast_cdr *cdr, int cause)
189 case AST_CAUSE_FAILURE:
192 case AST_CAUSE_NORMAL:
194 case AST_CAUSE_NOTDEFINED:
199 ast_log(LOG_WARNING, "We don't handle that cause yet\n");
205 void ast_cdr_setdestchan(struct ast_cdr *cdr, char *chann)
209 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
211 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
212 strncpy(cdr->dstchannel, chann, sizeof(cdr->dstchannel) - 1);
216 void ast_cdr_setapp(struct ast_cdr *cdr, char *app, char *data)
220 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
222 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
225 strncpy(cdr->lastapp, app, sizeof(cdr->lastapp) - 1);
228 strncpy(cdr->lastdata, data, sizeof(cdr->lastdata) - 1);
232 int ast_cdr_setcid(struct ast_cdr *cdr, struct ast_channel *c)
234 char tmp[AST_MAX_EXTENSION] = "";
237 /* Grab source from ANI or normal Caller*ID */
239 strncpy(tmp, c->ani, sizeof(tmp) - 1);
240 else if (c->callerid)
241 strncpy(tmp, c->callerid, sizeof(tmp) - 1);
243 strncpy(cdr->clid, c->callerid, sizeof(cdr->clid) - 1);
246 ast_callerid_parse(tmp, &name, &num);
248 ast_shrink_phone_number(num);
249 strncpy(cdr->src, num, sizeof(cdr->src) - 1);
255 int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c)
259 char tmp[AST_MAX_EXTENSION] = "";
261 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
262 if (strlen(cdr->channel))
263 ast_log(LOG_WARNING, "CDR already initialized on '%s'\n", chan);
264 strncpy(cdr->channel, c->name, sizeof(cdr->channel) - 1);
265 /* Grab source from ANI or normal Caller*ID */
267 strncpy(tmp, c->ani, sizeof(tmp) - 1);
268 else if (c->callerid)
269 strncpy(tmp, c->callerid, sizeof(tmp) - 1);
271 strncpy(cdr->clid, c->callerid, sizeof(cdr->clid) - 1);
274 ast_callerid_parse(tmp, &name, &num);
276 ast_shrink_phone_number(num);
277 strncpy(cdr->src, num, sizeof(cdr->src) - 1);
280 if (c->_state == AST_STATE_UP)
281 cdr->disposition = AST_CDR_ANSWERED;
283 cdr->disposition = AST_CDR_NOANSWER;
285 cdr->amaflags = c->amaflags;
287 cdr->amaflags = ast_default_amaflags;
288 strncpy(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode) - 1);
289 /* Destination information */
290 strncpy(cdr->dst, c->exten, sizeof(cdr->dst) - 1);
291 strncpy(cdr->dcontext, c->context, sizeof(cdr->dcontext) - 1);
292 /* Unique call identifier */
293 strncpy(cdr->uniqueid, c->uniqueid, sizeof(cdr->uniqueid) - 1);
298 void ast_cdr_end(struct ast_cdr *cdr)
302 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
304 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
305 if (!cdr->start.tv_sec && !cdr->start.tv_usec)
306 ast_log(LOG_WARNING, "CDR on channel '%s' has not started\n", chan);
307 if (!cdr->end.tv_sec && !cdr->end.tv_usec)
308 gettimeofday(&cdr->end, NULL);
312 char *ast_cdr_disp2str(int disposition)
314 switch (disposition) {
315 case AST_CDR_NOANSWER:
321 case AST_CDR_ANSWERED:
328 char *ast_cdr_flags2str(int flag)
333 case AST_CDR_BILLING:
335 case AST_CDR_DOCUMENTATION:
336 return "DOCUMENTATION";
341 int ast_cdr_setaccount(struct ast_channel *chan, char *account)
343 struct ast_cdr *cdr = chan->cdr;
345 strncpy(chan->accountcode, account, sizeof(chan->accountcode) - 1);
347 strncpy(cdr->accountcode, chan->accountcode, sizeof(cdr->accountcode) - 1);
351 int ast_cdr_setuserfield(struct ast_channel *chan, char *userfield)
353 struct ast_cdr *cdr = chan->cdr;
356 strncpy(cdr->userfield, userfield, sizeof(cdr->userfield) - 1);
360 int ast_cdr_appenduserfield(struct ast_channel *chan, char *userfield)
362 struct ast_cdr *cdr = chan->cdr;
366 int len = strlen(cdr->userfield);
367 strncpy(cdr->userfield+len, userfield, sizeof(cdr->userfield) - len - 1);
372 int ast_cdr_update(struct ast_channel *c)
374 struct ast_cdr *cdr = c->cdr;
376 char tmp[AST_MAX_EXTENSION] = "";
377 /* Grab source from ANI or normal Caller*ID */
380 strncpy(tmp, c->ani, sizeof(tmp) - 1);
381 else if (c->callerid && strlen(c->callerid))
382 strncpy(tmp, c->callerid, sizeof(tmp) - 1);
383 if (c->callerid && strlen(c->callerid))
384 strncpy(cdr->clid, c->callerid, sizeof(cdr->clid) - 1);
386 strcpy(cdr->clid, "");
389 ast_callerid_parse(tmp, &name, &num);
391 ast_shrink_phone_number(num);
392 strncpy(cdr->src, num, sizeof(cdr->src) - 1);
394 /* Copy account code et-al */
395 strncpy(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode) - 1);
396 /* Destination information */
397 strncpy(cdr->dst, c->exten, sizeof(cdr->dst) - 1);
398 strncpy(cdr->dcontext, c->context, sizeof(cdr->dcontext) - 1);
403 int ast_cdr_amaflags2int(char *flag)
405 if (!strcasecmp(flag, "default"))
407 if (!strcasecmp(flag, "omit"))
409 if (!strcasecmp(flag, "billing"))
410 return AST_CDR_BILLING;
411 if (!strcasecmp(flag, "documentation"))
412 return AST_CDR_DOCUMENTATION;
416 void ast_cdr_post(struct ast_cdr *cdr)
419 struct ast_cdr_beitem *i;
421 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
423 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
424 if (!cdr->end.tv_sec && !cdr->end.tv_usec)
425 ast_log(LOG_WARNING, "CDR on channel '%s' lacks end\n", chan);
426 if (!cdr->start.tv_sec && !cdr->start.tv_usec)
427 ast_log(LOG_WARNING, "CDR on channel '%s' lacks start\n", chan);
428 cdr->duration = cdr->end.tv_sec - cdr->start.tv_sec + (cdr->end.tv_usec - cdr->start.tv_usec) / 1000000;
429 if (cdr->answer.tv_sec || cdr->answer.tv_usec) {
430 cdr->billsec = cdr->end.tv_sec - cdr->answer.tv_sec + (cdr->end.tv_usec - cdr->answer.tv_usec) / 1000000;
434 ast_mutex_lock(&cdrlock);
440 ast_mutex_unlock(&cdrlock);
444 void ast_cdr_reset(struct ast_cdr *cdr, int post)
447 /* Post if requested */
452 /* Reset to initial state */
454 memset(&cdr->start, 0, sizeof(cdr->start));
455 memset(&cdr->end, 0, sizeof(cdr->end));
456 memset(&cdr->answer, 0, sizeof(cdr->answer));
460 cdr->disposition = AST_CDR_NOANSWER;