123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540 |
- /*
- * Asterisk -- A telephony toolkit for Linux.
- *
- * Call Detail Record API
- *
- * Copyright (C) 1999-2004, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License.
- *
- * Includes code and algorithms from the Zapata library.
- *
- */
- #include <asterisk/lock.h>
- #include <asterisk/channel.h>
- #include <asterisk/cdr.h>
- #include <asterisk/logger.h>
- #include <asterisk/callerid.h>
- #include <asterisk/causes.h>
- #include <asterisk/options.h>
- #include <asterisk/utils.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <string.h>
- int ast_default_amaflags = AST_CDR_DOCUMENTATION;
- char ast_default_accountcode[20] = "";
- AST_MUTEX_DEFINE_STATIC(cdrlock);
- static struct ast_cdr_beitem {
- char name[20];
- char desc[80];
- ast_cdrbe be;
- struct ast_cdr_beitem *next;
- } *bes = NULL;
- /*
- * We do a lot of checking here in the CDR code to try to be sure we don't ever let a CDR slip
- * through our fingers somehow. If someone allocates a CDR, it must be completely handled normally
- * or a WARNING shall be logged, so that we can best keep track of any escape condition where the CDR
- * isn't properly generated and posted.
- */
- int ast_cdr_register(char *name, char *desc, ast_cdrbe be)
- {
- struct ast_cdr_beitem *i;
- if (!name)
- return -1;
- if (!be) {
- ast_log(LOG_WARNING, "CDR engine '%s' lacks backend\n", name);
- return -1;
- }
- ast_mutex_lock(&cdrlock);
- i = bes;
- while(i) {
- if (!strcasecmp(name, i->name))
- break;
- i = i->next;
- }
- ast_mutex_unlock(&cdrlock);
- if (i) {
- ast_log(LOG_WARNING, "Already have a CDR backend called '%s'\n", name);
- return -1;
- }
- i = malloc(sizeof(struct ast_cdr_beitem));
- if (!i)
- return -1;
- memset(i, 0, sizeof(struct ast_cdr_beitem));
- strncpy(i->name, name, sizeof(i->name) - 1);
- strncpy(i->desc, desc, sizeof(i->desc) - 1);
- i->be = be;
- ast_mutex_lock(&cdrlock);
- i->next = bes;
- bes = i;
- ast_mutex_unlock(&cdrlock);
- return 0;
- }
- void ast_cdr_unregister(char *name)
- {
- struct ast_cdr_beitem *i, *prev = NULL;
- ast_mutex_lock(&cdrlock);
- i = bes;
- while(i) {
- if (!strcasecmp(name, i->name)) {
- if (prev)
- prev->next = i->next;
- else
- bes = i->next;
- break;
- }
- i = i->next;
- }
- if (option_verbose > 1)
- ast_verbose(VERBOSE_PREFIX_2 "Unregistered '%s' CDR backend\n", name);
- ast_mutex_unlock(&cdrlock);
- if (i)
- free(i);
- }
- 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>";
- if (!ast_cdr_has_flag(cdr,AST_CDR_FLAG_POSTED))
- ast_log(LOG_WARNING, "CDR on channel '%s' not posted\n", chan);
- if (!cdr->end.tv_sec && !cdr->end.tv_usec)
- 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);
- free(cdr);
- cdr = next;
- }
- }
- struct ast_cdr *ast_cdr_alloc(void)
- {
- struct ast_cdr *cdr;
- cdr = malloc(sizeof(struct ast_cdr));
- if (cdr) {
- memset(cdr, 0, sizeof(struct ast_cdr));
- }
- return cdr;
- }
- void ast_cdr_start(struct ast_cdr *cdr)
- {
- char *chan;
- while (cdr) {
- if (!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED)) {
- chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
- if (ast_cdr_has_flag(cdr,AST_CDR_FLAG_POSTED))
- ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
- if (cdr->start.tv_sec || cdr->start.tv_usec)
- ast_log(LOG_WARNING, "CDR on channel '%s' already started\n", chan);
- gettimeofday(&cdr->start, NULL);
- }
- cdr = cdr->next;
- }
- }
- void ast_cdr_answer(struct ast_cdr *cdr)
- {
- char *chan;
- while (cdr) {
- chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
- if (ast_cdr_has_flag(cdr,AST_CDR_FLAG_POSTED))
- ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
- if (cdr->disposition < AST_CDR_ANSWERED)
- cdr->disposition = AST_CDR_ANSWERED;
- if (!cdr->answer.tv_sec && !cdr->answer.tv_usec) {
- gettimeofday(&cdr->answer, NULL);
- }
- cdr = cdr->next;
- }
- }
- void ast_cdr_busy(struct ast_cdr *cdr)
- {
- char *chan;
- while (cdr) {
- if (!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED)) {
- chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
- if (ast_cdr_has_flag(cdr,AST_CDR_FLAG_POSTED))
- ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
- if (cdr->disposition < AST_CDR_BUSY)
- cdr->disposition = AST_CDR_BUSY;
- }
- cdr = cdr->next;
- }
- }
- void ast_cdr_failed(struct ast_cdr *cdr)
- {
- char *chan;
- while (cdr) {
- chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
- if (ast_cdr_has_flag(cdr,AST_CDR_FLAG_POSTED))
- ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
- if(!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED))
- cdr->disposition = AST_CDR_FAILED;
- cdr = cdr->next;
- }
- }
- int ast_cdr_disposition(struct ast_cdr *cdr, int cause)
- {
- int res = 0;
- while (cdr) {
- switch(cause) {
- case AST_CAUSE_BUSY:
- ast_cdr_busy(cdr);
- break;
- case AST_CAUSE_FAILURE:
- ast_cdr_failed(cdr);
- break;
- case AST_CAUSE_NORMAL:
- break;
- case AST_CAUSE_NOTDEFINED:
- res = -1;
- break;
- default:
- res = -1;
- ast_log(LOG_WARNING, "We don't handle that cause yet\n");
- }
- cdr = cdr->next;
- }
- return res;
- }
- void ast_cdr_setdestchan(struct ast_cdr *cdr, char *chann)
- {
- char *chan;
- while (cdr) {
- chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
- if (ast_cdr_has_flag(cdr,AST_CDR_FLAG_POSTED))
- ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
- if(!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED))
- strncpy(cdr->dstchannel, chann, sizeof(cdr->dstchannel) - 1);
- cdr = cdr->next;
- }
- }
- void ast_cdr_setapp(struct ast_cdr *cdr, char *app, char *data)
- {
- char *chan;
- while (cdr) {
- if(!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED)) {
- chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
- if (ast_cdr_has_flag(cdr,AST_CDR_FLAG_POSTED))
- ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
- if (!app)
- app = "";
- strncpy(cdr->lastapp, app, sizeof(cdr->lastapp) - 1);
- if (!data)
- data = "";
- strncpy(cdr->lastdata, data, sizeof(cdr->lastdata) - 1);
- }
- cdr = cdr->next;
- }
- }
- int ast_cdr_setcid(struct ast_cdr *cdr, struct ast_channel *c)
- {
- char tmp[AST_MAX_EXTENSION] = "";
- char *num, *name;
- while (cdr) {
- if(!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED)) {
- /* Grab source from ANI or normal Caller*ID */
- if (c->ani)
- strncpy(tmp, c->ani, sizeof(tmp) - 1);
- else if (c->callerid)
- strncpy(tmp, c->callerid, sizeof(tmp) - 1);
- if (c->callerid)
- strncpy(cdr->clid, c->callerid, sizeof(cdr->clid) - 1);
- name = NULL;
- num = NULL;
- ast_callerid_parse(tmp, &name, &num);
- if (num) {
- ast_shrink_phone_number(num);
- strncpy(cdr->src, num, sizeof(cdr->src) - 1);
- }
- }
- cdr = cdr->next;
- }
- return 0;
- }
- int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c)
- {
- char *chan;
- char *num, *name;
- char tmp[AST_MAX_EXTENSION] = "";
- while (cdr) {
- if(!ast_cdr_has_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);
- strncpy(cdr->channel, c->name, sizeof(cdr->channel) - 1);
- /* Grab source from ANI or normal Caller*ID */
- if (c->ani)
- strncpy(tmp, c->ani, sizeof(tmp) - 1);
- else if (c->callerid)
- strncpy(tmp, c->callerid, sizeof(tmp) - 1);
- if (c->callerid)
- strncpy(cdr->clid, c->callerid, sizeof(cdr->clid) - 1);
- name = NULL;
- num = NULL;
- ast_callerid_parse(tmp, &name, &num);
- if (num) {
- ast_shrink_phone_number(num);
- strncpy(cdr->src, num, sizeof(cdr->src) - 1);
- }
-
- if (c->_state == AST_STATE_UP)
- cdr->disposition = AST_CDR_ANSWERED;
- else
- cdr->disposition = AST_CDR_NOANSWER;
- if (c->amaflags)
- cdr->amaflags = c->amaflags;
- else
- cdr->amaflags = ast_default_amaflags;
- strncpy(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode) - 1);
- /* Destination information */
- strncpy(cdr->dst, c->exten, sizeof(cdr->dst) - 1);
- strncpy(cdr->dcontext, c->context, sizeof(cdr->dcontext) - 1);
- /* Unique call identifier */
- strncpy(cdr->uniqueid, c->uniqueid, sizeof(cdr->uniqueid) - 1);
- }
- cdr = cdr->next;
- }
- return 0;
- }
- void ast_cdr_end(struct ast_cdr *cdr)
- {
- char *chan;
- while (cdr) {
- chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
- if (ast_cdr_has_flag(cdr,AST_CDR_FLAG_POSTED))
- ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
- if (!cdr->start.tv_sec && !cdr->start.tv_usec)
- ast_log(LOG_WARNING, "CDR on channel '%s' has not started\n", chan);
- if (!cdr->end.tv_sec && !cdr->end.tv_usec)
- gettimeofday(&cdr->end, NULL);
- cdr = cdr->next;
- }
- }
- char *ast_cdr_disp2str(int disposition)
- {
- switch (disposition) {
- case AST_CDR_NOANSWER:
- return "NO ANSWER";
- case AST_CDR_FAILED:
- return "FAILED";
- case AST_CDR_BUSY:
- return "BUSY";
- case AST_CDR_ANSWERED:
- return "ANSWERED";
- default:
- return "UNKNOWN";
- }
- }
- char *ast_cdr_flags2str(int flag)
- {
- switch(flag) {
- case AST_CDR_OMIT:
- return "OMIT";
- case AST_CDR_BILLING:
- return "BILLING";
- case AST_CDR_DOCUMENTATION:
- return "DOCUMENTATION";
- }
- return "Unknown";
- }
- int ast_cdr_setaccount(struct ast_channel *chan, char *account)
- {
- struct ast_cdr *cdr = chan->cdr;
- strncpy(chan->accountcode, account, sizeof(chan->accountcode) - 1);
- while (cdr) {
- if(!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED))
- strncpy(cdr->accountcode, chan->accountcode, sizeof(cdr->accountcode) - 1);
- cdr = cdr->next;
- }
- return 0;
- }
- int ast_cdr_setamaflags(struct ast_channel *chan, char *flag)
- {
- struct ast_cdr *cdr = chan->cdr;
- int newflag;
- newflag = ast_cdr_amaflags2int(flag);
- if (newflag) {
- cdr->amaflags = newflag;
- }
- return 0;
- }
- int ast_cdr_setuserfield(struct ast_channel *chan, char *userfield)
- {
- struct ast_cdr *cdr = chan->cdr;
- while (cdr) {
- if(!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED))
- strncpy(cdr->userfield, userfield, sizeof(cdr->userfield) - 1);
- cdr = cdr->next;
- }
- return 0;
- }
- int ast_cdr_appenduserfield(struct ast_channel *chan, char *userfield)
- {
- struct ast_cdr *cdr = chan->cdr;
- while (cdr)
- {
- int len = strlen(cdr->userfield);
- if(!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED))
- strncpy(cdr->userfield+len, userfield, sizeof(cdr->userfield) - len - 1);
- cdr = cdr->next;
- }
- return 0;
- }
- int ast_cdr_update(struct ast_channel *c)
- {
- struct ast_cdr *cdr = c->cdr;
- char *name, *num;
- char tmp[AST_MAX_EXTENSION] = "";
- /* Grab source from ANI or normal Caller*ID */
- while (cdr) {
- if(!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED)) {
- if (c->ani)
- strncpy(tmp, c->ani, sizeof(tmp) - 1);
- else if (c->callerid && !ast_strlen_zero(c->callerid))
- strncpy(tmp, c->callerid, sizeof(tmp) - 1);
- if (c->callerid && !ast_strlen_zero(c->callerid))
- strncpy(cdr->clid, c->callerid, sizeof(cdr->clid) - 1);
- else
- cdr->clid[0] = '\0';
- name = NULL;
- num = NULL;
- ast_callerid_parse(tmp, &name, &num);
- if (num) {
- ast_shrink_phone_number(num);
- strncpy(cdr->src, num, sizeof(cdr->src) - 1);
- }
- /* Copy account code et-al */
- strncpy(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode) - 1);
- /* Destination information */
- if (ast_strlen_zero(c->macroexten))
- strncpy(cdr->dst, c->exten, sizeof(cdr->dst) - 1);
- else
- strncpy(cdr->dst, c->macroexten, sizeof(cdr->dst) - 1);
- if (ast_strlen_zero(c->macrocontext))
- strncpy(cdr->dcontext, c->context, sizeof(cdr->dcontext) - 1);
- else
- strncpy(cdr->dcontext, c->macrocontext, sizeof(cdr->dcontext) - 1);
- }
- cdr = cdr->next;
- }
- return 0;
- }
- int ast_cdr_amaflags2int(char *flag)
- {
- if (!strcasecmp(flag, "default"))
- return 0;
- if (!strcasecmp(flag, "omit"))
- return AST_CDR_OMIT;
- if (!strcasecmp(flag, "billing"))
- return AST_CDR_BILLING;
- if (!strcasecmp(flag, "documentation"))
- return AST_CDR_DOCUMENTATION;
- return -1;
- }
- void ast_cdr_post(struct ast_cdr *cdr)
- {
- char *chan;
- struct ast_cdr_beitem *i;
- while (cdr) {
- chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
- if (ast_cdr_has_flag(cdr,AST_CDR_FLAG_POSTED))
- ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
- if (!cdr->end.tv_sec && !cdr->end.tv_usec)
- 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);
- cdr->duration = cdr->end.tv_sec - cdr->start.tv_sec + (cdr->end.tv_usec - cdr->start.tv_usec) / 1000000;
- if (cdr->answer.tv_sec || cdr->answer.tv_usec) {
- cdr->billsec = cdr->end.tv_sec - cdr->answer.tv_sec + (cdr->end.tv_usec - cdr->answer.tv_usec) / 1000000;
- } else
- cdr->billsec = 0;
- ast_cdr_add_flag(cdr,AST_CDR_FLAG_POSTED);
- ast_mutex_lock(&cdrlock);
- i = bes;
- while(i) {
- i->be(cdr);
- i = i->next;
- }
- ast_mutex_unlock(&cdrlock);
- cdr = cdr->next;
- }
- }
- void ast_cdr_reset(struct ast_cdr *cdr, int flags)
- {
- while (cdr) {
- /* Post if requested */
- if (ast_cdr_compare_flag(flags,AST_CDR_FLAG_LOCKED) || !ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED)) {
- if (ast_cdr_compare_flag(flags,AST_CDR_FLAG_POSTED)) {
- ast_cdr_end(cdr);
- ast_cdr_post(cdr);
- }
- /* Reset to initial state */
- cdr->flags=0;
- memset(&cdr->start, 0, sizeof(cdr->start));
- memset(&cdr->end, 0, sizeof(cdr->end));
- memset(&cdr->answer, 0, sizeof(cdr->answer));
- cdr->billsec = 0;
- cdr->duration = 0;
- ast_cdr_start(cdr);
- cdr->disposition = AST_CDR_NOANSWER;
- }
-
- cdr = cdr->next;
- }
-
- }
- struct ast_cdr *ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr)
- {
- struct ast_cdr *ret;
- if (cdr) {
- ret = cdr;
- while(cdr->next)
- cdr = cdr->next;
- cdr->next = newcdr;
- } else {
- ret = newcdr;
- }
- return ret;
- }
|