Main Page | Alphabetical List | Data Structures | File List | Data Fields | Globals

cdr.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- A telephony toolkit for Linux.
00003  *
00004  * Call Detail Record API 
00005  * 
00006  * Copyright (C) 1999, Mark Spencer
00007  *
00008  * Mark Spencer <markster@linux-support.net>
00009  *
00010  * This program is free software, distributed under the terms of
00011  * the GNU General Public License.
00012  *
00013  * Includes code and algorithms from the Zapata library.
00014  *
00015  */
00016 
00017 #include <asterisk/lock.h>
00018 #include <asterisk/channel.h>
00019 #include <asterisk/cdr.h>
00020 #include <asterisk/logger.h>
00021 #include <asterisk/callerid.h>
00022 #include <asterisk/causes.h>
00023 #include <unistd.h>
00024 #include <stdlib.h>
00025 #include <string.h>
00026 #include <pthread.h>
00027 
00028 int ast_default_amaflags = AST_CDR_DOCUMENTATION;
00029 char ast_default_accountcode[20] = "";
00030 
00031 static ast_mutex_t cdrlock = AST_MUTEX_INITIALIZER;
00032 
00033 static struct ast_cdr_beitem {
00034    char name[20];
00035    char desc[80];
00036    ast_cdrbe be;
00037    struct ast_cdr_beitem *next;
00038 } *bes = NULL;
00039 
00040 /*
00041  * We do a lot of checking here in the CDR code to try to be sure we don't ever let a CDR slip
00042  * through our fingers somehow.  If someone allocates a CDR, it must be completely handled normally
00043  * or a WARNING shall be logged, so that we can best keep track of any escape condition where the CDR
00044  * isn't properly generated and posted.
00045  */
00046 
00047 int ast_cdr_register(char *name, char *desc, ast_cdrbe be)
00048 {
00049    struct ast_cdr_beitem *i;
00050    if (!name)
00051       return -1;
00052    if (!be) {
00053       ast_log(LOG_WARNING, "CDR engine '%s' lacks backend\n", name);
00054       return -1;
00055    }
00056    ast_mutex_lock(&cdrlock);
00057    i = bes;
00058    while(i) {
00059       if (!strcasecmp(name, i->name))
00060          break;
00061       i = i->next;
00062    }
00063    ast_mutex_unlock(&cdrlock);
00064    if (i) {
00065       ast_log(LOG_WARNING, "Already have a CDR backend called '%s'\n", name);
00066       return -1;
00067    }
00068    i = malloc(sizeof(struct ast_cdr_beitem));
00069    if (!i)  
00070       return -1;
00071    memset(i, 0, sizeof(struct ast_cdr_beitem));
00072    strncpy(i->name, name, sizeof(i->name) - 1);
00073    strncpy(i->desc, desc, sizeof(i->desc) - 1);
00074    i->be = be;
00075    ast_mutex_lock(&cdrlock);
00076    i->next = bes;
00077    bes = i;
00078    ast_mutex_unlock(&cdrlock);
00079    return 0;
00080 }
00081 
00082 void ast_cdr_unregister(char *name)
00083 {
00084    struct ast_cdr_beitem *i, *prev = NULL;
00085    ast_mutex_lock(&cdrlock);
00086    i = bes;
00087    while(i) {
00088       if (!strcasecmp(name, i->name)) {
00089          if (prev)
00090             prev->next = i->next;
00091          else
00092             bes = i->next;
00093          break;
00094       }
00095       i = i->next;
00096    }
00097    ast_mutex_unlock(&cdrlock);
00098    if (i) 
00099       free(i);
00100 }
00101 
00102 void ast_cdr_free(struct ast_cdr *cdr)
00103 {
00104    char *chan; 
00105    if (cdr) {
00106       chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
00107       if (!cdr->posted)
00108          ast_log(LOG_WARNING, "CDR on channel '%s' not posted\n", chan);
00109       if (!cdr->end.tv_sec && !cdr->end.tv_usec)
00110          ast_log(LOG_WARNING, "CDR on channel '%s' lacks end\n", chan);
00111       if (!cdr->start.tv_sec && !cdr->start.tv_usec)
00112          ast_log(LOG_WARNING, "CDR on channel '%s' lacks start\n", chan);
00113       free(cdr);
00114    }
00115 }
00116 
00117 struct ast_cdr *ast_cdr_alloc(void)
00118 {
00119    struct ast_cdr *cdr;
00120    cdr = malloc(sizeof(struct ast_cdr));
00121    if (cdr) {
00122       memset(cdr, 0, sizeof(struct ast_cdr));
00123    }
00124    return cdr;
00125 }
00126 
00127 void ast_cdr_start(struct ast_cdr *cdr)
00128 {
00129    char *chan; 
00130    if (cdr) {
00131       chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
00132       if (cdr->posted)
00133          ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
00134       if (cdr->start.tv_sec || cdr->start.tv_usec)
00135          ast_log(LOG_WARNING, "CDR on channel '%s' already started\n", chan);
00136       gettimeofday(&cdr->start, NULL);
00137    }
00138 }
00139 
00140 void ast_cdr_answer(struct ast_cdr *cdr)
00141 {
00142    char *chan; 
00143    if (cdr) {
00144       chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
00145       if (cdr->posted)
00146          ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
00147       if (cdr->disposition < AST_CDR_ANSWERED)
00148          cdr->disposition = AST_CDR_ANSWERED;
00149       if (!cdr->answer.tv_sec && !cdr->answer.tv_usec) {
00150          gettimeofday(&cdr->answer, NULL);
00151       }
00152    }
00153 }
00154 
00155 void ast_cdr_busy(struct ast_cdr *cdr)
00156 {
00157    char *chan; 
00158    if (cdr) {
00159       chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
00160       if (cdr->posted)
00161          ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
00162       if (cdr->disposition < AST_CDR_BUSY)
00163          cdr->disposition = AST_CDR_BUSY;
00164    }
00165 }
00166 
00167 void ast_cdr_failed(struct ast_cdr *cdr)
00168 {
00169    char *chan; 
00170    if (cdr) {
00171       chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
00172       if (cdr->posted)
00173          ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
00174          cdr->disposition = AST_CDR_FAILED;
00175    }
00176 }
00177 
00178 int ast_cdr_disposition(struct ast_cdr *cdr, int cause)
00179 {
00180    int res = 0;
00181    if (cdr) {
00182       switch(cause) {
00183          case AST_CAUSE_BUSY:
00184             ast_cdr_busy(cdr);
00185             break;
00186          case AST_CAUSE_FAILURE:
00187             ast_cdr_failed(cdr);
00188             break;
00189          case AST_CAUSE_NORMAL:
00190             break;
00191          case AST_CAUSE_NOTDEFINED:
00192             res = -1;
00193             break;
00194          default:
00195             res = -1;
00196             ast_log(LOG_WARNING, "We don't handle that cause yet\n");
00197       }
00198    }
00199    return res;
00200 }
00201 
00202 void ast_cdr_setdestchan(struct ast_cdr *cdr, char *chann)
00203 {
00204    char *chan; 
00205    if (cdr) {
00206       chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
00207       if (cdr->posted)
00208          ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
00209       strncpy(cdr->dstchannel, chann, sizeof(cdr->dstchannel) - 1);
00210    }
00211 }
00212 
00213 void ast_cdr_setapp(struct ast_cdr *cdr, char *app, char *data)
00214 {
00215    char *chan; 
00216    if (cdr) {
00217       chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
00218       if (cdr->posted)
00219          ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
00220       if (!app)
00221          app = "";
00222       strncpy(cdr->lastapp, app, sizeof(cdr->lastapp) - 1);
00223       if (!data)
00224          data = "";
00225       strncpy(cdr->lastdata, data, sizeof(cdr->lastdata) - 1);
00226    }
00227 }
00228 
00229 int ast_cdr_setcid(struct ast_cdr *cdr, struct ast_channel *c)
00230 {
00231    char tmp[AST_MAX_EXTENSION] = "";
00232    char *num, *name;
00233    if (cdr) {
00234       /* Grab source from ANI or normal Caller*ID */
00235       if (c->ani)
00236          strncpy(tmp, c->ani, sizeof(tmp) - 1);
00237       else if (c->callerid)
00238          strncpy(tmp, c->callerid, sizeof(tmp) - 1);
00239       if (c->callerid)
00240          strncpy(cdr->clid, c->callerid, sizeof(cdr->clid) - 1);
00241       name = NULL;
00242       num = NULL;
00243       ast_callerid_parse(tmp, &name, &num);
00244       if (num) {
00245          ast_shrink_phone_number(num);
00246          strncpy(cdr->src, num, sizeof(cdr->src) - 1);
00247       }
00248    }
00249    return 0;
00250 }
00251 
00252 int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c)
00253 {
00254    char *chan;
00255    char *num, *name;
00256    char tmp[AST_MAX_EXTENSION] = "";
00257    if (cdr) {
00258       chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
00259       if (strlen(cdr->channel)) 
00260          ast_log(LOG_WARNING, "CDR already initialized on '%s'\n", chan); 
00261       strncpy(cdr->channel, c->name, sizeof(cdr->channel) - 1);
00262       /* Grab source from ANI or normal Caller*ID */
00263       if (c->ani)
00264          strncpy(tmp, c->ani, sizeof(tmp) - 1);
00265       else if (c->callerid)
00266          strncpy(tmp, c->callerid, sizeof(tmp) - 1);
00267       if (c->callerid)
00268          strncpy(cdr->clid, c->callerid, sizeof(cdr->clid) - 1);
00269       name = NULL;
00270       num = NULL;
00271       ast_callerid_parse(tmp, &name, &num);
00272       if (num) {
00273          ast_shrink_phone_number(num);
00274          strncpy(cdr->src, num, sizeof(cdr->src) - 1);
00275       }
00276       
00277       if (c->_state == AST_STATE_UP)
00278          cdr->disposition = AST_CDR_ANSWERED;
00279       else
00280          cdr->disposition = AST_CDR_NOANSWER;
00281       if (c->amaflags)
00282          cdr->amaflags = c->amaflags;
00283       else
00284          cdr->amaflags = ast_default_amaflags;
00285       strncpy(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode) - 1);
00286       /* Destination information */
00287       strncpy(cdr->dst, c->exten, sizeof(cdr->dst) - 1);
00288       strncpy(cdr->dcontext, c->context, sizeof(cdr->dcontext) - 1);
00289       /* Unique call identifier */
00290       strncpy(cdr->uniqueid, c->uniqueid, sizeof(cdr->uniqueid) - 1);
00291    }
00292    return 0;
00293 }
00294 
00295 void ast_cdr_end(struct ast_cdr *cdr)
00296 {
00297    char *chan;
00298    if (cdr) {
00299       chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
00300       if (cdr->posted)
00301          ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
00302       if (!cdr->start.tv_sec && !cdr->start.tv_usec)
00303          ast_log(LOG_WARNING, "CDR on channel '%s' has not started\n", chan);
00304       if (!cdr->end.tv_sec && !cdr->end.tv_usec) 
00305          gettimeofday(&cdr->end, NULL);
00306    }
00307 }
00308 
00309 char *ast_cdr_disp2str(int disposition)
00310 {
00311    switch (disposition) {
00312    case AST_CDR_NOANSWER:
00313       return "NO ANSWER";
00314    case AST_CDR_FAILED:
00315       return "FAILED";     
00316    case AST_CDR_BUSY:
00317       return "BUSY";    
00318    case AST_CDR_ANSWERED:
00319       return "ANSWERED";
00320    default:
00321       return "UNKNOWN";
00322    }
00323 }
00324 
00325 char *ast_cdr_flags2str(int flag)
00326 {
00327    switch(flag) {
00328    case AST_CDR_OMIT:
00329       return "OMIT";
00330    case AST_CDR_BILLING:
00331       return "BILLING";
00332    case AST_CDR_DOCUMENTATION:
00333       return "DOCUMENTATION";
00334    }
00335    return "Unknown";
00336 }
00337 
00338 int ast_cdr_setaccount(struct ast_channel *chan, char *account)
00339 {
00340    struct ast_cdr *cdr = chan->cdr;
00341 
00342    strncpy(chan->accountcode, account, sizeof(chan->accountcode) - 1);
00343    if (cdr)
00344       strncpy(cdr->accountcode, chan->accountcode, sizeof(cdr->accountcode) - 1);
00345    return 0;
00346 }
00347 
00348 int ast_cdr_setuserfield(struct ast_channel *chan, char *userfield)
00349 {
00350    struct ast_cdr *cdr = chan->cdr;
00351 
00352    if (cdr)
00353       strncpy(cdr->userfield, userfield, sizeof(cdr->userfield) - 1);
00354    return 0;
00355 }
00356 
00357 int ast_cdr_appenduserfield(struct ast_channel *chan, char *userfield)
00358 {
00359    struct ast_cdr *cdr = chan->cdr;
00360 
00361    if (cdr)
00362    {
00363       int len = strlen(cdr->userfield);
00364       strncpy(cdr->userfield+len, userfield, sizeof(cdr->userfield) - len - 1);
00365    }
00366    return 0;
00367 }
00368 
00369 int ast_cdr_update(struct ast_channel *c)
00370 {
00371    struct ast_cdr *cdr = c->cdr;
00372    char *name, *num;
00373    char tmp[AST_MAX_EXTENSION] = "";
00374    /* Grab source from ANI or normal Caller*ID */
00375    if (cdr) {
00376       if (c->ani)
00377          strncpy(tmp, c->ani, sizeof(tmp) - 1);
00378       else if (c->callerid && strlen(c->callerid))
00379          strncpy(tmp, c->callerid, sizeof(tmp) - 1);
00380       if (c->callerid && strlen(c->callerid))
00381          strncpy(cdr->clid, c->callerid, sizeof(cdr->clid) - 1);
00382       else
00383          strcpy(cdr->clid, "");
00384       name = NULL;
00385       num = NULL;
00386       ast_callerid_parse(tmp, &name, &num);
00387       if (num) {
00388          ast_shrink_phone_number(num);
00389          strncpy(cdr->src, num, sizeof(cdr->src) - 1);
00390       }
00391       /* Copy account code et-al */ 
00392       strncpy(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode) - 1);
00393       /* Destination information */
00394       strncpy(cdr->dst, c->exten, sizeof(cdr->dst) - 1);
00395       strncpy(cdr->dcontext, c->context, sizeof(cdr->dcontext) - 1);
00396    }
00397    return 0;
00398 }
00399 
00400 int ast_cdr_amaflags2int(char *flag)
00401 {
00402    if (!strcasecmp(flag, "default"))
00403       return 0;
00404    if (!strcasecmp(flag, "omit"))
00405       return AST_CDR_OMIT;
00406    if (!strcasecmp(flag, "billing"))
00407       return AST_CDR_BILLING;
00408    if (!strcasecmp(flag, "documentation"))
00409       return AST_CDR_DOCUMENTATION;
00410    return -1;
00411 }
00412 
00413 void ast_cdr_post(struct ast_cdr *cdr)
00414 {
00415    char *chan;
00416    struct ast_cdr_beitem *i;
00417    if (cdr) {
00418       chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
00419       if (cdr->posted)
00420          ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
00421       if (!cdr->end.tv_sec && !cdr->end.tv_usec)
00422          ast_log(LOG_WARNING, "CDR on channel '%s' lacks end\n", chan);
00423       if (!cdr->start.tv_sec && !cdr->start.tv_usec)
00424          ast_log(LOG_WARNING, "CDR on channel '%s' lacks start\n", chan);
00425       cdr->duration = cdr->end.tv_sec - cdr->start.tv_sec + (cdr->end.tv_usec - cdr->start.tv_usec) / 1000000;
00426       if (cdr->answer.tv_sec || cdr->answer.tv_usec) {
00427          cdr->billsec = cdr->end.tv_sec - cdr->answer.tv_sec + (cdr->end.tv_usec - cdr->answer.tv_usec) / 1000000;
00428       } else
00429          cdr->billsec = 0;
00430       cdr->posted = 1;
00431       ast_mutex_lock(&cdrlock);
00432       i = bes;
00433       while(i) {
00434          i->be(cdr);
00435          i = i->next;
00436       }
00437       ast_mutex_unlock(&cdrlock);
00438    }
00439 }
00440 
00441 void ast_cdr_reset(struct ast_cdr *cdr, int post)
00442 {
00443    if (cdr) {
00444       /* Post if requested */
00445       if (post) {
00446          ast_cdr_end(cdr);
00447          ast_cdr_post(cdr);
00448       }
00449       /* Reset to initial state */
00450       cdr->posted = 0;
00451       memset(&cdr->start, 0, sizeof(cdr->start));
00452       memset(&cdr->end, 0, sizeof(cdr->end));
00453       memset(&cdr->answer, 0, sizeof(cdr->answer));
00454       cdr->billsec = 0;
00455       cdr->duration = 0;
00456       ast_cdr_start(cdr);
00457       cdr->disposition = AST_CDR_NOANSWER;
00458    }
00459 }

Generated on Sun Apr 18 23:33:48 2004 for Asterisk by doxygen 1.3.6-20040222