/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 2026, Sangoma Technologies Corporation * * George Joseph * * See http://www.asterisk.org for more information about * the Asterisk project. Please do not directly contact * any of the maintainers of this project for assistance; * the project provides a web site, mailing lists and IRC * channels for your use. * * This program is free software, distributed under the terms of * the GNU General Public License Version 2. See the LICENSE file * at the top of the source tree. */ /*! * \file * \author George Joseph * * \brief Formatters * */ #include "cdrel.h" #include "asterisk/json.h" #include "asterisk/cdr.h" #include "asterisk/cel.h" static char *quote_escaper(const char *value, char quote, char quote_escape, char *qvalue) { char *ptr = qvalue; const char *dataptr = value; if (!qvalue) { return NULL; } while(*dataptr != '\0') { if (*dataptr == quote) { *ptr++ = quote_escape; } *ptr++ = *dataptr++; } *ptr='\0'; return qvalue; } static int format_string(struct cdrel_config *config, struct cdrel_field *field, struct cdrel_value *input_value, struct cdrel_value *output_value) { int quotes_count = 0; int needs_quoting = ast_test_flag(&field->flags, cdrel_flag_quote); int ix = 0; int input_len = strlen(input_value->values.string ?: ""); int res = 0; char *qvalue = NULL; char *evalue = (char *)input_value->values.string; output_value->data_type = cdrel_type_string; output_value->field_name = input_value->field_name; if (input_len == 0) { output_value->values.string = ast_strdup(needs_quoting ? "\"\"" : ""); return 0; } for (ix = 0; ix < input_len; ix++) { char c = input_value->values.string[ix]; if (c == config->quote[0]) { quotes_count++; needs_quoting = 1; } else if (c == config->separator[0] || c == '\r' || c == '\n') { needs_quoting = 1; } } ast_debug(5, "%s: %s=%s %s", cdrel_basename(config->output_filename), input_value->field_name, input_value->values.string, ast_str_tmp(128, cdrel_get_field_flags(&field->flags, &STR_TMP))); if (!needs_quoting) { output_value->values.string = ast_strdup(input_value->values.string); return output_value->values.string == NULL ? -1 : 0; } /* For every quote_count, we need an extra byte for the quote escape character. */ qvalue = ast_alloca(input_len + quotes_count + 1); if (quotes_count) { evalue = quote_escaper(input_value->values.string, config->quote[0], config->quote_escape[0], qvalue); } res = ast_asprintf(&output_value->values.string, "%s%s%s", config->quote, evalue, config->quote); return res < 0 ? -1 : 0; } #define DEFINE_FORMATTER(_name, _field, _type, _fmt) \ static int format_ ## _name (struct cdrel_config *config, \ struct cdrel_field *field, struct cdrel_value *input_value, struct cdrel_value *output_value) \ { \ int res = 0; \ char *quote = ""; \ if (input_value->data_type != output_value->data_type) { \ /* Forward to other formatter */ \ return cdrel_field_formatters[output_value->data_type](config, field, input_value, output_value); \ } \ output_value->field_name = input_value->field_name; \ if ((config->quoting_method == cdrel_quoting_method_all || ast_test_flag(&field->flags, cdrel_flag_quote)) \ && !ast_test_flag(&field->flags, cdrel_flag_noquote)) { \ quote = config->quote; \ } \ res = ast_asprintf(&output_value->values.string, "%s" _fmt "%s", quote, input_value->values._field, quote); \ output_value->data_type = cdrel_type_string; \ return res < 0 ? -1 : 0; \ } DEFINE_FORMATTER(uint32, uint32, uint32_t, "%u") DEFINE_FORMATTER(int32, int32, int32_t, "%d") DEFINE_FORMATTER(uint64, uint64, uint64_t, "%lu") DEFINE_FORMATTER(int64, int64, int64_t, "%ld") DEFINE_FORMATTER(double, doubler, double, "%.6f") static int format_timeval(struct cdrel_config *config, struct cdrel_field *field, struct cdrel_value *input_value, struct cdrel_value *output_value) { struct ast_tm tm; char tempbuf[64] = { 0, }; int res = 0; const char *format = "%Y-%m-%d %T"; output_value->field_name = input_value->field_name; if (field->output_data_type == cdrel_type_int64) { output_value->data_type = cdrel_type_int64; output_value->values.int64 = input_value->values.tv.tv_sec; return format_int64(config, field, output_value, output_value); } else if (field->output_data_type == cdrel_type_double) { output_value->data_type = cdrel_type_double; output_value->values.doubler = ((double)input_value->values.tv.tv_sec) + ((double)input_value->values.tv.tv_usec) / 1000000.0; return format_double(config, field, output_value, output_value); } else if (field->output_data_type == cdrel_type_cel_timefmt) { res = ast_cel_format_eventtime(input_value->values.tv, tempbuf, 64); if (res != 0) { return res; } input_value->values.string = tempbuf; input_value->data_type = cdrel_type_string; output_value->data_type = cdrel_type_string; return format_string(config, field, input_value, output_value); } else if (!ast_strlen_zero(field->data)) { format = field->data; } if (input_value->values.tv.tv_sec > 0) { ast_localtime(&input_value->values.tv, &tm, NULL); ast_strftime(tempbuf, sizeof(tempbuf), format, &tm); } input_value->values.string = tempbuf; input_value->data_type = cdrel_type_string; output_value->data_type = cdrel_type_string; res = format_string(config, field, input_value, output_value); return res; } static int format_amaflags(struct cdrel_config *config, struct cdrel_field *field, struct cdrel_value *input_value, struct cdrel_value *output_value) { int res = 0; input_value->values.string = (char *)ast_channel_amaflags2string(input_value->values.int64); input_value->data_type = cdrel_type_string; output_value->data_type = cdrel_type_string; res = format_string(config, field, input_value, output_value); return res; } static int format_disposition(struct cdrel_config *config, struct cdrel_field *field, struct cdrel_value *input_value, struct cdrel_value *output_value) { int res = 0; input_value->values.string = (char *)ast_cdr_disp2str(input_value->values.int64); input_value->data_type = cdrel_type_string; output_value->data_type = cdrel_type_string; res = format_string(config, field, input_value, output_value); return res; } int load_formatters(void) { ast_debug(1, "Loading Formatters\n"); cdrel_field_formatters[cdrel_type_string] = format_string; cdrel_field_formatters[cdrel_type_int32] = format_int32; cdrel_field_formatters[cdrel_type_uint32] = format_uint32; cdrel_field_formatters[cdrel_type_int64] = format_int64; cdrel_field_formatters[cdrel_type_uint64] = format_uint64; cdrel_field_formatters[cdrel_type_timeval] = format_timeval; cdrel_field_formatters[cdrel_type_double] = format_double; cdrel_field_formatters[cdrel_type_amaflags] = format_amaflags; cdrel_field_formatters[cdrel_type_disposition] = format_disposition; return 0; }