Version 0.1.10 from FTP
[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/channel.h>
18 #include <asterisk/cdr.h>
19 #include <asterisk/logger.h>
20 #include <asterisk/callerid.h>
21 #include <unistd.h>
22 #include <stdlib.h>
23 #include <pthread.h>
24
25 int ast_default_amaflags = AST_CDR_DOCUMENTATION;
26 char ast_default_accountcode[20] = "";
27
28 static pthread_mutex_t cdrlock = PTHREAD_MUTEX_INITIALIZER;
29
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 /*
38  * We do a lot of checking here in the CDR code to try to be sure we don't ever let a CDR slip
39  * through our fingers somehow.  If someone allocates a CDR, it must be completely handled normally
40  * or a WARNING shall be logged, so that we can best keep track of any escape condition where the CDR
41  * isn't properly generated and posted.
42  */
43
44 int ast_cdr_register(char *name, char *desc, ast_cdrbe be)
45 {
46         struct ast_cdr_beitem *i;
47         if (!name)
48                 return -1;
49         if (!be) {
50                 ast_log(LOG_WARNING, "CDR engine '%s' lacks backend\n", name);
51                 return -1;
52         }
53         ast_pthread_mutex_lock(&cdrlock);
54         i = bes;
55         while(i) {
56                 if (!strcasecmp(name, i->name))
57                         break;
58                 i = i->next;
59         }
60         ast_pthread_mutex_unlock(&cdrlock);
61         if (i) {
62                 ast_log(LOG_WARNING, "Already have a CDR backend called '%s'\n", name);
63                 return -1;
64         }
65         i = malloc(sizeof(struct ast_cdr_beitem));
66         if (!i)         
67                 return -1;
68         memset(i, 0, sizeof(struct ast_cdr_beitem));
69         strncpy(i->name, name, sizeof(i->name) - 1);
70         strncpy(i->desc, desc, sizeof(i->desc) - 1);
71         i->be = be;
72         ast_pthread_mutex_lock(&cdrlock);
73         i->next = bes;
74         bes = i;
75         ast_pthread_mutex_unlock(&cdrlock);
76         return 0;
77 }
78
79 void ast_cdr_unregister(char *name)
80 {
81         struct ast_cdr_beitem *i, *prev = NULL;
82         ast_pthread_mutex_lock(&cdrlock);
83         i = bes;
84         while(i) {
85                 if (!strcasecmp(name, i->name)) {
86                         if (prev)
87                                 prev->next = i->next;
88                         else
89                                 bes = i->next;
90                         break;
91                 }
92                 i = i->next;
93         }
94         ast_pthread_mutex_unlock(&cdrlock);
95         if (i) 
96                 free(i);
97 }
98
99 void ast_cdr_free(struct ast_cdr *cdr)
100 {
101         char *chan; 
102         if (cdr) {
103                 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
104                 if (!cdr->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         }
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
124 void ast_cdr_start(struct ast_cdr *cdr)
125 {
126         char *chan; 
127         if (cdr) {
128                 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
129                 if (cdr->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 }
136
137 void ast_cdr_answer(struct ast_cdr *cdr)
138 {
139         char *chan; 
140         if (cdr) {
141                 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
142                 if (cdr->posted)
143                         ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
144                 if (cdr->disposition < AST_CDR_ANSWERED)
145                         cdr->disposition = AST_CDR_ANSWERED;
146                 if (!cdr->answer.tv_sec && !cdr->answer.tv_usec) {
147                         gettimeofday(&cdr->answer, NULL);
148                 }
149         }
150 }
151
152 void ast_cdr_busy(struct ast_cdr *cdr)
153 {
154         char *chan; 
155         if (cdr) {
156                 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
157                 if (cdr->posted)
158                         ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
159                 if (cdr->disposition < AST_CDR_BUSY)
160                         cdr->disposition = AST_CDR_BUSY;
161         }
162 }
163
164 void ast_cdr_setdestchan(struct ast_cdr *cdr, char *chann)
165 {
166         char *chan; 
167         if (cdr) {
168                 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
169                 if (cdr->posted)
170                         ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
171                 strncpy(cdr->dstchannel, chann, sizeof(cdr->dstchannel) - 1);
172         }
173 }
174
175 void ast_cdr_setapp(struct ast_cdr *cdr, char *app, char *data)
176 {
177         char *chan; 
178         if (cdr) {
179                 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
180                 if (cdr->posted)
181                         ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
182                 if (!app)
183                         app = "";
184                 strncpy(cdr->lastapp, app, sizeof(cdr->lastapp) - 1);
185                 if (!data)
186                         data = "";
187                 strncpy(cdr->lastdata, data, sizeof(cdr->lastdata) - 1);
188         }
189 }
190
191 int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c)
192 {
193         char *chan;
194         char *num, *name;
195         char tmp[AST_MAX_EXTENSION];
196         if (cdr) {
197                 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
198                 if (strlen(cdr->channel)) 
199                         ast_log(LOG_WARNING, "CDR already initialized on '%s'\n", chan); 
200                 strncpy(cdr->channel, c->name, sizeof(cdr->channel) - 1);
201                 /* Grab source from hidden or normal Caller*ID */
202                 if (c->hidden_callerid)
203                         strncpy(tmp, c->hidden_callerid, sizeof(tmp) - 1);
204                 else if (c->callerid)
205                         strncpy(tmp, c->callerid, sizeof(tmp) - 1);
206                 if (c->callerid)
207                         strncpy(cdr->clid, c->callerid, sizeof(cdr->clid) - 1);
208                 name = NULL;
209                 num = NULL;
210                 ast_callerid_parse(tmp, &name, &num);
211                 if (num) {
212                         ast_shrink_phone_number(num);
213                         strncpy(cdr->src, num, sizeof(cdr->src) - 1);
214                 }
215                 
216                 if (c->state == AST_STATE_UP)
217                         cdr->disposition = AST_CDR_ANSWERED;
218                 else
219                         cdr->disposition = AST_CDR_NOANSWER;
220                 if (c->amaflags)
221                         cdr->amaflags = c->amaflags;
222                 else
223                         cdr->amaflags = ast_default_amaflags;
224                 strncpy(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode) - 1);
225                 /* Destination information */
226                 strncpy(cdr->dst, c->exten, sizeof(cdr->dst) - 1);
227                 strncpy(cdr->dcontext, c->context, sizeof(cdr->dcontext) - 1);
228         }
229         return 0;
230 }
231
232 void ast_cdr_end(struct ast_cdr *cdr)
233 {
234         char *chan;
235         if (cdr) {
236                 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
237                 if (cdr->posted)
238                         ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
239                 if (!cdr->start.tv_sec && !cdr->start.tv_usec)
240                         ast_log(LOG_WARNING, "CDR on channel '%s' has not started\n", chan);
241                 if (!cdr->end.tv_sec && !cdr->end.tv_usec) 
242                         gettimeofday(&cdr->end, NULL);
243         }
244 }
245
246 char *ast_cdr_disp2str(int disposition)
247 {
248         switch (disposition) {
249         case AST_CDR_NOANSWER:
250                 return "NO ANSWER";
251         case AST_CDR_BUSY:
252                 return "BUSY";          
253         case AST_CDR_ANSWERED:
254                 return "ANSWERED";
255         default:
256                 return "UNKNOWN";
257         }
258 }
259
260 char *ast_cdr_flags2str(int flag)
261 {
262         switch(flag) {
263         case AST_CDR_OMIT:
264                 return "OMIT";
265         case AST_CDR_BILLING:
266                 return "BILLING";
267         case AST_CDR_DOCUMENTATION:
268                 return "DOCUMENTATION";
269         }
270         return "Unknown";
271 }
272
273 int ast_cdr_amaflags2int(char *flag)
274 {
275         if (!strcasecmp(flag, "default"))
276                 return 0;
277         if (!strcasecmp(flag, "omit"))
278                 return AST_CDR_OMIT;
279         if (!strcasecmp(flag, "billing"))
280                 return AST_CDR_BILLING;
281         if (!strcasecmp(flag, "documentation"))
282                 return AST_CDR_DOCUMENTATION;
283         return -1;
284 }
285
286 void ast_cdr_post(struct ast_cdr *cdr)
287 {
288         char *chan;
289         struct ast_cdr_beitem *i;
290         if (cdr) {
291                 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
292                 if (cdr->posted)
293                         ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
294                 if (!cdr->end.tv_sec && !cdr->end.tv_usec)
295                         ast_log(LOG_WARNING, "CDR on channel '%s' lacks end\n", chan);
296                 if (!cdr->start.tv_sec && !cdr->start.tv_usec)
297                         ast_log(LOG_WARNING, "CDR on channel '%s' lacks start\n", chan);
298                 cdr->duration = cdr->end.tv_sec - cdr->start.tv_sec + (cdr->end.tv_usec - cdr->start.tv_usec) / 1000000;
299                 if (cdr->answer.tv_sec || cdr->answer.tv_usec) {
300                         cdr->billsec = cdr->end.tv_sec - cdr->answer.tv_sec + (cdr->end.tv_usec - cdr->answer.tv_usec) / 1000000;
301                 } else
302                         cdr->billsec = 0;
303                 cdr->posted = 1;
304                 ast_pthread_mutex_lock(&cdrlock);
305                 i = bes;
306                 while(i) {
307                         i->be(cdr);
308                         i = i->next;
309                 }
310                 ast_pthread_mutex_unlock(&cdrlock);
311         }
312 }