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-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 post) 00503 { 00504 while (cdr) { 00505 /* Post if requested */ 00506 if (post) { 00507 ast_cdr_end(cdr); 00508 ast_cdr_post(cdr); 00509 } 00510 /* Reset to initial state */ 00511 cdr->flags=0; 00512 memset(&cdr->start, 0, sizeof(cdr->start)); 00513 memset(&cdr->end, 0, sizeof(cdr->end)); 00514 memset(&cdr->answer, 0, sizeof(cdr->answer)); 00515 cdr->billsec = 0; 00516 cdr->duration = 0; 00517 ast_cdr_start(cdr); 00518 cdr->disposition = AST_CDR_NOANSWER; 00519 cdr = cdr->next; 00520 } 00521 } 00522 00523 void ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr) { 00524 if (cdr) { 00525 while(cdr->next) 00526 cdr = cdr->next; 00527 cdr->next = newcdr; 00528 } else 00529 ast_log(LOG_ERROR, "Can't append a CDR to NULL!\n"); 00530 }

Generated on Fri Sep 24 21:03:46 2004 for Asterisk by doxygen 1.3.8