mirror of https://github.com/asterisk/asterisk
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
365 lines
12 KiB
365 lines
12 KiB
/*
|
|
* 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 */
|