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