cdr.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. /*
  2. * Asterisk -- A telephony toolkit for Linux.
  3. *
  4. * Call Detail Record API
  5. *
  6. * Copyright (C) 1999-2004, Digium, Inc.
  7. *
  8. * Mark Spencer <markster@digium.com>
  9. *
  10. * This program is free software, distributed under the terms of
  11. * the GNU General Public License.
  12. *
  13. * Includes code and algorithms from the Zapata library.
  14. *
  15. */
  16. #include <asterisk/lock.h>
  17. #include <asterisk/channel.h>
  18. #include <asterisk/cdr.h>
  19. #include <asterisk/logger.h>
  20. #include <asterisk/callerid.h>
  21. #include <asterisk/causes.h>
  22. #include <asterisk/options.h>
  23. #include <asterisk/utils.h>
  24. #include <unistd.h>
  25. #include <stdlib.h>
  26. #include <string.h>
  27. int ast_default_amaflags = AST_CDR_DOCUMENTATION;
  28. char ast_default_accountcode[20] = "";
  29. AST_MUTEX_DEFINE_STATIC(cdrlock);
  30. static struct ast_cdr_beitem {
  31. char name[20];
  32. char desc[80];
  33. ast_cdrbe be;
  34. struct ast_cdr_beitem *next;
  35. } *bes = NULL;
  36. /*
  37. * We do a lot of checking here in the CDR code to try to be sure we don't ever let a CDR slip
  38. * through our fingers somehow. If someone allocates a CDR, it must be completely handled normally
  39. * or a WARNING shall be logged, so that we can best keep track of any escape condition where the CDR
  40. * isn't properly generated and posted.
  41. */
  42. int ast_cdr_register(char *name, char *desc, ast_cdrbe be)
  43. {
  44. struct ast_cdr_beitem *i;
  45. if (!name)
  46. return -1;
  47. if (!be) {
  48. ast_log(LOG_WARNING, "CDR engine '%s' lacks backend\n", name);
  49. return -1;
  50. }
  51. ast_mutex_lock(&cdrlock);
  52. i = bes;
  53. while(i) {
  54. if (!strcasecmp(name, i->name))
  55. break;
  56. i = i->next;
  57. }
  58. ast_mutex_unlock(&cdrlock);
  59. if (i) {
  60. ast_log(LOG_WARNING, "Already have a CDR backend called '%s'\n", name);
  61. return -1;
  62. }
  63. i = malloc(sizeof(struct ast_cdr_beitem));
  64. if (!i)
  65. return -1;
  66. memset(i, 0, sizeof(struct ast_cdr_beitem));
  67. strncpy(i->name, name, sizeof(i->name) - 1);
  68. strncpy(i->desc, desc, sizeof(i->desc) - 1);
  69. i->be = be;
  70. ast_mutex_lock(&cdrlock);
  71. i->next = bes;
  72. bes = i;
  73. ast_mutex_unlock(&cdrlock);
  74. return 0;
  75. }
  76. void ast_cdr_unregister(char *name)
  77. {
  78. struct ast_cdr_beitem *i, *prev = NULL;
  79. ast_mutex_lock(&cdrlock);
  80. i = bes;
  81. while(i) {
  82. if (!strcasecmp(name, i->name)) {
  83. if (prev)
  84. prev->next = i->next;
  85. else
  86. bes = i->next;
  87. break;
  88. }
  89. i = i->next;
  90. }
  91. if (option_verbose > 1)
  92. ast_verbose(VERBOSE_PREFIX_2 "Unregistered '%s' CDR backend\n", name);
  93. ast_mutex_unlock(&cdrlock);
  94. if (i)
  95. free(i);
  96. }
  97. void ast_cdr_free(struct ast_cdr *cdr)
  98. {
  99. char *chan;
  100. struct ast_cdr *next;
  101. while (cdr) {
  102. next = cdr->next;
  103. chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
  104. if (!ast_cdr_has_flag(cdr,AST_CDR_FLAG_POSTED))
  105. ast_log(LOG_WARNING, "CDR on channel '%s' not posted\n", chan);
  106. if (!cdr->end.tv_sec && !cdr->end.tv_usec)
  107. ast_log(LOG_WARNING, "CDR on channel '%s' lacks end\n", chan);
  108. if (!cdr->start.tv_sec && !cdr->start.tv_usec)
  109. ast_log(LOG_WARNING, "CDR on channel '%s' lacks start\n", chan);
  110. free(cdr);
  111. cdr = next;
  112. }
  113. }
  114. struct ast_cdr *ast_cdr_alloc(void)
  115. {
  116. struct ast_cdr *cdr;
  117. cdr = malloc(sizeof(struct ast_cdr));
  118. if (cdr) {
  119. memset(cdr, 0, sizeof(struct ast_cdr));
  120. }
  121. return cdr;
  122. }
  123. void ast_cdr_start(struct ast_cdr *cdr)
  124. {
  125. char *chan;
  126. while (cdr) {
  127. if (!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED)) {
  128. chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
  129. if (ast_cdr_has_flag(cdr,AST_CDR_FLAG_POSTED))
  130. ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
  131. if (cdr->start.tv_sec || cdr->start.tv_usec)
  132. ast_log(LOG_WARNING, "CDR on channel '%s' already started\n", chan);
  133. gettimeofday(&cdr->start, NULL);
  134. }
  135. cdr = cdr->next;
  136. }
  137. }
  138. void ast_cdr_answer(struct ast_cdr *cdr)
  139. {
  140. char *chan;
  141. while (cdr) {
  142. chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
  143. if (ast_cdr_has_flag(cdr,AST_CDR_FLAG_POSTED))
  144. ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
  145. if (cdr->disposition < AST_CDR_ANSWERED)
  146. cdr->disposition = AST_CDR_ANSWERED;
  147. if (!cdr->answer.tv_sec && !cdr->answer.tv_usec) {
  148. gettimeofday(&cdr->answer, NULL);
  149. }
  150. cdr = cdr->next;
  151. }
  152. }
  153. void ast_cdr_busy(struct ast_cdr *cdr)
  154. {
  155. char *chan;
  156. while (cdr) {
  157. if (!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED)) {
  158. chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
  159. if (ast_cdr_has_flag(cdr,AST_CDR_FLAG_POSTED))
  160. ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
  161. if (cdr->disposition < AST_CDR_BUSY)
  162. cdr->disposition = AST_CDR_BUSY;
  163. }
  164. cdr = cdr->next;
  165. }
  166. }
  167. void ast_cdr_failed(struct ast_cdr *cdr)
  168. {
  169. char *chan;
  170. while (cdr) {
  171. chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
  172. if (ast_cdr_has_flag(cdr,AST_CDR_FLAG_POSTED))
  173. ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
  174. if(!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED))
  175. cdr->disposition = AST_CDR_FAILED;
  176. cdr = cdr->next;
  177. }
  178. }
  179. int ast_cdr_disposition(struct ast_cdr *cdr, int cause)
  180. {
  181. int res = 0;
  182. while (cdr) {
  183. switch(cause) {
  184. case AST_CAUSE_BUSY:
  185. ast_cdr_busy(cdr);
  186. break;
  187. case AST_CAUSE_FAILURE:
  188. ast_cdr_failed(cdr);
  189. break;
  190. case AST_CAUSE_NORMAL:
  191. break;
  192. case AST_CAUSE_NOTDEFINED:
  193. res = -1;
  194. break;
  195. default:
  196. res = -1;
  197. ast_log(LOG_WARNING, "We don't handle that cause yet\n");
  198. }
  199. cdr = cdr->next;
  200. }
  201. return res;
  202. }
  203. void ast_cdr_setdestchan(struct ast_cdr *cdr, char *chann)
  204. {
  205. char *chan;
  206. while (cdr) {
  207. chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
  208. if (ast_cdr_has_flag(cdr,AST_CDR_FLAG_POSTED))
  209. ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
  210. if(!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED))
  211. strncpy(cdr->dstchannel, chann, sizeof(cdr->dstchannel) - 1);
  212. cdr = cdr->next;
  213. }
  214. }
  215. void ast_cdr_setapp(struct ast_cdr *cdr, char *app, char *data)
  216. {
  217. char *chan;
  218. while (cdr) {
  219. if(!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED)) {
  220. chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
  221. if (ast_cdr_has_flag(cdr,AST_CDR_FLAG_POSTED))
  222. ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
  223. if (!app)
  224. app = "";
  225. strncpy(cdr->lastapp, app, sizeof(cdr->lastapp) - 1);
  226. if (!data)
  227. data = "";
  228. strncpy(cdr->lastdata, data, sizeof(cdr->lastdata) - 1);
  229. }
  230. cdr = cdr->next;
  231. }
  232. }
  233. int ast_cdr_setcid(struct ast_cdr *cdr, struct ast_channel *c)
  234. {
  235. char tmp[AST_MAX_EXTENSION] = "";
  236. char *num, *name;
  237. while (cdr) {
  238. if(!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED)) {
  239. /* Grab source from ANI or normal Caller*ID */
  240. if (c->ani)
  241. strncpy(tmp, c->ani, sizeof(tmp) - 1);
  242. else if (c->callerid)
  243. strncpy(tmp, c->callerid, sizeof(tmp) - 1);
  244. if (c->callerid)
  245. strncpy(cdr->clid, c->callerid, sizeof(cdr->clid) - 1);
  246. name = NULL;
  247. num = NULL;
  248. ast_callerid_parse(tmp, &name, &num);
  249. if (num) {
  250. ast_shrink_phone_number(num);
  251. strncpy(cdr->src, num, sizeof(cdr->src) - 1);
  252. }
  253. }
  254. cdr = cdr->next;
  255. }
  256. return 0;
  257. }
  258. int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c)
  259. {
  260. char *chan;
  261. char *num, *name;
  262. char tmp[AST_MAX_EXTENSION] = "";
  263. while (cdr) {
  264. if(!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED)) {
  265. chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
  266. if (!ast_strlen_zero(cdr->channel))
  267. ast_log(LOG_WARNING, "CDR already initialized on '%s'\n", chan);
  268. strncpy(cdr->channel, c->name, sizeof(cdr->channel) - 1);
  269. /* Grab source from ANI or normal Caller*ID */
  270. if (c->ani)
  271. strncpy(tmp, c->ani, sizeof(tmp) - 1);
  272. else if (c->callerid)
  273. strncpy(tmp, c->callerid, sizeof(tmp) - 1);
  274. if (c->callerid)
  275. strncpy(cdr->clid, c->callerid, sizeof(cdr->clid) - 1);
  276. name = NULL;
  277. num = NULL;
  278. ast_callerid_parse(tmp, &name, &num);
  279. if (num) {
  280. ast_shrink_phone_number(num);
  281. strncpy(cdr->src, num, sizeof(cdr->src) - 1);
  282. }
  283. if (c->_state == AST_STATE_UP)
  284. cdr->disposition = AST_CDR_ANSWERED;
  285. else
  286. cdr->disposition = AST_CDR_NOANSWER;
  287. if (c->amaflags)
  288. cdr->amaflags = c->amaflags;
  289. else
  290. cdr->amaflags = ast_default_amaflags;
  291. strncpy(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode) - 1);
  292. /* Destination information */
  293. strncpy(cdr->dst, c->exten, sizeof(cdr->dst) - 1);
  294. strncpy(cdr->dcontext, c->context, sizeof(cdr->dcontext) - 1);
  295. /* Unique call identifier */
  296. strncpy(cdr->uniqueid, c->uniqueid, sizeof(cdr->uniqueid) - 1);
  297. }
  298. cdr = cdr->next;
  299. }
  300. return 0;
  301. }
  302. void ast_cdr_end(struct ast_cdr *cdr)
  303. {
  304. char *chan;
  305. while (cdr) {
  306. chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
  307. if (ast_cdr_has_flag(cdr,AST_CDR_FLAG_POSTED))
  308. ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
  309. if (!cdr->start.tv_sec && !cdr->start.tv_usec)
  310. ast_log(LOG_WARNING, "CDR on channel '%s' has not started\n", chan);
  311. if (!cdr->end.tv_sec && !cdr->end.tv_usec)
  312. gettimeofday(&cdr->end, NULL);
  313. cdr = cdr->next;
  314. }
  315. }
  316. char *ast_cdr_disp2str(int disposition)
  317. {
  318. switch (disposition) {
  319. case AST_CDR_NOANSWER:
  320. return "NO ANSWER";
  321. case AST_CDR_FAILED:
  322. return "FAILED";
  323. case AST_CDR_BUSY:
  324. return "BUSY";
  325. case AST_CDR_ANSWERED:
  326. return "ANSWERED";
  327. default:
  328. return "UNKNOWN";
  329. }
  330. }
  331. char *ast_cdr_flags2str(int flag)
  332. {
  333. switch(flag) {
  334. case AST_CDR_OMIT:
  335. return "OMIT";
  336. case AST_CDR_BILLING:
  337. return "BILLING";
  338. case AST_CDR_DOCUMENTATION:
  339. return "DOCUMENTATION";
  340. }
  341. return "Unknown";
  342. }
  343. int ast_cdr_setaccount(struct ast_channel *chan, char *account)
  344. {
  345. struct ast_cdr *cdr = chan->cdr;
  346. strncpy(chan->accountcode, account, sizeof(chan->accountcode) - 1);
  347. while (cdr) {
  348. if(!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED))
  349. strncpy(cdr->accountcode, chan->accountcode, sizeof(cdr->accountcode) - 1);
  350. cdr = cdr->next;
  351. }
  352. return 0;
  353. }
  354. int ast_cdr_setamaflags(struct ast_channel *chan, char *flag)
  355. {
  356. struct ast_cdr *cdr = chan->cdr;
  357. int newflag;
  358. newflag = ast_cdr_amaflags2int(flag);
  359. if (newflag) {
  360. cdr->amaflags = newflag;
  361. }
  362. return 0;
  363. }
  364. int ast_cdr_setuserfield(struct ast_channel *chan, char *userfield)
  365. {
  366. struct ast_cdr *cdr = chan->cdr;
  367. while (cdr) {
  368. if(!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED))
  369. strncpy(cdr->userfield, userfield, sizeof(cdr->userfield) - 1);
  370. cdr = cdr->next;
  371. }
  372. return 0;
  373. }
  374. int ast_cdr_appenduserfield(struct ast_channel *chan, char *userfield)
  375. {
  376. struct ast_cdr *cdr = chan->cdr;
  377. while (cdr)
  378. {
  379. int len = strlen(cdr->userfield);
  380. if(!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED))
  381. strncpy(cdr->userfield+len, userfield, sizeof(cdr->userfield) - len - 1);
  382. cdr = cdr->next;
  383. }
  384. return 0;
  385. }
  386. int ast_cdr_update(struct ast_channel *c)
  387. {
  388. struct ast_cdr *cdr = c->cdr;
  389. char *name, *num;
  390. char tmp[AST_MAX_EXTENSION] = "";
  391. /* Grab source from ANI or normal Caller*ID */
  392. while (cdr) {
  393. if(!ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED)) {
  394. if (c->ani)
  395. strncpy(tmp, c->ani, sizeof(tmp) - 1);
  396. else if (c->callerid && !ast_strlen_zero(c->callerid))
  397. strncpy(tmp, c->callerid, sizeof(tmp) - 1);
  398. if (c->callerid && !ast_strlen_zero(c->callerid))
  399. strncpy(cdr->clid, c->callerid, sizeof(cdr->clid) - 1);
  400. else
  401. cdr->clid[0] = '\0';
  402. name = NULL;
  403. num = NULL;
  404. ast_callerid_parse(tmp, &name, &num);
  405. if (num) {
  406. ast_shrink_phone_number(num);
  407. strncpy(cdr->src, num, sizeof(cdr->src) - 1);
  408. }
  409. /* Copy account code et-al */
  410. strncpy(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode) - 1);
  411. /* Destination information */
  412. if (ast_strlen_zero(c->macroexten))
  413. strncpy(cdr->dst, c->exten, sizeof(cdr->dst) - 1);
  414. else
  415. strncpy(cdr->dst, c->macroexten, sizeof(cdr->dst) - 1);
  416. if (ast_strlen_zero(c->macrocontext))
  417. strncpy(cdr->dcontext, c->context, sizeof(cdr->dcontext) - 1);
  418. else
  419. strncpy(cdr->dcontext, c->macrocontext, sizeof(cdr->dcontext) - 1);
  420. }
  421. cdr = cdr->next;
  422. }
  423. return 0;
  424. }
  425. int ast_cdr_amaflags2int(char *flag)
  426. {
  427. if (!strcasecmp(flag, "default"))
  428. return 0;
  429. if (!strcasecmp(flag, "omit"))
  430. return AST_CDR_OMIT;
  431. if (!strcasecmp(flag, "billing"))
  432. return AST_CDR_BILLING;
  433. if (!strcasecmp(flag, "documentation"))
  434. return AST_CDR_DOCUMENTATION;
  435. return -1;
  436. }
  437. void ast_cdr_post(struct ast_cdr *cdr)
  438. {
  439. char *chan;
  440. struct ast_cdr_beitem *i;
  441. while (cdr) {
  442. chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
  443. if (ast_cdr_has_flag(cdr,AST_CDR_FLAG_POSTED))
  444. ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
  445. if (!cdr->end.tv_sec && !cdr->end.tv_usec)
  446. ast_log(LOG_WARNING, "CDR on channel '%s' lacks end\n", chan);
  447. if (!cdr->start.tv_sec && !cdr->start.tv_usec)
  448. ast_log(LOG_WARNING, "CDR on channel '%s' lacks start\n", chan);
  449. cdr->duration = cdr->end.tv_sec - cdr->start.tv_sec + (cdr->end.tv_usec - cdr->start.tv_usec) / 1000000;
  450. if (cdr->answer.tv_sec || cdr->answer.tv_usec) {
  451. cdr->billsec = cdr->end.tv_sec - cdr->answer.tv_sec + (cdr->end.tv_usec - cdr->answer.tv_usec) / 1000000;
  452. } else
  453. cdr->billsec = 0;
  454. ast_cdr_add_flag(cdr,AST_CDR_FLAG_POSTED);
  455. ast_mutex_lock(&cdrlock);
  456. i = bes;
  457. while(i) {
  458. i->be(cdr);
  459. i = i->next;
  460. }
  461. ast_mutex_unlock(&cdrlock);
  462. cdr = cdr->next;
  463. }
  464. }
  465. void ast_cdr_reset(struct ast_cdr *cdr, int flags)
  466. {
  467. while (cdr) {
  468. /* Post if requested */
  469. if (ast_cdr_compare_flag(flags,AST_CDR_FLAG_LOCKED) || !ast_cdr_has_flag(cdr,AST_CDR_FLAG_LOCKED)) {
  470. if (ast_cdr_compare_flag(flags,AST_CDR_FLAG_POSTED)) {
  471. ast_cdr_end(cdr);
  472. ast_cdr_post(cdr);
  473. }
  474. /* Reset to initial state */
  475. cdr->flags=0;
  476. memset(&cdr->start, 0, sizeof(cdr->start));
  477. memset(&cdr->end, 0, sizeof(cdr->end));
  478. memset(&cdr->answer, 0, sizeof(cdr->answer));
  479. cdr->billsec = 0;
  480. cdr->duration = 0;
  481. ast_cdr_start(cdr);
  482. cdr->disposition = AST_CDR_NOANSWER;
  483. }
  484. cdr = cdr->next;
  485. }
  486. }
  487. struct ast_cdr *ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr)
  488. {
  489. struct ast_cdr *ret;
  490. if (cdr) {
  491. ret = cdr;
  492. while(cdr->next)
  493. cdr = cdr->next;
  494. cdr->next = newcdr;
  495. } else {
  496. ret = newcdr;
  497. }
  498. return ret;
  499. }