00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
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
00044
00045
00046
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
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
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
00313 strncpy(cdr->dst, c->exten, sizeof(cdr->dst) - 1);
00314 strncpy(cdr->dcontext, c->context, sizeof(cdr->dcontext) - 1);
00315
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
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
00443 strncpy(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode) - 1);
00444
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
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
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 }