Main Page | Alphabetical List | Data Structures | Directories | 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-2004, Digium, Inc.
00007  *
00008  * Mark Spencer <markster@digium.com>
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 <asterisk/options.h>
00024 #include <asterisk/utils.h>
00025 #include <unistd.h>
00026 #include <stdlib.h>
00027 #include <string.h>
00028 
00029 int ast_default_amaflags = AST_CDR_DOCUMENTATION;
00030 char ast_default_accountcode[20] = "";
00031 
00032 AST_MUTEX_DEFINE_STATIC(cdrlock);
00033 
00034 static struct ast_cdr_beitem {
00035    char name[20];
00036    char desc[80];
00037    ast_cdrbe be;
00038    struct ast_cdr_beitem *next;
00039 } *bes = NULL;
00040 
00041 
00042 /*
00043  * We do a lot of checking here in the CDR code to try to be sure we don't ever let a CDR slip
00044  * through our fingers somehow.  If someone allocates a CDR, it must be completely handled normally
00045  * or a WARNING shall be logged, so that we can best keep track of any escape condition where the CDR
00046  * isn't properly generated and posted.
00047  */
00048 
00049 int ast_cdr_register(char *name, char *desc, ast_cdrbe be)
00050 {
00051    struct ast_cdr_beitem *i;
00052    if (!name)
00053       return -1;
00054    if (!be) {
00055       ast_log(LOG_WARNING, "CDR engine '%s' lacks backend\n", name);
00056       return -1;
00057    }
00058    ast_mutex_lock(&cdrlock);
00059    i = bes;
00060    while(i) {
00061       if (!strcasecmp(name, i->name))
00062          break;
00063       i = i->next;
00064    }
00065    ast_mutex_unlock(&cdrlock);
00066    if (i) {
00067       ast_log(LOG_WARNING, "Already have a CDR backend called '%s'\n", name);
00068       return -1;
00069    }
00070    i = malloc(sizeof(struct ast_cdr_beitem));
00071    if (!i)  
00072       return -1;
00073    memset(i, 0, sizeof(struct ast_cdr_beitem));
00074    strncpy(i->name, name, sizeof(i->name) - 1);
00075    strncpy(i->desc, desc, sizeof(i->desc) - 1);
00076    i->be = be;
00077    ast_mutex_lock(&cdrlock);
00078    i->next = bes;
00079    bes = i;
00080    ast_mutex_unlock(&cdrlock);
00081    return 0;
00082 }
00083 
00084 void ast_cdr_unregister(char *name)
00085 {
00086    struct ast_cdr_beitem *i, *prev = NULL;
00087    ast_mutex_lock(&cdrlock);
00088    i = bes;
00089    while(i) {
00090       if (!strcasecmp(name, i->name)) {
00091          if (prev)
00092             prev->next = i->next;
00093          else
00094             bes = i->next;
00095          break;
00096       }
00097       i = i->next;
00098    }
00099    if (option_verbose > 1)
00100       ast_verbose(VERBOSE_PREFIX_2 "Unregistered '%s' CDR backend\n", name);
00101    ast_mutex_unlock(&cdrlock);
00102    if (i) 
00103       free(i);
00104 }
00105 
00106 void ast_cdr_free(struct ast_cdr *cdr)
00107 {
00108    char *chan;
00109    struct ast_cdr *next; 
00110    while (cdr) {
00111       next = cdr->next;
00112       chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
00113       if (!ast_cdr_has_flag(cdr,AST_CDR_FLAG_POSTED))
00114          ast_log(LOG_WARNING, "CDR on channel '%s' not posted\n", chan);
00115       if (!cdr->end.tv_sec && !cdr->end.tv_usec)
00116          ast_log(LOG_WARNING, "CDR on channel '%s' lacks end\n", chan);
00117       if (!cdr->start.tv_sec && !cdr->start.tv_usec)
00118          ast_log(LOG_WARNING, "CDR on channel '%s' lacks start\n", chan);
00119       free(cdr);
00120       cdr = next;
00121    }
00122 }
00123 
00124 struct ast_cdr *ast_cdr_alloc(void)
00125 {
00126    struct ast_cdr *cdr;
00127    cdr = malloc(sizeof(struct ast_cdr));
00128    if (cdr) {
00129       memset(cdr, 0, sizeof(struct ast_cdr));
00130    }
00131    return cdr;
00132 }
00133 
00134 void ast_cdr_start(struct ast_cdr *cdr)
00135 {
00136    char *chan; 
00137    while (cdr) {
00138       if (!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED)) {
00139          chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
00140          if (ast_cdr_has_flag(cdr,AST_CDR_FLAG_POSTED))
00141             ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
00142          if (cdr->start.tv_sec || cdr->start.tv_usec)
00143             ast_log(LOG_WARNING, "CDR on channel '%s' already started\n", chan);
00144          gettimeofday(&cdr->start, NULL);
00145       }
00146          cdr = cdr->next;
00147    }
00148 }
00149 
00150 void ast_cdr_answer(struct ast_cdr *cdr)
00151 {
00152    char *chan; 
00153    while (cdr) {
00154       chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
00155       if (ast_cdr_has_flag(cdr,AST_CDR_FLAG_POSTED))
00156          ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
00157       if (cdr->disposition < AST_CDR_ANSWERED)
00158          cdr->disposition = AST_CDR_ANSWERED;
00159       if (!cdr->answer.tv_sec && !cdr->answer.tv_usec) {
00160          gettimeofday(&cdr->answer, NULL);
00161       }
00162       cdr = cdr->next;
00163    }
00164 }
00165 
00166 void ast_cdr_busy(struct ast_cdr *cdr)
00167 {
00168    char *chan; 
00169    while (cdr) {
00170       if (!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED)) {
00171          chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
00172          if (ast_cdr_has_flag(cdr,AST_CDR_FLAG_POSTED))
00173             ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
00174          if (cdr->disposition < AST_CDR_BUSY)
00175             cdr->disposition = AST_CDR_BUSY;
00176       }
00177       cdr = cdr->next;
00178    }
00179 }
00180 
00181 void ast_cdr_failed(struct ast_cdr *cdr)
00182 {
00183    char *chan; 
00184    while (cdr) {
00185       chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
00186       if (ast_cdr_has_flag(cdr,AST_CDR_FLAG_POSTED))
00187          ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
00188       if(!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED))
00189          cdr->disposition = AST_CDR_FAILED;
00190       cdr = cdr->next;
00191    }
00192 }
00193 
00194 int ast_cdr_disposition(struct ast_cdr *cdr, int cause)
00195 {
00196    int res = 0;
00197    while (cdr) {
00198       switch(cause) {
00199          case AST_CAUSE_BUSY:
00200             ast_cdr_busy(cdr);
00201             break;
00202          case AST_CAUSE_FAILURE:
00203             ast_cdr_failed(cdr);
00204             break;
00205          case AST_CAUSE_NORMAL:
00206             break;
00207          case AST_CAUSE_NOTDEFINED:
00208             res = -1;
00209             break;
00210          default:
00211             res = -1;
00212             ast_log(LOG_WARNING, "We don't handle that cause yet\n");
00213       }
00214       cdr = cdr->next;
00215    }
00216    return res;
00217 }
00218 
00219 void ast_cdr_setdestchan(struct ast_cdr *cdr, char *chann)
00220 {
00221    char *chan; 
00222    while (cdr) {
00223       chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
00224       if (ast_cdr_has_flag(cdr,AST_CDR_FLAG_POSTED))
00225          ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
00226       if(!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED))
00227          strncpy(cdr->dstchannel, chann, sizeof(cdr->dstchannel) - 1);
00228       cdr = cdr->next;
00229    }
00230 }
00231 
00232 void ast_cdr_setapp(struct ast_cdr *cdr, char *app, char *data)
00233 {
00234    char *chan; 
00235    while (cdr) {
00236       if(!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED)) {
00237          chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
00238          if (ast_cdr_has_flag(cdr,AST_CDR_FLAG_POSTED))
00239             ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
00240          if (!app)
00241             app = "";
00242          strncpy(cdr->lastapp, app, sizeof(cdr->lastapp) - 1);
00243          if (!data)
00244             data = "";
00245          strncpy(cdr->lastdata, data, sizeof(cdr->lastdata) - 1);
00246       }
00247       cdr = cdr->next;
00248    }
00249 }
00250 
00251 int ast_cdr_setcid(struct ast_cdr *cdr, struct ast_channel *c)
00252 {
00253    char tmp[AST_MAX_EXTENSION] = "";
00254    char *num, *name;
00255    while (cdr) {
00256       if(!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED)) {
00257          /* Grab source from ANI or normal Caller*ID */
00258          if (c->ani)
00259             strncpy(tmp, c->ani, sizeof(tmp) - 1);
00260          else if (c->callerid)
00261             strncpy(tmp, c->callerid, sizeof(tmp) - 1);
00262          if (c->callerid)
00263             strncpy(cdr->clid, c->callerid, sizeof(cdr->clid) - 1);
00264          name = NULL;
00265          num = NULL;
00266          ast_callerid_parse(tmp, &name, &num);
00267          if (num) {
00268             ast_shrink_phone_number(num);
00269             strncpy(cdr->src, num, sizeof(cdr->src) - 1);
00270          }
00271       }
00272       cdr = cdr->next;
00273    }
00274    return 0;
00275 }
00276 
00277 int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c)
00278 {
00279    char *chan;
00280    char *num, *name;
00281    char tmp[AST_MAX_EXTENSION] = "";
00282    while (cdr) {
00283       if(!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED)) {
00284          chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
00285          if (!ast_strlen_zero(cdr->channel)) 
00286             ast_log(LOG_WARNING, "CDR already initialized on '%s'\n", chan); 
00287          strncpy(cdr->channel, c->name, sizeof(cdr->channel) - 1);
00288          /* Grab source from ANI or normal Caller*ID */
00289          if (c->ani)
00290             strncpy(tmp, c->ani, sizeof(tmp) - 1);
00291          else if (c->callerid)
00292             strncpy(tmp, c->callerid, sizeof(tmp) - 1);
00293          if (c->callerid)
00294             strncpy(cdr->clid, c->callerid, sizeof(cdr->clid) - 1);
00295          name = NULL;
00296          num = NULL;
00297          ast_callerid_parse(tmp, &name, &num);
00298          if (num) {
00299             ast_shrink_phone_number(num);
00300             strncpy(cdr->src, num, sizeof(cdr->src) - 1);
00301          }
00302          
00303          if (c->_state == AST_STATE_UP)
00304             cdr->disposition = AST_CDR_ANSWERED;
00305          else
00306             cdr->disposition = AST_CDR_NOANSWER;
00307          if (c->amaflags)
00308             cdr->amaflags = c->amaflags;
00309          else
00310             cdr->amaflags = ast_default_amaflags;
00311          strncpy(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode) - 1);
00312          /* Destination information */
00313          strncpy(cdr->dst, c->exten, sizeof(cdr->dst) - 1);
00314          strncpy(cdr->dcontext, c->context, sizeof(cdr->dcontext) - 1);
00315          /* Unique call identifier */
00316          strncpy(cdr->uniqueid, c->uniqueid, sizeof(cdr->uniqueid) - 1);
00317       }
00318       cdr = cdr->next;
00319    }
00320    return 0;
00321 }
00322 
00323 void ast_cdr_end(struct ast_cdr *cdr)
00324 {
00325    char *chan;
00326    while (cdr) {
00327       chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
00328       if (ast_cdr_has_flag(cdr,AST_CDR_FLAG_POSTED))
00329          ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
00330       if (!cdr->start.tv_sec && !cdr->start.tv_usec)
00331          ast_log(LOG_WARNING, "CDR on channel '%s' has not started\n", chan);
00332       if (!cdr->end.tv_sec && !cdr->end.tv_usec) 
00333          gettimeofday(&cdr->end, NULL);
00334       cdr = cdr->next;
00335    }
00336 }
00337 
00338 char *ast_cdr_disp2str(int disposition)
00339 {
00340    switch (disposition) {
00341    case AST_CDR_NOANSWER:
00342       return "NO ANSWER";
00343    case AST_CDR_FAILED:
00344       return "FAILED";     
00345    case AST_CDR_BUSY:
00346       return "BUSY";    
00347    case AST_CDR_ANSWERED:
00348       return "ANSWERED";
00349    default:
00350       return "UNKNOWN";
00351    }
00352 }
00353 
00354 char *ast_cdr_flags2str(int flag)
00355 {
00356    switch(flag) {
00357    case AST_CDR_OMIT:
00358       return "OMIT";
00359    case AST_CDR_BILLING:
00360       return "BILLING";
00361    case AST_CDR_DOCUMENTATION:
00362       return "DOCUMENTATION";
00363    }
00364    return "Unknown";
00365 }
00366 
00367 int ast_cdr_setaccount(struct ast_channel *chan, char *account)
00368 {
00369    struct ast_cdr *cdr = chan->cdr;
00370 
00371    strncpy(chan->accountcode, account, sizeof(chan->accountcode) - 1);
00372    while (cdr) {
00373       if(!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED))
00374          strncpy(cdr->accountcode, chan->accountcode, sizeof(cdr->accountcode) - 1);
00375       cdr = cdr->next;
00376    }
00377    return 0;
00378 }
00379 
00380 int ast_cdr_setamaflags(struct ast_channel *chan, char *flag)
00381 {
00382    struct ast_cdr *cdr = chan->cdr;
00383    int newflag;
00384 
00385    newflag = ast_cdr_amaflags2int(flag);
00386    if (newflag) {
00387       cdr->amaflags = newflag;
00388    }
00389    return 0;
00390 }
00391 
00392 int ast_cdr_setuserfield(struct ast_channel *chan, char *userfield)
00393 {
00394    struct ast_cdr *cdr = chan->cdr;
00395 
00396    while (cdr) {
00397       if(!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED)) 
00398          strncpy(cdr->userfield, userfield, sizeof(cdr->userfield) - 1);
00399       cdr = cdr->next;
00400    }
00401    return 0;
00402 }
00403 
00404 int ast_cdr_appenduserfield(struct ast_channel *chan, char *userfield)
00405 {
00406    struct ast_cdr *cdr = chan->cdr;
00407 
00408    while (cdr)
00409    {
00410 
00411       int len = strlen(cdr->userfield);
00412       if(!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED))
00413          strncpy(cdr->userfield+len, userfield, sizeof(cdr->userfield) - len - 1);
00414       cdr = cdr->next;
00415    }
00416    return 0;
00417 }
00418 
00419 int ast_cdr_update(struct ast_channel *c)
00420 {
00421    struct ast_cdr *cdr = c->cdr;
00422    char *name, *num;
00423    char tmp[AST_MAX_EXTENSION] = "";
00424    /* Grab source from ANI or normal Caller*ID */
00425    while (cdr) {
00426       if(!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED)) {
00427          if (c->ani)
00428             strncpy(tmp, c->ani, sizeof(tmp) - 1);
00429          else if (c->callerid && !ast_strlen_zero(c->callerid))
00430             strncpy(tmp, c->callerid, sizeof(tmp) - 1);
00431          if (c->callerid && !ast_strlen_zero(c->callerid))
00432             strncpy(cdr->clid, c->callerid, sizeof(cdr->clid) - 1);
00433          else
00434             cdr->clid[0] = '\0';
00435          name = NULL;
00436          num = NULL;
00437          ast_callerid_parse(tmp, &name, &num);
00438          if (num) {
00439             ast_shrink_phone_number(num);
00440             strncpy(cdr->src, num, sizeof(cdr->src) - 1);
00441          }
00442          /* Copy account code et-al */ 
00443          strncpy(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode) - 1);
00444          /* Destination information */
00445          if (ast_strlen_zero(c->macroexten))
00446             strncpy(cdr->dst, c->exten, sizeof(cdr->dst) - 1);
00447          else
00448             strncpy(cdr->dst, c->macroexten, sizeof(cdr->dst) - 1);
00449          if (ast_strlen_zero(c->macrocontext))
00450             strncpy(cdr->dcontext, c->context, sizeof(cdr->dcontext) - 1);
00451          else
00452             strncpy(cdr->dcontext, c->macrocontext, sizeof(cdr->dcontext) - 1);
00453       }
00454       cdr = cdr->next;
00455    }
00456 
00457    return 0;
00458 }
00459 
00460 int ast_cdr_amaflags2int(char *flag)
00461 {
00462    if (!strcasecmp(flag, "default"))
00463       return 0;
00464    if (!strcasecmp(flag, "omit"))
00465       return AST_CDR_OMIT;
00466    if (!strcasecmp(flag, "billing"))
00467       return AST_CDR_BILLING;
00468    if (!strcasecmp(flag, "documentation"))
00469       return AST_CDR_DOCUMENTATION;
00470    return -1;
00471 }
00472 
00473 void ast_cdr_post(struct ast_cdr *cdr)
00474 {
00475    char *chan;
00476    struct ast_cdr_beitem *i;
00477    while (cdr) {
00478       chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
00479       if (ast_cdr_has_flag(cdr,AST_CDR_FLAG_POSTED))
00480          ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
00481       if (!cdr->end.tv_sec && !cdr->end.tv_usec)
00482          ast_log(LOG_WARNING, "CDR on channel '%s' lacks end\n", chan);
00483       if (!cdr->start.tv_sec && !cdr->start.tv_usec)
00484          ast_log(LOG_WARNING, "CDR on channel '%s' lacks start\n", chan);
00485       cdr->duration = cdr->end.tv_sec - cdr->start.tv_sec + (cdr->end.tv_usec - cdr->start.tv_usec) / 1000000;
00486       if (cdr->answer.tv_sec || cdr->answer.tv_usec) {
00487          cdr->billsec = cdr->end.tv_sec - cdr->answer.tv_sec + (cdr->end.tv_usec - cdr->answer.tv_usec) / 1000000;
00488       } else
00489          cdr->billsec = 0;
00490       ast_cdr_add_flag(cdr,AST_CDR_FLAG_POSTED);
00491       ast_mutex_lock(&cdrlock);
00492       i = bes;
00493       while(i) {
00494          i->be(cdr);
00495          i = i->next;
00496       }
00497       ast_mutex_unlock(&cdrlock);
00498       cdr = cdr->next;
00499    }
00500 }
00501 
00502 void ast_cdr_reset(struct ast_cdr *cdr, int flags)
00503 {
00504    while (cdr) {
00505       /* Post if requested */
00506       if (ast_cdr_compare_flag(flags,AST_CDR_FLAG_LOCKED) || !ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED)) {
00507          if (ast_cdr_compare_flag(flags,AST_CDR_FLAG_POSTED)) {
00508             ast_cdr_end(cdr);
00509             ast_cdr_post(cdr);
00510          }
00511          /* Reset to initial state */
00512          cdr->flags=0;
00513          memset(&cdr->start, 0, sizeof(cdr->start));
00514          memset(&cdr->end, 0, sizeof(cdr->end));
00515          memset(&cdr->answer, 0, sizeof(cdr->answer));
00516          cdr->billsec = 0;
00517          cdr->duration = 0;
00518          ast_cdr_start(cdr);
00519          cdr->disposition = AST_CDR_NOANSWER;
00520       }
00521          
00522       cdr = cdr->next;
00523    }
00524    
00525 }
00526 
00527 struct ast_cdr *ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr) 
00528 {
00529    struct ast_cdr *ret;
00530    if (cdr) {
00531       ret = cdr;
00532       while(cdr->next)
00533          cdr = cdr->next;
00534       cdr->next = newcdr;
00535    } else {
00536       ret = newcdr;
00537    }
00538    return ret;
00539 }

Generated on Wed Mar 16 20:08:34 2005 for Asterisk by  doxygen 1.4.0