Add Q.SIG option to zapata.conf
[asterisk/asterisk.git] / cdr.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Call Detail Record API 
5  * 
6  * Copyright (C) 1999 - 2005, 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
17 #include <unistd.h>
18 #include <stdlib.h>
19 #include <string.h>
20
21 #include "asterisk/lock.h"
22 #include "asterisk/channel.h"
23 #include "asterisk/cdr.h"
24 #include "asterisk/logger.h"
25 #include "asterisk/callerid.h"
26 #include "asterisk/causes.h"
27 #include "asterisk/options.h"
28 #include "asterisk/utils.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 static const char *ast_cdr_getvar_internal(struct ast_cdr *cdr, const char *name, int recur) 
108 {
109         struct ast_var_t *variables;
110         struct varshead *headp;
111
112         while(cdr) {
113                 headp = &cdr->varshead;
114                 if (name) {
115                         AST_LIST_TRAVERSE(headp,variables,entries) {
116                                 if (!strcmp(name, ast_var_name(variables)))
117                                         return ast_var_value(variables);
118                         }
119                 }
120                 if (!recur) {
121                         break;
122                 }
123                 cdr = cdr->next;
124         }
125         return NULL;
126 }
127
128 #define ast_val_or_null(val) do { \
129         if (val[0]) { \
130                 strncpy(workspace, val, workspacelen - 1);\
131                 *ret = workspace; \
132         } \
133 } while(0)
134
135 void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret, char *workspace, int workspacelen, int recur) 
136 {
137         struct tm tm;
138         time_t t;
139         const char *fmt = "%Y-%m-%d %T";
140
141         *ret = NULL;
142         /* special vars (the ones from the struct ast_cdr when requested by name) 
143            I'd almost say we should convert all the stringed vals to vars */
144
145         if (!strcasecmp(name, "clid")) {
146                 ast_val_or_null(cdr->clid);
147         } else if (!strcasecmp(name, "src")) {
148                 ast_val_or_null(cdr->src);
149         } else if (!strcasecmp(name, "dst")) {
150                 ast_val_or_null(cdr->dst);
151         } else if (!strcasecmp(name, "dcontext")) {
152                 ast_val_or_null(cdr->dcontext);
153         } else if (!strcasecmp(name, "channel")) {
154                 ast_val_or_null(cdr->channel);
155         } else if (!strcasecmp(name, "dstchannel")) {
156                 ast_val_or_null(cdr->dstchannel);
157         } else if (!strcasecmp(name, "lastapp")) {
158                 ast_val_or_null(cdr->lastapp);
159         } else if (!strcasecmp(name, "lastdata")) {
160                 ast_val_or_null(cdr->lastdata);
161         } else if (!strcasecmp(name, "start")) {
162                 t = cdr->start.tv_sec;
163                 if (t) {
164                         localtime_r(&t,&tm);
165                         strftime(workspace, workspacelen, fmt, &tm);
166                         *ret = workspace;
167                 }
168         } else if (!strcasecmp(name, "answer")) {
169                 t = cdr->start.tv_sec;
170                 if (t) {
171                         localtime_r(&t,&tm);
172                         strftime(workspace, workspacelen, fmt, &tm);
173                         *ret = workspace;
174                 }
175         } else if (!strcasecmp(name, "end")) {
176                 t = cdr->start.tv_sec;
177                 if (t) {
178                         localtime_r(&t,&tm);
179                         strftime(workspace, workspacelen, fmt, &tm);
180                         *ret = workspace;
181                 }
182         } else if (!strcasecmp(name, "duration")) {
183                 snprintf(workspace, workspacelen, "%d", cdr->duration);
184                 *ret = workspace;
185         } else if (!strcasecmp(name, "billsec")) {
186                 snprintf(workspace, workspacelen, "%d", cdr->billsec);
187                 *ret = workspace;
188         } else if (!strcasecmp(name, "disposition")) {
189                 strncpy(workspace, ast_cdr_disp2str(cdr->disposition), workspacelen - 1);
190                 *ret = workspace;
191         } else if (!strcasecmp(name, "amaflags")) {
192                 strncpy(workspace, ast_cdr_flags2str(cdr->amaflags), workspacelen - 1);
193                 *ret = workspace;
194         } else if (!strcasecmp(name, "accountcode")) {
195                 ast_val_or_null(cdr->accountcode);
196         } else if (!strcasecmp(name, "uniqueid")) {
197                 ast_val_or_null(cdr->uniqueid);
198         } else if (!strcasecmp(name, "userfield")) {
199                 ast_val_or_null(cdr->userfield);
200         } else {
201                 if ((*ret = (char *)ast_cdr_getvar_internal(cdr, name, recur))) {
202                         strncpy(workspace, *ret, workspacelen - 1);
203                         *ret = workspace;
204                 }
205         }
206 }
207
208 int ast_cdr_setvar(struct ast_cdr *cdr, const char *name, const char *value, int recur) 
209 {
210         struct ast_var_t *newvariable;
211     struct varshead *headp;
212         
213     if (!cdr) {
214                 ast_log(LOG_ERROR, "Attempt to set a variable on a nonexistent CDR record.\n");
215                 return -1;
216         }
217         while (cdr) {
218                 headp = &cdr->varshead;
219                 AST_LIST_TRAVERSE (headp, newvariable, entries) {
220                         if (strcasecmp(ast_var_name(newvariable), name) == 0) {
221                                 /* there is already such a variable, delete it */
222                                 AST_LIST_REMOVE(headp, newvariable, entries);
223                                 ast_var_delete(newvariable);
224                                 break;
225                         }
226                 }
227
228                 if (value) {
229                         newvariable = ast_var_assign(name, value);
230                         AST_LIST_INSERT_HEAD(headp, newvariable, entries);
231                 }
232
233                 if (!recur) {
234                         break;
235                 }
236                 cdr = cdr->next;
237         }
238         return 0;
239 }
240
241 int ast_cdr_copy_vars(struct ast_cdr *to_cdr, struct ast_cdr *from_cdr)
242 {
243         struct ast_var_t *variables, *newvariable = NULL;
244     struct varshead *headpa, *headpb;
245         char *var, *val;
246         int x = 0;
247
248         headpa=&from_cdr->varshead;
249         headpb=&to_cdr->varshead;
250
251         AST_LIST_TRAVERSE(headpa,variables,entries) {
252                 if (variables && (var=ast_var_name(variables)) && (val=ast_var_value(variables)) && !ast_strlen_zero(var) && !ast_strlen_zero(val)) {
253                         newvariable = ast_var_assign(var, val);
254                         AST_LIST_INSERT_HEAD(headpb, newvariable, entries);
255                         x++;
256                 }
257         }
258
259         return x;
260 }
261
262 #define CDR_CLEN 18
263 int ast_cdr_serialize_variables(struct ast_cdr *cdr, char *buf, size_t size, char delim, char sep, int recur) 
264 {
265         struct ast_var_t *variables;
266         struct varshead *headp;
267         char *var=NULL ,*val=NULL;
268         char *tmp = NULL;
269         char workspace[256];
270         int workspacelen;
271         int total = 0, x = 0, i = 0;
272         const char *cdrcols[CDR_CLEN] = { 
273                 "clid",
274                 "src",
275                 "dst",
276                 "dcontext",
277                 "channel",
278                 "dstchannel",
279                 "lastapp",
280                 "lastdata",
281                 "start",
282                 "answer",
283                 "end",
284                 "duration",
285                 "billsec",
286                 "disposition",
287                 "amaflags",
288                 "accountcode",
289                 "uniqueid",
290                 "userfield"
291         };
292
293
294         memset(buf,0,size);
295         while (cdr) {
296                 x++;
297                 if (x > 1) {
298                         strncat(buf, "\n", size);
299                 }
300                 headp=&cdr->varshead;
301                 AST_LIST_TRAVERSE(headp,variables,entries) {
302                         if (cdr && variables && (var=ast_var_name(variables)) && (val=ast_var_value(variables)) && !ast_strlen_zero(var) && !ast_strlen_zero(val)) {
303                                 snprintf(buf + strlen(buf), size - strlen(buf), "level %d: %s%c%s%c", x, var, delim, val, sep);
304                                 if (strlen(buf) >= size) {
305                                         ast_log(LOG_ERROR,"Data Buffer Size Exceeded!\n");
306                                         break;
307                                 }
308                                 total++;
309                         } else 
310                                 break;
311                 }
312                 for (i = 0 ; i < CDR_CLEN; i++) {
313                         workspacelen = sizeof(workspace);
314                         ast_cdr_getvar(cdr, cdrcols[i], &tmp, workspace, workspacelen, 0);
315                         if (!tmp)
316                                 continue;
317                         
318                         snprintf(buf + strlen(buf), size - strlen(buf), "level %d: %s%c%s%c", x, cdrcols[i], delim, tmp, sep);
319                         if (strlen(buf) >= size) {
320                                 ast_log(LOG_ERROR,"Data Buffer Size Exceeded!\n");
321                                 break;
322                         }
323                         total++;
324                 }
325
326                 if (!recur) {
327                         break;
328                 }
329                 cdr = cdr->next;
330         }
331         return total;
332 }
333
334
335 void ast_cdr_free_vars(struct ast_cdr *cdr, int recur)
336 {
337         struct varshead *headp;
338         struct ast_var_t *vardata;
339
340         /* clear variables */
341         while(cdr) {
342                 headp = &cdr->varshead;
343                 while (!AST_LIST_EMPTY(headp)) {
344                         vardata = AST_LIST_REMOVE_HEAD(headp, entries);
345                         ast_var_delete(vardata);
346                 }
347                 if (!recur) {
348                         break;
349                 }
350                 cdr = cdr->next;
351         }
352 }
353
354 void ast_cdr_free(struct ast_cdr *cdr)
355 {
356         char *chan;
357         struct ast_cdr *next; 
358
359         while (cdr) {
360                 next = cdr->next;
361                 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
362                 if (!ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
363                         ast_log(LOG_WARNING, "CDR on channel '%s' not posted\n", chan);
364                 if (!cdr->end.tv_sec && !cdr->end.tv_usec)
365                         ast_log(LOG_WARNING, "CDR on channel '%s' lacks end\n", chan);
366                 if (!cdr->start.tv_sec && !cdr->start.tv_usec)
367                         ast_log(LOG_WARNING, "CDR on channel '%s' lacks start\n", chan);
368
369                 ast_cdr_free_vars(cdr, 0);
370                 free(cdr);
371                 cdr = next;
372         }
373 }
374
375 struct ast_cdr *ast_cdr_alloc(void)
376 {
377         struct ast_cdr *cdr;
378         cdr = malloc(sizeof(struct ast_cdr));
379         if (cdr) {
380                 memset(cdr, 0, sizeof(struct ast_cdr));
381         }
382         return cdr;
383 }
384
385 void ast_cdr_start(struct ast_cdr *cdr)
386 {
387         char *chan; 
388         while (cdr) {
389                 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
390                         chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
391                         if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
392                                 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
393                         if (cdr->start.tv_sec || cdr->start.tv_usec)
394                                 ast_log(LOG_WARNING, "CDR on channel '%s' already started\n", chan);
395                         gettimeofday(&cdr->start, NULL);
396                 }
397                         cdr = cdr->next;
398         }
399 }
400
401 void ast_cdr_answer(struct ast_cdr *cdr)
402 {
403         char *chan; 
404         while (cdr) {
405                 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
406                 if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
407                         ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
408                 if (cdr->disposition < AST_CDR_ANSWERED)
409                         cdr->disposition = AST_CDR_ANSWERED;
410                 if (!cdr->answer.tv_sec && !cdr->answer.tv_usec) {
411                         gettimeofday(&cdr->answer, NULL);
412                 }
413                 cdr = cdr->next;
414         }
415 }
416
417 void ast_cdr_busy(struct ast_cdr *cdr)
418 {
419         char *chan; 
420         while (cdr) {
421                 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
422                         chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
423                         if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
424                                 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
425                         if (cdr->disposition < AST_CDR_BUSY)
426                                 cdr->disposition = AST_CDR_BUSY;
427                 }
428                 cdr = cdr->next;
429         }
430 }
431
432 void ast_cdr_failed(struct ast_cdr *cdr)
433 {
434         char *chan; 
435         while (cdr) {
436                 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
437                 if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
438                         ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
439                 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
440                         cdr->disposition = AST_CDR_FAILED;
441                 cdr = cdr->next;
442         }
443 }
444
445 int ast_cdr_disposition(struct ast_cdr *cdr, int cause)
446 {
447         int res = 0;
448         while (cdr) {
449                 switch(cause) {
450                         case AST_CAUSE_BUSY:
451                                 ast_cdr_busy(cdr);
452                                 break;
453                         case AST_CAUSE_FAILURE:
454                                 ast_cdr_failed(cdr);
455                                 break;
456                         case AST_CAUSE_NORMAL:
457                                 break;
458                         case AST_CAUSE_NOTDEFINED:
459                                 res = -1;
460                                 break;
461                         default:
462                                 res = -1;
463                                 ast_log(LOG_WARNING, "Cause not handled\n");
464                 }
465                 cdr = cdr->next;
466         }
467         return res;
468 }
469
470 void ast_cdr_setdestchan(struct ast_cdr *cdr, char *chann)
471 {
472         char *chan; 
473         while (cdr) {
474                 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
475                 if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
476                         ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
477                 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
478                         strncpy(cdr->dstchannel, chann, sizeof(cdr->dstchannel) - 1);
479                 cdr = cdr->next;
480         }
481 }
482
483 void ast_cdr_setapp(struct ast_cdr *cdr, char *app, char *data)
484 {
485         char *chan; 
486         while (cdr) {
487                 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
488                         chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
489                         if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
490                                 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
491                         if (!app)
492                                 app = "";
493                         strncpy(cdr->lastapp, app, sizeof(cdr->lastapp) - 1);
494                         if (!data)
495                                 data = "";
496                         strncpy(cdr->lastdata, data, sizeof(cdr->lastdata) - 1);
497                 }
498                 cdr = cdr->next;
499         }
500 }
501
502 int ast_cdr_setcid(struct ast_cdr *cdr, struct ast_channel *c)
503 {
504         char tmp[AST_MAX_EXTENSION] = "";
505         char *num;
506         while (cdr) {
507                 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
508                         /* Grab source from ANI or normal Caller*ID */
509                         if (c->cid.cid_ani)
510                                 num = c->cid.cid_ani;
511                         else
512                                 num = c->cid.cid_num;
513                         
514                         if (c->cid.cid_name && num)
515                                 snprintf(tmp, sizeof(tmp), "\"%s\" <%s>", c->cid.cid_name, num);
516                         else if (c->cid.cid_name)
517                                 strncpy(tmp, c->cid.cid_name, sizeof(tmp) - 1);
518                         else if (num)
519                                 strncpy(tmp, num, sizeof(tmp) - 1);
520                         else
521                                 strcpy(tmp, "");
522                         strncpy(cdr->clid, tmp, sizeof(cdr->clid) - 1);
523                         if (num)
524                                 strncpy(cdr->src, num, sizeof(cdr->src) - 1);
525                         else
526                                 strcpy(cdr->src, "");
527                 }
528                 cdr = cdr->next;
529         }
530         return 0;
531 }
532
533
534 int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c)
535 {
536         char *chan;
537         char *num;
538         char tmp[AST_MAX_EXTENSION] = "";
539         while (cdr) {
540                 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
541                         chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
542                         if (!ast_strlen_zero(cdr->channel)) 
543                                 ast_log(LOG_WARNING, "CDR already initialized on '%s'\n", chan); 
544                         strncpy(cdr->channel, c->name, sizeof(cdr->channel) - 1);
545                         /* Grab source from ANI or normal Caller*ID */
546                         if (c->cid.cid_ani)
547                                 num = c->cid.cid_ani;
548                         else
549                                 num = c->cid.cid_num;
550                         
551                         if (c->cid.cid_name && num)
552                                 snprintf(tmp, sizeof(tmp), "\"%s\" <%s>", c->cid.cid_name, num);
553                         else if (c->cid.cid_name)
554                                 strncpy(tmp, c->cid.cid_name, sizeof(tmp) - 1);
555                         else if (num)
556                                 strncpy(tmp, num, sizeof(tmp) - 1);
557                         else
558                                 strcpy(tmp, "");
559                         strncpy(cdr->clid, tmp, sizeof(cdr->clid) - 1);
560                         if (num)
561                                 strncpy(cdr->src, num, sizeof(cdr->src) - 1);
562                         else
563                                 strcpy(cdr->src, "");
564
565                         if (c->_state == AST_STATE_UP)
566                                 cdr->disposition = AST_CDR_ANSWERED;
567                         else
568                                 cdr->disposition = AST_CDR_NOANSWER;
569                         if (c->amaflags)
570                                 cdr->amaflags = c->amaflags;
571                         else
572                                 cdr->amaflags = ast_default_amaflags;
573                         strncpy(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode) - 1);
574                         /* Destination information */
575                         strncpy(cdr->dst, c->exten, sizeof(cdr->dst) - 1);
576                         strncpy(cdr->dcontext, c->context, sizeof(cdr->dcontext) - 1);
577                         /* Unique call identifier */
578                         strncpy(cdr->uniqueid, c->uniqueid, sizeof(cdr->uniqueid) - 1);
579                 }
580                 cdr = cdr->next;
581         }
582         return 0;
583 }
584
585 void ast_cdr_end(struct ast_cdr *cdr)
586 {
587         char *chan;
588         while (cdr) {
589                 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
590                 if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
591                         ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
592                 if (!cdr->start.tv_sec && !cdr->start.tv_usec)
593                         ast_log(LOG_WARNING, "CDR on channel '%s' has not started\n", chan);
594                 if (!cdr->end.tv_sec && !cdr->end.tv_usec) 
595                         gettimeofday(&cdr->end, NULL);
596                 cdr = cdr->next;
597         }
598 }
599
600 char *ast_cdr_disp2str(int disposition)
601 {
602         switch (disposition) {
603         case AST_CDR_NOANSWER:
604                 return "NO ANSWER";
605         case AST_CDR_FAILED:
606                 return "FAILED";                
607         case AST_CDR_BUSY:
608                 return "BUSY";          
609         case AST_CDR_ANSWERED:
610                 return "ANSWERED";
611         default:
612                 return "UNKNOWN";
613         }
614 }
615
616 char *ast_cdr_flags2str(int flag)
617 {
618         switch(flag) {
619         case AST_CDR_OMIT:
620                 return "OMIT";
621         case AST_CDR_BILLING:
622                 return "BILLING";
623         case AST_CDR_DOCUMENTATION:
624                 return "DOCUMENTATION";
625         }
626         return "Unknown";
627 }
628
629 int ast_cdr_setaccount(struct ast_channel *chan, const char *account)
630 {
631         struct ast_cdr *cdr = chan->cdr;
632
633         strncpy(chan->accountcode, account, sizeof(chan->accountcode) - 1);
634         while (cdr) {
635                 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
636                         strncpy(cdr->accountcode, chan->accountcode, sizeof(cdr->accountcode) - 1);
637                 cdr = cdr->next;
638         }
639         return 0;
640 }
641
642 int ast_cdr_setamaflags(struct ast_channel *chan, const char *flag)
643 {
644         struct ast_cdr *cdr = chan->cdr;
645         int newflag;
646
647         newflag = ast_cdr_amaflags2int(flag);
648         if (newflag) {
649                 cdr->amaflags = newflag;
650         }
651         return 0;
652 }
653
654 int ast_cdr_setuserfield(struct ast_channel *chan, const char *userfield)
655 {
656         struct ast_cdr *cdr = chan->cdr;
657
658         while (cdr) {
659                 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) 
660                         strncpy(cdr->userfield, userfield, sizeof(cdr->userfield) - 1);
661                 cdr = cdr->next;
662         }
663         return 0;
664 }
665
666 int ast_cdr_appenduserfield(struct ast_channel *chan, const char *userfield)
667 {
668         struct ast_cdr *cdr = chan->cdr;
669
670         while (cdr)
671         {
672
673                 int len = strlen(cdr->userfield);
674                 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
675                         strncpy(cdr->userfield+len, userfield, sizeof(cdr->userfield) - len - 1);
676                 cdr = cdr->next;
677         }
678         return 0;
679 }
680
681 int ast_cdr_update(struct ast_channel *c)
682 {
683         struct ast_cdr *cdr = c->cdr;
684         char *num;
685         char tmp[AST_MAX_EXTENSION] = "";
686         /* Grab source from ANI or normal Caller*ID */
687         while (cdr) {
688                 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
689                         /* Grab source from ANI or normal Caller*ID */
690                         if (c->cid.cid_ani)
691                                 num = c->cid.cid_ani;
692                         else
693                                 num = c->cid.cid_num;
694                         
695                         if (c->cid.cid_name && num)
696                                 snprintf(tmp, sizeof(tmp), "\"%s\" <%s>", c->cid.cid_name, num);
697                         else if (c->cid.cid_name)
698                                 strncpy(tmp, c->cid.cid_name, sizeof(tmp) - 1);
699                         else if (num)
700                                 strncpy(tmp, num, sizeof(tmp) - 1);
701                         else
702                                 strcpy(tmp, "");
703                         strncpy(cdr->clid, tmp, sizeof(cdr->clid) - 1);
704                         if (num)
705                                 strncpy(cdr->src, num, sizeof(cdr->src) - 1);
706                         else
707                                 strcpy(cdr->src, "");
708
709                         /* Copy account code et-al */   
710                         strncpy(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode) - 1);
711                         /* Destination information */
712                         if (ast_strlen_zero(c->macroexten))
713                                 strncpy(cdr->dst, c->exten, sizeof(cdr->dst) - 1);
714                         else
715                                 strncpy(cdr->dst, c->macroexten, sizeof(cdr->dst) - 1);
716                         if (ast_strlen_zero(c->macrocontext))
717                                 strncpy(cdr->dcontext, c->context, sizeof(cdr->dcontext) - 1);
718                         else
719                                 strncpy(cdr->dcontext, c->macrocontext, sizeof(cdr->dcontext) - 1);
720                 }
721                 cdr = cdr->next;
722         }
723
724         return 0;
725 }
726
727 int ast_cdr_amaflags2int(const char *flag)
728 {
729         if (!strcasecmp(flag, "default"))
730                 return 0;
731         if (!strcasecmp(flag, "omit"))
732                 return AST_CDR_OMIT;
733         if (!strcasecmp(flag, "billing"))
734                 return AST_CDR_BILLING;
735         if (!strcasecmp(flag, "documentation"))
736                 return AST_CDR_DOCUMENTATION;
737         return -1;
738 }
739
740 void ast_cdr_post(struct ast_cdr *cdr)
741 {
742         char *chan;
743         struct ast_cdr_beitem *i;
744         while (cdr) {
745                 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
746                 if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
747                         ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
748                 if (!cdr->end.tv_sec && !cdr->end.tv_usec)
749                         ast_log(LOG_WARNING, "CDR on channel '%s' lacks end\n", chan);
750                 if (!cdr->start.tv_sec && !cdr->start.tv_usec)
751                         ast_log(LOG_WARNING, "CDR on channel '%s' lacks start\n", chan);
752                 cdr->duration = cdr->end.tv_sec - cdr->start.tv_sec + (cdr->end.tv_usec - cdr->start.tv_usec) / 1000000;
753                 if (cdr->answer.tv_sec || cdr->answer.tv_usec) {
754                         cdr->billsec = cdr->end.tv_sec - cdr->answer.tv_sec + (cdr->end.tv_usec - cdr->answer.tv_usec) / 1000000;
755                 } else
756                         cdr->billsec = 0;
757                 ast_set_flag(cdr, AST_CDR_FLAG_POSTED);
758                 ast_mutex_lock(&cdrlock);
759                 i = bes;
760                 while(i) {
761                         i->be(cdr);
762                         i = i->next;
763                 }
764                 ast_mutex_unlock(&cdrlock);
765                 cdr = cdr->next;
766         }
767 }
768
769 void ast_cdr_reset(struct ast_cdr *cdr, int flags)
770 {
771         struct ast_flags tmp = {flags};
772         while (cdr) {
773                 /* Post if requested */
774                 if (ast_test_flag(&tmp, AST_CDR_FLAG_LOCKED) || !ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
775                         if (ast_test_flag(&tmp, AST_CDR_FLAG_POSTED)) {
776                                 ast_cdr_end(cdr);
777                                 ast_cdr_post(cdr);
778                         }
779
780                         /* clear variables */
781                         if (! ast_test_flag(&tmp, AST_CDR_FLAG_KEEP_VARS)) {
782                                 ast_cdr_free_vars(cdr, 0);
783                         }
784
785                         /* Reset to initial state */
786                         ast_clear_flag(cdr, AST_FLAGS_ALL);     
787                         memset(&cdr->start, 0, sizeof(cdr->start));
788                         memset(&cdr->end, 0, sizeof(cdr->end));
789                         memset(&cdr->answer, 0, sizeof(cdr->answer));
790                         cdr->billsec = 0;
791                         cdr->duration = 0;
792                         ast_cdr_start(cdr);
793                         cdr->disposition = AST_CDR_NOANSWER;
794                 }
795                         
796                 cdr = cdr->next;
797         }
798         
799 }
800
801 struct ast_cdr *ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr) 
802 {
803         struct ast_cdr *ret;
804         if (cdr) {
805                 ret = cdr;
806                 while(cdr->next)
807                         cdr = cdr->next;
808                 cdr->next = newcdr;
809         } else {
810                 ret = newcdr;
811         }
812         return ret;
813 }