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