mirror of
https://github.com/asterisk/asterisk.git
synced 2026-06-25 18:06:40 -07:00
b3f6728a28
Several issues are resolved: * Internally, floats were used for timestamp values but this could result in wrapping so they've been changed to doubles. * Historically, the default CEL eventtime format is `<seconds>.<microseconds>` with `<microseconds>` always being 6 digits. This should have continued to be the case but res_cdrel_custom wasn't checking the `dateformat` setting in cel.conf and was defaulting to `%F %T`. res_cdrel_custom now gets the default date format from cel.conf, which will be whatever the `dateformat` parameter is set to or `<seconds>.<microseconds>` if not set. * The timeval field formatter for both CDR and CEL wasn't handling custom strftime format strings correctly. This is now fixed so you should be able to specifiy custom strftime format strings for the CEL `eventtime` and CDR `start`, `answer` and `end` fields. For example: `eventtime(%FT%T%z)`. Resolves: #1844 Resolves: #1845
365 lines
12 KiB
C
365 lines
12 KiB
C
/*
|
|
* Asterisk -- An open source telephony toolkit.
|
|
*
|
|
* Copyright (C) 2026, Sangoma Technologies Corporation
|
|
*
|
|
* George Joseph <gjoseph@sangoma.com>
|
|
*
|
|
* 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 <gjoseph@sangoma.com>
|
|
*
|
|
* \brief Private header for res_cdrel_custom.
|
|
*
|
|
*/
|
|
|
|
#ifndef _CDREL_H
|
|
#define _CDREL_H
|
|
|
|
#include <sqlite3.h>
|
|
|
|
#include "asterisk.h"
|
|
#include "asterisk/cdr.h"
|
|
#include "asterisk/cel.h"
|
|
#include "asterisk/event.h"
|
|
#include "asterisk/lock.h"
|
|
#include "asterisk/strings.h"
|
|
#include "asterisk/vector.h"
|
|
#include "asterisk/res_cdrel_custom.h"
|
|
|
|
extern const char *cdrel_record_type_map[];
|
|
#define RECORD_TYPE_STR(_rt) (cdrel_record_type_map[_rt])
|
|
|
|
extern const char *cdrel_module_type_map[];
|
|
#define MODULE_TYPE_STR(_mt) (cdrel_module_type_map[_mt])
|
|
|
|
enum cdrel_text_format_type {
|
|
cdrel_format_dsv = 0,
|
|
cdrel_format_json,
|
|
cdrel_format_sql,
|
|
cdrel_format_type_end,
|
|
};
|
|
|
|
enum cdrel_config_type {
|
|
cdrel_config_legacy = 0,
|
|
cdrel_config_advanced,
|
|
cdrel_config_type_end,
|
|
};
|
|
|
|
enum cdrel_quoting_method {
|
|
cdrel_quoting_method_none = 0,
|
|
cdrel_quoting_method_all,
|
|
cdrel_quoting_method_minimal,
|
|
cdrel_quoting_method_non_numeric,
|
|
cdrel_quoting_method_end,
|
|
};
|
|
|
|
/*
|
|
* ORDER IS IMPORTANT!
|
|
* The string output data types need to be first.
|
|
*/
|
|
enum cdrel_data_type {
|
|
cdrel_type_string = 0,
|
|
cdrel_type_timeval,
|
|
cdrel_type_literal,
|
|
cdrel_type_amaflags,
|
|
cdrel_type_disposition,
|
|
cdrel_type_uservar,
|
|
cdrel_type_event_type,
|
|
cdrel_type_event_enum,
|
|
cdrel_type_cel_timefmt,
|
|
cdrel_data_type_strings_end,
|
|
cdrel_type_int32,
|
|
cdrel_type_uint32,
|
|
cdrel_type_int64,
|
|
cdrel_type_uint64,
|
|
cdrel_type_double,
|
|
cdrel_data_type_end
|
|
};
|
|
|
|
extern const char *cdrel_data_type_map[];
|
|
#define DATA_TYPE_STR(_dt) (_dt < cdrel_data_type_end ? cdrel_data_type_map[_dt] : NULL)
|
|
enum cdrel_data_type cdrel_data_type_from_str(const char *str);
|
|
|
|
#define CDREL_FIELD_FLAG_QUOTE (0)
|
|
#define CDREL_FIELD_FLAG_NOQUOTE (1)
|
|
#define CDREL_FIELD_FLAG_TYPE_FORCED (2)
|
|
#define CDREL_FIELD_FLAG_USERVAR (3)
|
|
#define CDREL_FIELD_FLAG_LITERAL (4)
|
|
#define CDREL_FIELD_FLAG_FORMAT_SPEC (5)
|
|
#define CDREL_FIELD_FLAG_LAST (6)
|
|
|
|
enum cdrel_field_flags {
|
|
cdrel_flag_quote = (1 << CDREL_FIELD_FLAG_QUOTE),
|
|
cdrel_flag_noquote = (1 << CDREL_FIELD_FLAG_NOQUOTE),
|
|
cdrel_flag_type_forced = (1 << CDREL_FIELD_FLAG_TYPE_FORCED),
|
|
cdrel_flag_uservar = (1 << CDREL_FIELD_FLAG_USERVAR),
|
|
cdrel_flag_literal = (1 << CDREL_FIELD_FLAG_LITERAL),
|
|
cdrel_flag_format_spec = (1 << CDREL_FIELD_FLAG_FORMAT_SPEC),
|
|
cdrel_field_flags_end = (1 << CDREL_FIELD_FLAG_LAST),
|
|
};
|
|
|
|
/*
|
|
* CEL has a few synthetic fields that aren't defined
|
|
* in event.h so we'll define them ourselves after the
|
|
* last official id.
|
|
*/
|
|
#define AST_EVENT_IE_CEL_LITERAL (AST_EVENT_IE_TOTAL + 1)
|
|
#define AST_EVENT_IE_CEL_EVENT_ENUM (AST_EVENT_IE_TOTAL + 2)
|
|
#define LAST_CEL_ID AST_EVENT_IE_CEL_EVENT_ENUM
|
|
|
|
/*!
|
|
* While CEL event fields are published as a generic AST_EVENT with
|
|
* a field id assigned to each field, CDRs are published as a fixed
|
|
* ast_cdr structure. To make it easier to share lower level code,
|
|
* we assign pseudo-field-ids to each CDR field that are really the offset
|
|
* of the field in the structure. This allows us to generically get
|
|
* any field using its id just like we do for CEL.
|
|
*
|
|
* To avoid conflicts with the existing CEL field ids, we'll start these
|
|
* after the last one.
|
|
*/
|
|
#define CDR_OFFSET_SHIFT (LAST_CEL_ID + 1)
|
|
#define CDR_OFFSETOF(_field) (offsetof(struct ast_cdr, _field) + CDR_OFFSET_SHIFT)
|
|
#define CDR_FIELD(__cdr, __offset) (((void *)__cdr) + __offset - CDR_OFFSET_SHIFT)
|
|
|
|
enum cdr_field_id {
|
|
cdr_field_literal = -1,
|
|
cdr_field_clid = CDR_OFFSETOF(clid),
|
|
cdr_field_src = CDR_OFFSETOF(src),
|
|
cdr_field_dst = CDR_OFFSETOF(dst),
|
|
cdr_field_dcontext = CDR_OFFSETOF(dcontext),
|
|
cdr_field_channel = CDR_OFFSETOF(channel),
|
|
cdr_field_dstchannel = CDR_OFFSETOF(dstchannel),
|
|
cdr_field_lastapp = CDR_OFFSETOF(lastapp),
|
|
cdr_field_lastdata = CDR_OFFSETOF(lastdata),
|
|
cdr_field_start = CDR_OFFSETOF(start),
|
|
cdr_field_answer = CDR_OFFSETOF(answer),
|
|
cdr_field_end = CDR_OFFSETOF(end),
|
|
cdr_field_duration = CDR_OFFSETOF(duration),
|
|
cdr_field_billsec = CDR_OFFSETOF(billsec),
|
|
cdr_field_disposition = CDR_OFFSETOF(disposition),
|
|
cdr_field_amaflags = CDR_OFFSETOF(amaflags),
|
|
cdr_field_accountcode = CDR_OFFSETOF(accountcode),
|
|
cdr_field_peeraccount = CDR_OFFSETOF(peeraccount),
|
|
cdr_field_flags = CDR_OFFSETOF(flags),
|
|
cdr_field_uniqueid = CDR_OFFSETOF(uniqueid),
|
|
cdr_field_linkedid = CDR_OFFSETOF(linkedid),
|
|
cdr_field_tenantid = CDR_OFFSETOF(tenantid),
|
|
cdr_field_peertenantid = CDR_OFFSETOF(peertenantid),
|
|
cdr_field_userfield = CDR_OFFSETOF(userfield),
|
|
cdr_field_sequence = CDR_OFFSETOF(sequence),
|
|
cdr_field_varshead = CDR_OFFSETOF(varshead),
|
|
};
|
|
|
|
|
|
struct cdrel_field;
|
|
|
|
/*!
|
|
* \internal
|
|
* \brief A generic value wrapper structure.
|
|
*/
|
|
struct cdrel_value {
|
|
char *field_name;
|
|
enum cdrel_data_type data_type;
|
|
int mallocd;
|
|
union {
|
|
char *string;
|
|
int32_t int32;
|
|
uint32_t uint32;
|
|
int64_t int64;
|
|
uint64_t uint64;
|
|
struct timeval tv;
|
|
double doubler;
|
|
} values;
|
|
};
|
|
|
|
/*!
|
|
* \internal
|
|
* \brief A vector of cdrel_values.
|
|
*/
|
|
AST_VECTOR(cdrel_values, struct cdrel_value *);
|
|
|
|
/*!
|
|
* \internal
|
|
* \brief Getter callbacks.
|
|
*
|
|
* \param record An ast_cdr or ast_event structure.
|
|
* \param config Config object.
|
|
* \param field Field object.
|
|
* \param value A pointer to a cdrel_value structure to populate.
|
|
* \retval 0 on success.
|
|
* \retval -1 on failure.
|
|
*/
|
|
typedef int (*cdrel_field_getter)(void *record, struct cdrel_config *config,
|
|
struct cdrel_field *field, struct cdrel_value *value);
|
|
/*!
|
|
* \internal
|
|
* \brief The table of getter callbacks. Populated by getters_cdr.c and getters_cel.c.
|
|
*
|
|
* Defined in res_cdrel_custom.c
|
|
*/
|
|
extern cdrel_field_getter cdrel_field_getters[cdrel_record_type_end][cdrel_data_type_end];
|
|
|
|
/*!
|
|
* \internal
|
|
* \brief Data type formatters.
|
|
*
|
|
* \param config Config object.
|
|
* \param field Field object.
|
|
* \param input_value A pointer to a cdrel_value structure with the data to format.
|
|
* \param output_value A pointer to a cdrel_value structure to receive the formatted data.
|
|
* \retval 0 on success.
|
|
* \retval -1 on failure.
|
|
*/
|
|
typedef int (*cdrel_field_formatter)(struct cdrel_config *config,
|
|
struct cdrel_field *field, struct cdrel_value *input_value, struct cdrel_value *output_value);
|
|
/*!
|
|
* \internal
|
|
* \brief The table of formatter callbacks. Populated by formatters.c.
|
|
*
|
|
* Defined in res_cdrel_custom.c
|
|
*/
|
|
extern cdrel_field_formatter cdrel_field_formatters[cdrel_data_type_end];
|
|
|
|
/*!
|
|
* \internal
|
|
* \brief Backend writers.
|
|
*
|
|
* \param config Config object.
|
|
* \param values A vector of cdrel_values to write.
|
|
* \retval 0 on success.
|
|
* \retval -1 on failure.
|
|
*/
|
|
typedef int (*cdrel_backend_writer)(struct cdrel_config *config, struct cdrel_values *values);
|
|
/*!
|
|
* \internal
|
|
* \brief The table of writer callbacks. Populated by writers.c.
|
|
*
|
|
* Defined in res_cdrel_custom.c
|
|
*/
|
|
extern cdrel_backend_writer cdrel_backend_writers[cdrel_format_type_end];
|
|
|
|
/*!
|
|
* \internal
|
|
* \brief Dummy channel allocators.
|
|
*
|
|
* Legacy configurations use dialplan functions so they need a dummy channel
|
|
* to operate on. CDR and CEL each have their own allocator.
|
|
*
|
|
* \param config Config object.
|
|
* \param record An ast_cdr or ast_event structure.
|
|
* \return an ast_channel or NULL on failure.
|
|
*/
|
|
typedef struct ast_channel * (*cdrel_dummy_channel_alloc)(struct cdrel_config *config, void *record);
|
|
extern cdrel_dummy_channel_alloc cdrel_dummy_channel_allocators[cdrel_format_type_end];
|
|
|
|
|
|
/*! Represents a field definition */
|
|
struct cdrel_field {
|
|
enum cdrel_record_type record_type; /*!< CDR or CEL */
|
|
int field_id; /*!< May be an AST_EVENT_IE_CEL or a cdr_field_id */
|
|
char *name; /*!< The name of the field */
|
|
enum cdrel_data_type input_data_type; /*!< The data type of the field in the source record */
|
|
enum cdrel_data_type output_data_type;
|
|
struct ast_flags flags; /*!< Flags used during config parsing */
|
|
char data[1]; /*!< Could be literal data, a user variable name, etc */
|
|
};
|
|
|
|
/*! Represents an output definition from a config file */
|
|
struct cdrel_config {
|
|
enum cdrel_record_type record_type; /*!< CDR or CEL */
|
|
AST_DECLARE_STRING_FIELDS(
|
|
AST_STRING_FIELD(config_filename); /*!< Input configuration filename */
|
|
AST_STRING_FIELD(output_filename); /*!< Output text file or database */
|
|
AST_STRING_FIELD(template); /*!< Input template */
|
|
AST_STRING_FIELD(db_columns); /*!< List of columns for database backends */
|
|
AST_STRING_FIELD(db_table); /*!< Table name for database backends */
|
|
);
|
|
sqlite3 *db; /*!< sqlite3 database handle */
|
|
sqlite3_stmt *insert; /*!< sqlite3 prepared statement for insert */
|
|
int busy_timeout; /*!< sqlite3 query timeout value */
|
|
cdrel_dummy_channel_alloc dummy_channel_alloc; /*!< Legacy config types need a dummy channel */
|
|
enum cdrel_backend_type backend_type; /*!< Text file or database */
|
|
enum cdrel_config_type config_type; /*!< Legacy or advanced */
|
|
enum cdrel_text_format_type format_type; /*!< For text files, CSV or JSON */
|
|
enum cdrel_quoting_method quoting_method; /*!< When to quote */
|
|
char separator[2]; /*!< For text files, the field separator */
|
|
char quote[2]; /*!< For text files, the quote character */
|
|
char quote_escape[2]; /*!< For text files, character to use to escape embedded quotes */
|
|
AST_VECTOR(, struct cdrel_field *) fields; /*!< Vector of fields for this config */
|
|
ast_mutex_t lock; /*!< Lock that serializes filesystem writes */
|
|
};
|
|
|
|
/*!
|
|
* \internal
|
|
* \brief Get a cdrel_field structure by record type and field name.
|
|
*
|
|
* \param record_type The cdrel_record_type to search.
|
|
* \param name The field name to search for.
|
|
* \returns A pointer to a constant cdrel_field structure or NULL if not found.
|
|
* This pointer must never be freed.
|
|
*/
|
|
const struct cdrel_field *get_registered_field_by_name(enum cdrel_record_type record_type,
|
|
const char *name);
|
|
|
|
/*!
|
|
* \internal
|
|
* \brief Write a record to a text file
|
|
*
|
|
* \param config The configuration object.
|
|
* \param record The data to write.
|
|
* \retval 0 on success
|
|
* \retval -1 on failure
|
|
*/
|
|
int write_record_to_file(struct cdrel_config *config, struct ast_str *record);
|
|
|
|
/*!
|
|
* \internal
|
|
* \brief Write a record to a database
|
|
*
|
|
* \param config The configuration object.
|
|
* \param values The values to write.
|
|
* \retval 0 on success
|
|
* \retval -1 on failure
|
|
*/
|
|
int write_record_to_database(struct cdrel_config *config, struct cdrel_values *values);
|
|
|
|
/*!
|
|
* \internal
|
|
* \brief Return the basename of a path
|
|
*
|
|
* \param path
|
|
* \returns Pointer to last '/' or path if no '/' was found.
|
|
*/
|
|
const char *cdrel_basename(const char *path);
|
|
|
|
/*!
|
|
* \internal
|
|
* \brief Get a string representing a field's flags
|
|
*
|
|
* \param flags An ast_flags structure
|
|
* \param str Pointer to ast_str* buffer
|
|
* \returns A string of field flag names
|
|
*/
|
|
const char *cdrel_get_field_flags(struct ast_flags *flags, struct ast_str **str);
|
|
|
|
|
|
int load_cdr(void);
|
|
int load_cel(void);
|
|
int load_formatters(void);
|
|
int load_writers(void);
|
|
|
|
#endif /* _CDREL_H */
|