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