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