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