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 <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
00042
00043
00044
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
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
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
00287 strncpy(cdr->dst, c->exten, sizeof(cdr->dst) - 1);
00288 strncpy(cdr->dcontext, c->context, sizeof(cdr->dcontext) - 1);
00289
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
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
00392 strncpy(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode) - 1);
00393
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
00445 if (post) {
00446 ast_cdr_end(cdr);
00447 ast_cdr_post(cdr);
00448 }
00449
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 }