dect
/
asterisk
Archived
13
0
Fork 0
This repository has been archived on 2022-02-17. You can view files and clone it, but cannot push or open issues or pull requests.
asterisk/main/aoc.c

1608 lines
44 KiB
C

/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2010, Digium, Inc.
*
* David Vossel <dvossel@digium.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
* \brief generic AOC payload generation encoding and decoding
*
* \author David Vossel <dvossel@digium.com>
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
#include "asterisk/aoc.h"
#include "asterisk/utils.h"
#include "asterisk/strings.h"
#include "asterisk/_private.h"
#include "asterisk/cli.h"
#include "asterisk/manager.h"
/* Encoded Payload Flags */
#define AST_AOC_ENCODED_TYPE_REQUEST (0 << 0)
#define AST_AOC_ENCODED_TYPE_D (1 << 0)
#define AST_AOC_ENCODED_TYPE_E (2 << 0)
#define AST_AOC_ENCODED_TYPE_S (3 << 0)
#define AST_AOC_ENCODED_REQUEST_S (1 << 2)
#define AST_AOC_ENCODED_REQUEST_D (1 << 3)
#define AST_AOC_ENCODED_REQUEST_E (1 << 4)
#define AST_AOC_ENCODED_CHARGE_NA (0 << 5)
#define AST_AOC_ENCODED_CHARGE_FREE (1 << 5)
#define AST_AOC_ENCODED_CHARGE_CURRENCY (2 << 5)
#define AST_AOC_ENCODED_CHARGE_UNIT (3 << 5)
#define AST_AOC_ENCODED_CHARGE_SUBTOTAL (1 << 7)
#define AST_AOC_ENCODED_CHARGE_TOTAL (0 << 7)
#define AST_AOC_ENCODE_VERSION 1
static char aoc_debug_enabled = 0;
static void aoc_display_decoded_debug(const struct ast_aoc_decoded *decoded, int decoding, struct ast_channel *chan);
static int aoc_s_add_entry(struct ast_aoc_decoded *decoded, struct ast_aoc_s_entry *entry);
/* AOC Payload Header. Holds all the encoded AOC data to pass on the wire */
struct ast_aoc_encoded {
uint8_t version;
uint8_t flags;
uint16_t datalen;
unsigned char data[0];
};
/* Decoded AOC data */
struct ast_aoc_decoded {
enum ast_aoc_type msg_type;
enum ast_aoc_charge_type charge_type;
enum ast_aoc_request request_flag;
enum ast_aoc_total_type total_type;
/* currency information */
enum ast_aoc_currency_multiplier multiplier;
unsigned int currency_amount;
char currency_name[AOC_CURRENCY_NAME_SIZE];
/* unit information */
int unit_count;
struct ast_aoc_unit_entry unit_list[32];
/* Billing Id */
enum ast_aoc_billing_id billing_id;
/* Charging Association information */
struct ast_aoc_charging_association charging_association;
/* AOC-S charge information */
int aoc_s_count;
struct ast_aoc_s_entry aoc_s_entries[10];
/* Is this an AOC Termination Request */
char termination_request;
};
/*! \brief AOC Payload Information Elements */
enum AOC_IE {
AOC_IE_CURRENCY = 1,
AOC_IE_UNIT = 2,
AOC_IE_BILLING = 3,
AOC_IE_CHARGING_ASSOCIATION = 4,
AOC_IE_RATE = 5,
AOC_IE_TERMINATION_REQUEST = 6,
};
/*! \brief AOC IE payload header */
struct aoc_pl_ie_hdr {
uint8_t ie_id;
uint8_t datalen;
char data[0];
} __attribute__((packed));
struct aoc_ie_currency {
uint32_t amount;
uint8_t multiplier;
char name[AOC_CURRENCY_NAME_SIZE];
} __attribute__((packed));
struct aoc_ie_unit {
uint32_t amount;
uint8_t valid_type;
uint8_t valid_amount;
uint8_t type;
} __attribute__((packed));
struct aoc_ie_billing {
uint8_t id;
} __attribute__((packed));
struct aoc_ie_charging_association {
struct ast_aoc_charging_association ca;
} __attribute__((packed));
struct aoc_ie_charging_rate {
struct ast_aoc_s_entry entry;
} __attribute__((packed));
struct ast_aoc_decoded *ast_aoc_create(const enum ast_aoc_type msg_type,
const enum ast_aoc_charge_type charge_type,
const enum ast_aoc_request requests)
{
struct ast_aoc_decoded *decoded = NULL;
/* verify input */
if (((unsigned int) charge_type > AST_AOC_CHARGE_UNIT) ||
((unsigned int) msg_type > AST_AOC_E) ||
((msg_type == AST_AOC_REQUEST) && !requests)) {
ast_log(LOG_WARNING, "Failed to create ast_aoc_decoded object, invalid input\n");
return NULL;
}
if (!(decoded = ast_calloc(1, sizeof(struct ast_aoc_decoded)))) {
ast_log(LOG_WARNING, "Failed to create ast_aoc_decoded object \n");
return NULL;
}
decoded->msg_type = msg_type;
if (msg_type == AST_AOC_REQUEST) {
decoded->request_flag = requests;
} else if ((msg_type == AST_AOC_D) || (msg_type == AST_AOC_E)) {
decoded->charge_type = charge_type;
}
return decoded;
}
void *ast_aoc_destroy_decoded(struct ast_aoc_decoded *decoded)
{
ast_free(decoded);
return NULL;
}
void *ast_aoc_destroy_encoded(struct ast_aoc_encoded *encoded)
{
ast_free(encoded);
return NULL;
}
static void aoc_parse_ie_charging_rate(struct ast_aoc_decoded *decoded, const struct aoc_ie_charging_rate *ie)
{
struct ast_aoc_s_entry entry = { 0, };
entry.charged_item = ntohs(ie->entry.charged_item);
entry.rate_type = ntohs(ie->entry.rate_type);
switch (entry.rate_type) {
case AST_AOC_RATE_TYPE_DURATION:
entry.rate.duration.multiplier = ntohs(ie->entry.rate.duration.multiplier);
entry.rate.duration.amount = ntohl(ie->entry.rate.duration.amount);
entry.rate.duration.time = ntohl(ie->entry.rate.duration.time);
entry.rate.duration.time_scale = ntohs(ie->entry.rate.duration.time_scale);
entry.rate.duration.granularity_time = ntohl(ie->entry.rate.duration.granularity_time);
entry.rate.duration.granularity_time_scale = ntohs(ie->entry.rate.duration.granularity_time_scale);
entry.rate.duration.charging_type = ie->entry.rate.duration.charging_type; /* only one byte */
if (!ast_strlen_zero(ie->entry.rate.duration.currency_name)) {
ast_copy_string(entry.rate.duration.currency_name,
ie->entry.rate.duration.currency_name,
sizeof(entry.rate.duration.currency_name));
}
break;
case AST_AOC_RATE_TYPE_FLAT:
entry.rate.flat.multiplier = ntohs(ie->entry.rate.flat.multiplier);
entry.rate.flat.amount = ntohl(ie->entry.rate.flat.amount);
if (!ast_strlen_zero(ie->entry.rate.flat.currency_name)) {
ast_copy_string(entry.rate.flat.currency_name,
ie->entry.rate.flat.currency_name,
sizeof(entry.rate.flat.currency_name));
}
break;
case AST_AOC_RATE_TYPE_VOLUME:
entry.rate.volume.multiplier = ntohs(ie->entry.rate.volume.multiplier);
entry.rate.volume.amount = ntohl(ie->entry.rate.volume.amount);
entry.rate.volume.volume_unit = ntohs(ie->entry.rate.volume.volume_unit);
if (!ast_strlen_zero(ie->entry.rate.volume.currency_name)) {
ast_copy_string(entry.rate.volume.currency_name,
ie->entry.rate.volume.currency_name,
sizeof(entry.rate.volume.currency_name));
}
break;
case AST_AOC_RATE_TYPE_SPECIAL_CODE:
entry.rate.special_code = ntohs(ie->entry.rate.special_code);
break;
}
aoc_s_add_entry(decoded, &entry);
}
static int aoc_parse_ie(struct ast_aoc_decoded *decoded, unsigned char *data, unsigned int datalen)
{
enum AOC_IE ie_id;
unsigned int len;
while (datalen >= 2) {
ie_id = data[0];
len = data[1];
if (len > datalen -2) {
ast_log(LOG_ERROR, "AOC information element length exceeds the total message size\n");
return -1;
}
switch(ie_id) {
case AOC_IE_CURRENCY:
if (len == sizeof(struct aoc_ie_currency)) {
struct aoc_ie_currency ie;
memcpy(&ie, data + 2, len);
decoded->currency_amount = ntohl(ie.amount);
decoded->multiplier = ie.multiplier; /* only one byte */
memcpy(decoded->currency_name, ie.name, sizeof(decoded->currency_name));
} else {
ast_log(LOG_WARNING, "Received invalid currency ie\n");
}
break;
case AOC_IE_UNIT:
if (len == sizeof(struct aoc_ie_unit)) {
struct aoc_ie_unit ie;
memcpy(&ie, data + 2, len);
ast_aoc_add_unit_entry(decoded, ie.valid_amount, ntohl(ie.amount), ie.valid_type, ie.type);
} else {
ast_log(LOG_WARNING, "Received invalid unit ie\n");
}
break;
case AOC_IE_BILLING:
if (len == sizeof(struct aoc_ie_billing)) {
struct aoc_ie_billing ie;
memcpy(&ie, data + 2, len);
decoded->billing_id = ie.id; /* only one byte */
} else {
ast_log(LOG_WARNING, "Received invalid billing ie\n");
}
break;
case AOC_IE_CHARGING_ASSOCIATION:
if (len == sizeof(struct aoc_ie_charging_association)) {
memcpy(&decoded->charging_association, data + 2, sizeof(decoded->charging_association));
/* everything in the charging_association struct is a single byte except for the id */
if (decoded->charging_association.charging_type == AST_AOC_CHARGING_ASSOCIATION_ID) {
decoded->charging_association.charge.id = ntohl(decoded->charging_association.charge.id);
}
} else {
ast_log(LOG_WARNING, "Received invalid charging association ie\n");
}
break;
case AOC_IE_RATE:
if (len == sizeof(struct aoc_ie_charging_rate)) {
struct aoc_ie_charging_rate ie;
memcpy(&ie, data + 2, len);
aoc_parse_ie_charging_rate(decoded, &ie);
} else {
ast_log(LOG_WARNING, "Received invalid charging rate ie\n");
}
break;
case AOC_IE_TERMINATION_REQUEST:
if (len == 0) {
decoded->termination_request = 1;
} else {
ast_log(LOG_WARNING, "Received invalid termination request ie\n");
}
break;
default:
ast_log(LOG_WARNING, "Unknown AOC Information Element, ignoring.\n");
}
datalen -= (len + 2);
data += (len + 2);
}
return 0;
}
struct ast_aoc_decoded *ast_aoc_decode(struct ast_aoc_encoded *encoded, size_t size, struct ast_channel *chan)
{
struct ast_aoc_decoded *decoded;
/* verify our encoded payload is actually large enough to hold all the ies */
if ((size - (sizeof(struct ast_aoc_encoded)) != ntohs(encoded->datalen))) {
ast_log(LOG_WARNING, "Corrupted aoc encoded object, can not decode\n");
return NULL;
}
if (!(decoded = ast_calloc(1, sizeof(struct ast_aoc_decoded)))) {
ast_log(LOG_WARNING, "Failed to create ast_aoc_decoded object \n");
return NULL;
}
/* decode flags */
if ((encoded->flags & AST_AOC_ENCODED_TYPE_S) == AST_AOC_ENCODED_TYPE_S) {
decoded->msg_type = AST_AOC_S;
} else if (encoded->flags & AST_AOC_ENCODED_TYPE_E) {
decoded->msg_type = AST_AOC_E;
} else if (encoded->flags & AST_AOC_ENCODED_TYPE_D) {
decoded->msg_type = AST_AOC_D;
} else {
decoded->msg_type = AST_AOC_REQUEST;
}
if (decoded->msg_type == AST_AOC_REQUEST) {
if (encoded->flags & AST_AOC_ENCODED_REQUEST_S) {
decoded->request_flag |= AST_AOC_REQUEST_S;
}
if (encoded->flags & AST_AOC_ENCODED_REQUEST_D) {
decoded->request_flag |= AST_AOC_REQUEST_D;
}
if (encoded->flags & AST_AOC_ENCODED_REQUEST_E) {
decoded->request_flag |= AST_AOC_REQUEST_E;
}
} else if ((decoded->msg_type == AST_AOC_D) || (decoded->msg_type == AST_AOC_E)) {
if ((encoded->flags & AST_AOC_ENCODED_CHARGE_UNIT) == AST_AOC_ENCODED_CHARGE_UNIT) {
decoded->charge_type = AST_AOC_CHARGE_UNIT;
} else if ((encoded->flags & AST_AOC_ENCODED_CHARGE_CURRENCY) == AST_AOC_ENCODED_CHARGE_CURRENCY) {
decoded->charge_type = AST_AOC_CHARGE_CURRENCY;
} else if ((encoded->flags & AST_AOC_ENCODED_CHARGE_FREE) == AST_AOC_ENCODED_CHARGE_FREE) {
decoded->charge_type = AST_AOC_CHARGE_FREE;
} else {
decoded->charge_type = AST_AOC_CHARGE_NA;
}
if (encoded->flags & AST_AOC_ENCODED_CHARGE_SUBTOTAL) {
decoded->total_type = AST_AOC_SUBTOTAL;
}
}
/* decode information elements */
aoc_parse_ie(decoded, encoded->data, ntohs(encoded->datalen));
if (aoc_debug_enabled) {
aoc_display_decoded_debug(decoded, 1, chan);
}
return decoded;
}
struct aoc_ie_data {
unsigned char buf[1024];
int pos;
};
/*!
* \internal
* \brief append an AOC information element
* \note data is expected to already be in network byte order at this point
*/
static int aoc_append_ie(struct aoc_ie_data *ied, unsigned short ie_id, const void *data, unsigned short datalen)
{
if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
ast_log(LOG_WARNING, "Failure to append AOC information element, out of space \n");
return -1;
}
ied->buf[ied->pos++] = ie_id;
ied->buf[ied->pos++] = datalen;
if (datalen) {
memcpy(ied->buf + ied->pos, data, datalen);
ied->pos += datalen;
}
return 0;
}
static void aoc_create_ie_data_charging_rate(const struct ast_aoc_s_entry *entry, struct aoc_ie_charging_rate *ie)
{
ie->entry.charged_item = htons(entry->charged_item);
ie->entry.rate_type = htons(entry->rate_type);
switch (entry->rate_type) {
case AST_AOC_RATE_TYPE_DURATION:
ie->entry.rate.duration.multiplier = htons(entry->rate.duration.multiplier);
ie->entry.rate.duration.amount = htonl(entry->rate.duration.amount);
ie->entry.rate.duration.time = htonl(entry->rate.duration.time);
ie->entry.rate.duration.time_scale = htons(entry->rate.duration.time_scale);
ie->entry.rate.duration.granularity_time = htonl(entry->rate.duration.granularity_time);
ie->entry.rate.duration.granularity_time_scale = htons(entry->rate.duration.granularity_time_scale);
ie->entry.rate.duration.charging_type = entry->rate.duration.charging_type; /* only one byte */
if (!ast_strlen_zero(entry->rate.duration.currency_name)) {
ast_copy_string(ie->entry.rate.duration.currency_name,
entry->rate.duration.currency_name,
sizeof(ie->entry.rate.duration.currency_name));
}
break;
case AST_AOC_RATE_TYPE_FLAT:
ie->entry.rate.flat.multiplier = htons(entry->rate.flat.multiplier);
ie->entry.rate.flat.amount = htonl(entry->rate.flat.amount);
if (!ast_strlen_zero(entry->rate.flat.currency_name)) {
ast_copy_string(ie->entry.rate.flat.currency_name,
entry->rate.flat.currency_name,
sizeof(ie->entry.rate.flat.currency_name));
}
break;
case AST_AOC_RATE_TYPE_VOLUME:
ie->entry.rate.volume.multiplier = htons(entry->rate.volume.multiplier);
ie->entry.rate.volume.amount = htonl(entry->rate.volume.amount);
ie->entry.rate.volume.volume_unit = htons(entry->rate.volume.volume_unit);
if (!ast_strlen_zero(entry->rate.volume.currency_name)) {
ast_copy_string(ie->entry.rate.volume.currency_name,
entry->rate.volume.currency_name,
sizeof(ie->entry.rate.volume.currency_name));
}
break;
case AST_AOC_RATE_TYPE_SPECIAL_CODE:
ie->entry.rate.special_code = htons(entry->rate.special_code);
break;
}
}
static void aoc_create_ie_data(struct ast_aoc_decoded *decoded, struct aoc_ie_data *ied)
{
ied->pos = 0;
if (decoded->currency_amount) {
struct aoc_ie_currency ie = {
.amount = htonl(decoded->currency_amount),
.multiplier = decoded->multiplier, /* only one byte */
.name = { 0, },
};
if (!ast_strlen_zero(decoded->currency_name)) {
ast_copy_string(ie.name, decoded->currency_name, sizeof(ie.name));
}
aoc_append_ie(ied, AOC_IE_CURRENCY, (const void *) &ie, sizeof(ie));
}
if (decoded->unit_count) {
struct aoc_ie_unit ie = { 0 };
int i;
for (i = 0; i < decoded->unit_count; i++) {
ie.valid_amount = decoded->unit_list[i].valid_amount; /* only one byte */
ie.amount = htonl(decoded->unit_list[i].amount);
ie.valid_type = decoded->unit_list[i].valid_type; /* only one byte */
ie.type = decoded->unit_list[i].type; /* only one byte */
aoc_append_ie(ied, AOC_IE_UNIT, (const void *) &ie, sizeof(ie));
}
}
if (decoded->billing_id) {
struct aoc_ie_billing ie;
ie.id = decoded->billing_id; /* only one byte */
aoc_append_ie(ied, AOC_IE_BILLING, (const void *) &ie, sizeof(ie));
}
if (decoded->charging_association.charging_type != AST_AOC_CHARGING_ASSOCIATION_NA) {
struct aoc_ie_charging_association ie;
memset(&ie, 0, sizeof(ie));
ie.ca.charging_type = decoded->charging_association.charging_type; /* only one byte */
if (decoded->charging_association.charging_type == AST_AOC_CHARGING_ASSOCIATION_NUMBER) {
ie.ca.charge.number.plan = decoded->charging_association.charge.number.plan; /* only one byte */
ast_copy_string(ie.ca.charge.number.number,
decoded->charging_association.charge.number.number,
sizeof(ie.ca.charge.number.number));
} else if (decoded->charging_association.charging_type == AST_AOC_CHARGING_ASSOCIATION_ID) {
ie.ca.charge.id = htonl(decoded->charging_association.charge.id);
}
aoc_append_ie(ied, AOC_IE_CHARGING_ASSOCIATION, (const void *) &ie, sizeof(ie));
}
if (decoded->aoc_s_count) {
struct aoc_ie_charging_rate ie;
int i;
for (i = 0; i < decoded->aoc_s_count; i++) {
memset(&ie, 0, sizeof(ie));
aoc_create_ie_data_charging_rate(&decoded->aoc_s_entries[i], &ie);
aoc_append_ie(ied, AOC_IE_RATE, (const void *) &ie, sizeof(ie));
}
}
if (decoded->termination_request) {
aoc_append_ie(ied, AOC_IE_TERMINATION_REQUEST, NULL, 0);
}
}
struct ast_aoc_encoded *ast_aoc_encode(struct ast_aoc_decoded *decoded, size_t *out_size, struct ast_channel *chan)
{
struct aoc_ie_data ied;
struct ast_aoc_encoded *encoded = NULL;
size_t size = 0;
if (!decoded || !out_size) {
return NULL;
}
*out_size = 0;
/* create information element buffer before allocating the payload,
* by doing this the exact size of the payload + the id data can be
* allocated all at once. */
aoc_create_ie_data(decoded, &ied);
size = sizeof(struct ast_aoc_encoded) + ied.pos;
if (!(encoded = ast_calloc(1, size))) {
ast_log(LOG_WARNING, "Failed to create ast_aoc_encoded object during decode routine. \n");
return NULL;
}
/* -- Set ie data buffer */
if (ied.pos) {
/* this is safe because encoded was allocated to fit this perfectly */
memcpy(encoded->data, ied.buf, ied.pos);
encoded->datalen = htons(ied.pos);
}
/* --- Set Flags --- */
switch (decoded->msg_type) {
case AST_AOC_S:
encoded->flags = AST_AOC_ENCODED_TYPE_S;
break;
case AST_AOC_D:
encoded->flags = AST_AOC_ENCODED_TYPE_D;
break;
case AST_AOC_E:
encoded->flags = AST_AOC_ENCODED_TYPE_E;
break;
case AST_AOC_REQUEST:
encoded->flags = AST_AOC_ENCODED_TYPE_REQUEST;
default:
break;
}
/* if it is type request, set the types requested, else set charge type */
if (decoded->msg_type == AST_AOC_REQUEST) {
if (decoded->request_flag & AST_AOC_REQUEST_S) {
encoded->flags |= AST_AOC_ENCODED_REQUEST_S;
}
if (decoded->request_flag & AST_AOC_REQUEST_D) {
encoded->flags |= AST_AOC_ENCODED_REQUEST_D;
}
if (decoded->request_flag & AST_AOC_REQUEST_E) {
encoded->flags |= AST_AOC_ENCODED_REQUEST_E;
}
} else if ((decoded->msg_type == AST_AOC_D) || (decoded->msg_type == AST_AOC_E)) {
switch (decoded->charge_type) {
case AST_AOC_CHARGE_UNIT:
encoded->flags |= AST_AOC_ENCODED_CHARGE_UNIT;
break;
case AST_AOC_CHARGE_CURRENCY:
encoded->flags |= AST_AOC_ENCODED_CHARGE_CURRENCY;
break;
case AST_AOC_CHARGE_FREE:
encoded->flags |= AST_AOC_ENCODED_CHARGE_FREE;
case AST_AOC_CHARGE_NA:
default:
encoded->flags |= AST_AOC_ENCODED_CHARGE_NA;
break;
}
if (decoded->total_type == AST_AOC_SUBTOTAL) {
encoded->flags |= AST_AOC_ENCODED_CHARGE_SUBTOTAL;
}
}
/* --- Set Version Number --- */
encoded->version = AST_AOC_ENCODE_VERSION;
/* set the output size */
*out_size = size;
if (aoc_debug_enabled) {
aoc_display_decoded_debug(decoded, 0, chan);
}
return encoded;
}
static int aoc_s_add_entry(struct ast_aoc_decoded *decoded, struct ast_aoc_s_entry *entry)
{
if (decoded->aoc_s_count >= ARRAY_LEN(decoded->aoc_s_entries)) {
return -1;
}
decoded->aoc_s_entries[decoded->aoc_s_count] = *entry;
decoded->aoc_s_count++;
return 0;
}
unsigned int ast_aoc_s_get_count(struct ast_aoc_decoded *decoded)
{
return decoded->aoc_s_count;
}
const struct ast_aoc_s_entry *ast_aoc_s_get_rate_info(struct ast_aoc_decoded *decoded, unsigned int entry_number)
{
if (entry_number >= decoded->aoc_s_count) {
return NULL;
}
return (const struct ast_aoc_s_entry *) &decoded->aoc_s_entries[entry_number];
}
int ast_aoc_s_add_rate_duration(struct ast_aoc_decoded *decoded,
enum ast_aoc_s_charged_item charged_item,
unsigned int amount,
enum ast_aoc_currency_multiplier multiplier,
const char *currency_name,
unsigned long time,
enum ast_aoc_time_scale time_scale,
unsigned long granularity_time,
enum ast_aoc_time_scale granularity_time_scale,
int step_function)
{
struct ast_aoc_s_entry entry = { 0, };
entry.charged_item = charged_item;
entry.rate_type = AST_AOC_RATE_TYPE_DURATION;
entry.rate.duration.amount = amount;
entry.rate.duration.multiplier = multiplier;
entry.rate.duration.time = time;
entry.rate.duration.time_scale = time_scale;
entry.rate.duration.granularity_time = granularity_time;
entry.rate.duration.granularity_time_scale = granularity_time_scale;
entry.rate.duration.charging_type = step_function ? 1 : 0;
if (!ast_strlen_zero(currency_name)) {
ast_copy_string(entry.rate.duration.currency_name, currency_name, sizeof(entry.rate.duration.currency_name));
}
return aoc_s_add_entry(decoded, &entry);
}
int ast_aoc_s_add_rate_flat(struct ast_aoc_decoded *decoded,
enum ast_aoc_s_charged_item charged_item,
unsigned int amount,
enum ast_aoc_currency_multiplier multiplier,
const char *currency_name)
{
struct ast_aoc_s_entry entry = { 0, };
entry.charged_item = charged_item;
entry.rate_type = AST_AOC_RATE_TYPE_FLAT;
entry.rate.flat.amount = amount;
entry.rate.flat.multiplier = multiplier;
if (!ast_strlen_zero(currency_name)) {
ast_copy_string(entry.rate.flat.currency_name, currency_name, sizeof(entry.rate.flat.currency_name));
}
return aoc_s_add_entry(decoded, &entry);
}
int ast_aoc_s_add_rate_volume(struct ast_aoc_decoded *decoded,
enum ast_aoc_s_charged_item charged_item,
enum ast_aoc_volume_unit volume_unit,
unsigned int amount,
enum ast_aoc_currency_multiplier multiplier,
const char *currency_name)
{
struct ast_aoc_s_entry entry = { 0, };
entry.charged_item = charged_item;
entry.rate_type = AST_AOC_RATE_TYPE_VOLUME;
entry.rate.volume.multiplier = multiplier;
entry.rate.volume.amount = amount;
entry.rate.volume.volume_unit = volume_unit;
if (!ast_strlen_zero(currency_name)) {
ast_copy_string(entry.rate.volume.currency_name, currency_name, sizeof(entry.rate.volume.currency_name));
}
return aoc_s_add_entry(decoded, &entry);
}
int ast_aoc_s_add_rate_special_charge_code(struct ast_aoc_decoded *decoded,
enum ast_aoc_s_charged_item charged_item,
unsigned int code)
{
struct ast_aoc_s_entry entry = { 0, };
entry.charged_item = charged_item;
entry.rate_type = AST_AOC_RATE_TYPE_SPECIAL_CODE;
entry.rate.special_code = code;
return aoc_s_add_entry(decoded, &entry);
}
int ast_aoc_s_add_rate_free(struct ast_aoc_decoded *decoded,
enum ast_aoc_s_charged_item charged_item,
int from_beginning)
{
struct ast_aoc_s_entry entry = { 0, };
entry.charged_item = charged_item;
entry.rate_type = from_beginning ? AST_AOC_RATE_TYPE_FREE_FROM_BEGINNING : AST_AOC_RATE_TYPE_FREE;
return aoc_s_add_entry(decoded, &entry);
}
int ast_aoc_s_add_rate_na(struct ast_aoc_decoded *decoded,
enum ast_aoc_s_charged_item charged_item)
{
struct ast_aoc_s_entry entry = { 0, };
entry.charged_item = charged_item;
entry.rate_type = AST_AOC_RATE_TYPE_NA;
return aoc_s_add_entry(decoded, &entry);
}
int ast_aoc_s_add_special_arrangement(struct ast_aoc_decoded *decoded,
unsigned int code)
{
struct ast_aoc_s_entry entry = { 0, };
entry.charged_item = AST_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT;
entry.rate_type = AST_AOC_RATE_TYPE_SPECIAL_CODE;
entry.rate.special_code = code;
return aoc_s_add_entry(decoded, &entry);
}
enum ast_aoc_type ast_aoc_get_msg_type(struct ast_aoc_decoded *decoded)
{
return decoded->msg_type;
}
enum ast_aoc_charge_type ast_aoc_get_charge_type(struct ast_aoc_decoded *decoded)
{
return decoded->charge_type;
}
enum ast_aoc_request ast_aoc_get_request(struct ast_aoc_decoded *decoded)
{
return decoded->request_flag;
}
int ast_aoc_set_total_type(struct ast_aoc_decoded *decoded,
const enum ast_aoc_total_type type)
{
decoded->total_type = type;
return 0;
}
enum ast_aoc_total_type ast_aoc_get_total_type(struct ast_aoc_decoded *decoded)
{
return decoded->total_type;
}
int ast_aoc_set_currency_info(struct ast_aoc_decoded *decoded,
const unsigned int amount,
const enum ast_aoc_currency_multiplier multiplier,
const char *name)
{
if (!ast_strlen_zero(name)) {
ast_copy_string(decoded->currency_name, name, sizeof(decoded->currency_name));
}
decoded->currency_amount = amount;
if (multiplier && (multiplier < AST_AOC_MULT_NUM_ENTRIES)) {
decoded->multiplier = multiplier;
} else {
decoded->multiplier = AST_AOC_MULT_ONE;
}
return 0;
}
unsigned int ast_aoc_get_currency_amount(struct ast_aoc_decoded *decoded)
{
return decoded->currency_amount;
}
enum ast_aoc_currency_multiplier ast_aoc_get_currency_multiplier(struct ast_aoc_decoded *decoded)
{
return decoded->multiplier;
}
const char *ast_aoc_get_currency_multiplier_decimal(struct ast_aoc_decoded *decoded)
{
switch (decoded->multiplier) {
case AST_AOC_MULT_ONETHOUSANDTH:
return "0.001";
case AST_AOC_MULT_ONEHUNDREDTH:
return "0.01";
case AST_AOC_MULT_ONETENTH:
return "0.1";
case AST_AOC_MULT_ONE:
return "1.0";
case AST_AOC_MULT_TEN:
return "10.0";
case AST_AOC_MULT_HUNDRED:
return "100.0";
case AST_AOC_MULT_THOUSAND:
return "1000.0";
default:
return "1.0";
}
}
const char *ast_aoc_get_currency_name(struct ast_aoc_decoded *decoded)
{
return decoded->currency_name;
}
int ast_aoc_add_unit_entry(struct ast_aoc_decoded *decoded,
const unsigned int amount_is_present,
const unsigned int amount,
const unsigned int type_is_present,
const unsigned int type)
{
if ((decoded->msg_type == AST_AOC_REQUEST) ||
(decoded->unit_count >= ARRAY_LEN(decoded->unit_list))) {
return -1;
}
if (!amount_is_present && !type_is_present) {
return -1;
}
decoded->unit_list[decoded->unit_count].valid_amount = amount_is_present;
if (amount_is_present) {
decoded->unit_list[decoded->unit_count].amount = amount;
} else {
decoded->unit_list[decoded->unit_count].amount = 0;
}
decoded->unit_list[decoded->unit_count].valid_type = type_is_present;
if (type_is_present) {
decoded->unit_list[decoded->unit_count].type = type;
} else {
decoded->unit_list[decoded->unit_count].type = 0;
}
decoded->unit_count++;
return 0;
}
const struct ast_aoc_unit_entry *ast_aoc_get_unit_info(struct ast_aoc_decoded *decoded, unsigned int entry_number)
{
if (entry_number >= decoded->unit_count) {
return NULL;
}
return (const struct ast_aoc_unit_entry *) &decoded->unit_list[entry_number];
}
unsigned int ast_aoc_get_unit_count(struct ast_aoc_decoded *decoded)
{
return decoded->unit_count;
}
int ast_aoc_set_billing_id(struct ast_aoc_decoded *decoded, const enum ast_aoc_billing_id id)
{
if ((id >= AST_AOC_BILLING_NUM_ENTRIES) || (id < AST_AOC_BILLING_NA)) {
return -1;
}
decoded->billing_id = id;
return 0;
}
enum ast_aoc_billing_id ast_aoc_get_billing_id(struct ast_aoc_decoded *decoded)
{
return decoded->billing_id;
}
int ast_aoc_set_association_id(struct ast_aoc_decoded *decoded, const int id)
{
if (decoded->msg_type != AST_AOC_E) {
return -1;
}
memset(&decoded->charging_association, 0, sizeof(decoded->charging_association));
decoded->charging_association.charging_type = AST_AOC_CHARGING_ASSOCIATION_ID;
decoded->charging_association.charge.id = id;
return 0;
}
const struct ast_aoc_charging_association *ast_aoc_get_association_info(struct ast_aoc_decoded *decoded)
{
return &decoded->charging_association;
}
int ast_aoc_set_association_number(struct ast_aoc_decoded *decoded, const char *num, uint8_t plan)
{
if ((decoded->msg_type != AST_AOC_E) || ast_strlen_zero(num)) {
return -1;
}
memset(&decoded->charging_association, 0, sizeof(decoded->charging_association));
decoded->charging_association.charging_type = AST_AOC_CHARGING_ASSOCIATION_NUMBER;
decoded->charging_association.charge.number.plan = plan;
ast_copy_string(decoded->charging_association.charge.number.number, num, sizeof(decoded->charging_association.charge.number.number));
return 0;
}
int ast_aoc_set_termination_request(struct ast_aoc_decoded *decoded)
{
if (decoded->msg_type != AST_AOC_REQUEST) {
return -1;
}
decoded->termination_request = 1;
return 0;
}
int ast_aoc_get_termination_request(struct ast_aoc_decoded *decoded)
{
return decoded->termination_request;
}
/*!
* \internal
* \brief Convert AST_AOC_VOLUME_UNIT to string.
* \since 1.8
*
* \param value Value to convert to string.
*
* \return String equivalent.
*/
static const char *aoc_volume_unit_str(enum ast_aoc_volume_unit value)
{
const char *str;
switch (value) {
default:
case AST_AOC_VOLUME_UNIT_OCTET:
str = "Octet";
break;
case AST_AOC_VOLUME_UNIT_SEGMENT:
str = "Segment";
break;
case AST_AOC_VOLUME_UNIT_MESSAGE:
str = "Message";
break;
}
return str;
}
/*!
* \internal
* \brief Convert ast_aoc_charged_item to string.
* \since 1.8
*
* \param value Value to convert to string.
*
* \return String equivalent.
*/
static const char *aoc_charged_item_str(enum ast_aoc_s_charged_item value)
{
const char *str;
switch (value) {
default:
case AST_AOC_CHARGED_ITEM_NA:
str = "NotAvailable";
break;
case AST_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT:
str = "SpecialArrangement";
break;
case AST_AOC_CHARGED_ITEM_BASIC_COMMUNICATION:
str = "BasicCommunication";
break;
case AST_AOC_CHARGED_ITEM_CALL_ATTEMPT:
str = "CallAttempt";
break;
case AST_AOC_CHARGED_ITEM_CALL_SETUP:
str = "CallSetup";
break;
case AST_AOC_CHARGED_ITEM_USER_USER_INFO:
str = "UserUserInfo";
break;
case AST_AOC_CHARGED_ITEM_SUPPLEMENTARY_SERVICE:
str = "SupplementaryService";
break;
}
return str;
}
/*!
* \internal
* \brief Convert ast_aoc_total_type to string.
* \since 1.8
*
* \param value Value to convert to string.
*
* \return String equivalent.
*/
static const char *aoc_type_of_totaling_str(enum ast_aoc_total_type value)
{
const char *str;
switch (value) {
default:
case AST_AOC_SUBTOTAL:
str = "SubTotal";
break;
case AST_AOC_TOTAL:
str = "Total";
break;
}
return str;
}
/*!
* \internal
* \brief Convert ast_aoc_rate_type to string.
* \since 1.8
*
* \param value Value to convert to string.
*
* \return String equivalent.
*/
static const char *aoc_rate_type_str(enum ast_aoc_s_rate_type value)
{
const char *str;
switch (value) {
default:
case AST_AOC_RATE_TYPE_NA:
str = "NotAvailable";
break;
case AST_AOC_RATE_TYPE_FREE:
str = "Free";
break;
case AST_AOC_RATE_TYPE_FREE_FROM_BEGINNING:
str = "FreeFromBeginning";
break;
case AST_AOC_RATE_TYPE_DURATION:
str = "Duration";
break;
case AST_AOC_RATE_TYPE_FLAT:
str = "Flat";
break;
case AST_AOC_RATE_TYPE_VOLUME:
str = "Volume";
break;
case AST_AOC_RATE_TYPE_SPECIAL_CODE:
str = "SpecialCode";
break;
}
return str;
}
/*!
* \internal
* \brief Convert AST_AOC_TIME_SCALE to string.
* \since 1.8
*
* \param value Value to convert to string.
*
* \return String equivalent.
*/
static const char *aoc_scale_str(enum ast_aoc_time_scale value)
{
const char *str;
switch (value) {
default:
case AST_AOC_TIME_SCALE_HUNDREDTH_SECOND:
str = "OneHundredthSecond";
break;
case AST_AOC_TIME_SCALE_TENTH_SECOND:
str = "OneTenthSecond";
break;
case AST_AOC_TIME_SCALE_SECOND:
str = "Second";
break;
case AST_AOC_TIME_SCALE_TEN_SECOND:
str = "TenSeconds";
break;
case AST_AOC_TIME_SCALE_MINUTE:
str = "Minute";
break;
case AST_AOC_TIME_SCALE_HOUR:
str = "Hour";
break;
case AST_AOC_TIME_SCALE_DAY:
str = "Day";
break;
}
return str;
}
static const char *aoc_charge_type_str(enum ast_aoc_charge_type value)
{
const char *str;
switch (value) {
default:
case AST_AOC_CHARGE_NA:
str = "NotAvailable";
break;
case AST_AOC_CHARGE_FREE:
str = "Free";
break;
case AST_AOC_CHARGE_CURRENCY:
str = "Currency";
break;
case AST_AOC_CHARGE_UNIT:
str = "Units";
break;
}
return str;
}
static const char *aoc_multiplier_str(enum ast_aoc_currency_multiplier mult)
{
switch (mult) {
case AST_AOC_MULT_ONETHOUSANDTH:
return "1/1000";
case AST_AOC_MULT_ONEHUNDREDTH:
return "1/100";
case AST_AOC_MULT_ONETENTH:
return "1/10";
case AST_AOC_MULT_ONE:
return "1";
case AST_AOC_MULT_TEN:
return "10";
case AST_AOC_MULT_HUNDRED:
return "100";
case AST_AOC_MULT_THOUSAND:
return "1000";
case AST_AOC_MULT_NUM_ENTRIES:
break;
}
return "1";
}
static const char *aoc_billingid_str(enum ast_aoc_billing_id billing_id)
{
switch (billing_id) {
case AST_AOC_BILLING_NORMAL:
return "Normal";
case AST_AOC_BILLING_REVERSE_CHARGE:
return "Reverse";
case AST_AOC_BILLING_CREDIT_CARD:
return "CreditCard";
case AST_AOC_BILLING_CALL_FWD_UNCONDITIONAL:
return "CallForwardingUnconditional";
case AST_AOC_BILLING_CALL_FWD_BUSY:
return "CallForwardingBusy";
case AST_AOC_BILLING_CALL_FWD_NO_REPLY:
return "CallForwardingNoReply";
case AST_AOC_BILLING_CALL_DEFLECTION:
return "CallDeflection";
case AST_AOC_BILLING_CALL_TRANSFER:
return "CallTransfer";
case AST_AOC_BILLING_NA:
return "NotAvailable";
case AST_AOC_BILLING_NUM_ENTRIES:
break;
}
return "NotAvailable";
}
int ast_aoc_test_encode_decode_match(struct ast_aoc_decoded *decoded)
{
struct ast_aoc_decoded *new_decoded = NULL;
struct ast_aoc_encoded *encoded = NULL;
size_t size;
int res = 0;
if (!(encoded = ast_aoc_encode(decoded, &size, NULL))) {
return -1;
}
if (!(new_decoded = ast_aoc_decode(encoded, size, NULL))) {
ast_free(encoded);
return -1;
}
if (memcmp(new_decoded, decoded, sizeof(struct ast_aoc_decoded))) {
res = -1;
}
ast_aoc_destroy_decoded(new_decoded);
ast_aoc_destroy_encoded(encoded);
return res;
}
static char *aoc_cli_debug_enable(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
switch (cmd) {
case CLI_INIT:
e->command = "aoc set debug";
e->usage =
"Usage: 'aoc set debug on' to enable aoc debug, 'aoc set debug off' to disable debug.\n";
return NULL;
case CLI_GENERATE:
return NULL;
case CLI_HANDLER:
if (a->argc != 4) {
return CLI_SHOWUSAGE;
} else if(ast_true(a->argv[3])) {
ast_cli(a->fd, "aoc debug enabled\n");
aoc_debug_enabled = 1;
} else if (ast_false(a->argv[3])) {
ast_cli(a->fd, "aoc debug disabled\n");
aoc_debug_enabled = 0;
} else {
return CLI_SHOWUSAGE;
}
}
return CLI_SUCCESS;
}
/*!
* \internal
* \brief Append the time structure to the event message string.
* \since 1.8
*
* \param msg Event message string being built.
* \param prefix Prefix to add to the amount lines.
* \param name Name of the time structure to convert.
* \param time Data to convert.
* \param scale Data to convert.
*
* \return Nothing
*/
static void aoc_time_str(struct ast_str **msg, const char *prefix, const char *name, unsigned long time, enum ast_aoc_time_scale scale)
{
ast_str_append(msg, 0, "%s/%s/Length: %lu\r\n", prefix, name, time);
ast_str_append(msg, 0, "%s/%s/Scale: %s\r\n", prefix, name,
aoc_scale_str(scale));
}
/*!
* \internal
* \brief Append the amount structure to the event message string.
* \since 1.8
*
* \param msg Event message string being built.
* \param prefix Prefix to add to the amount lines.
* \param amount Data to convert.
* \param multipler to convert
*
* \return Nothing
*/
static void aoc_amount_str(struct ast_str **msg, const char *prefix, unsigned int amount, enum ast_aoc_currency_multiplier mult)
{
static const char name[] = "Amount";
ast_str_append(msg, 0, "%s/%s/Cost: %u\r\n", prefix, name, amount);
ast_str_append(msg, 0, "%s/%s/Multiplier: %s\r\n", prefix, name,
aoc_multiplier_str(mult));
}
static void aoc_request_event(const struct ast_aoc_decoded *decoded, struct ast_channel *chan, struct ast_str **msg)
{
if (chan) {
ast_str_append(msg, 0, "Channel: %s\r\n", chan->name);
ast_str_append(msg, 0, "UniqueID: %s\r\n", chan->uniqueid);
}
if (decoded->request_flag) {
ast_str_append(msg, 0, "AOCRequest:");
if (decoded->request_flag & AST_AOC_REQUEST_S) {
ast_str_append(msg, 0, "S");
}
if (decoded->request_flag & AST_AOC_REQUEST_D) {
ast_str_append(msg, 0, "D");
}
if (decoded->request_flag & AST_AOC_REQUEST_E) {
ast_str_append(msg, 0, "E");
}
ast_str_append(msg, 0, "\r\n");
} else {
ast_str_append(msg, 0, "AOCRequest: NONE\r\n");
}
}
static void aoc_s_event(const struct ast_aoc_decoded *decoded, struct ast_channel *owner, struct ast_str **msg)
{
const char *rate_str;
char prefix[32];
int idx;
if (owner) {
ast_str_append(msg, 0, "Channel: %s\r\n", owner->name);
ast_str_append(msg, 0, "UniqueID: %s\r\n", owner->uniqueid);
}
ast_str_append(msg, 0, "NumberRates: %d\r\n", decoded->aoc_s_count);
for (idx = 0; idx < decoded->aoc_s_count; ++idx) {
snprintf(prefix, sizeof(prefix), "Rate(%d)", idx);
ast_str_append(msg, 0, "%s/Chargeable: %s\r\n", prefix,
aoc_charged_item_str(decoded->aoc_s_entries[idx].charged_item));
if (decoded->aoc_s_entries[idx].charged_item == AST_AOC_CHARGED_ITEM_NA) {
continue;
}
rate_str = aoc_rate_type_str(decoded->aoc_s_entries[idx].rate_type);
ast_str_append(msg, 0, "%s/Type: %s\r\n", prefix, rate_str);
switch (decoded->aoc_s_entries[idx].rate_type) {
case AST_AOC_RATE_TYPE_DURATION:
strcat(prefix, "/");
strcat(prefix, rate_str);
ast_str_append(msg, 0, "%s/Currency: %s\r\n", prefix,
decoded->aoc_s_entries[idx].rate.duration.currency_name);
aoc_amount_str(msg, prefix,
decoded->aoc_s_entries[idx].rate.duration.amount,
decoded->aoc_s_entries[idx].rate.duration.multiplier);
ast_str_append(msg, 0, "%s/ChargingType: %s\r\n", prefix,
decoded->aoc_s_entries[idx].rate.duration.charging_type ?
"StepFunction" : "ContinuousCharging");
aoc_time_str(msg, prefix, "Time",
decoded->aoc_s_entries[idx].rate.duration.time,
decoded->aoc_s_entries[idx].rate.duration.time_scale);
if (decoded->aoc_s_entries[idx].rate.duration.granularity_time) {
aoc_time_str(msg, prefix, "Granularity",
decoded->aoc_s_entries[idx].rate.duration.granularity_time,
decoded->aoc_s_entries[idx].rate.duration.granularity_time_scale);
}
break;
case AST_AOC_RATE_TYPE_FLAT:
strcat(prefix, "/");
strcat(prefix, rate_str);
ast_str_append(msg, 0, "%s/Currency: %s\r\n", prefix,
decoded->aoc_s_entries[idx].rate.flat.currency_name);
aoc_amount_str(msg, prefix,
decoded->aoc_s_entries[idx].rate.flat.amount,
decoded->aoc_s_entries[idx].rate.flat.multiplier);
break;
case AST_AOC_RATE_TYPE_VOLUME:
strcat(prefix, "/");
strcat(prefix, rate_str);
ast_str_append(msg, 0, "%s/Currency: %s\r\n", prefix,
decoded->aoc_s_entries[idx].rate.volume.currency_name);
aoc_amount_str(msg, prefix,
decoded->aoc_s_entries[idx].rate.volume.amount,
decoded->aoc_s_entries[idx].rate.volume.multiplier);
ast_str_append(msg, 0, "%s/Unit: %s\r\n", prefix,
aoc_volume_unit_str(decoded->aoc_s_entries[idx].rate.volume.volume_unit));
break;
case AST_AOC_RATE_TYPE_SPECIAL_CODE:
ast_str_append(msg, 0, "%s/%s: %d\r\n", prefix, rate_str,
decoded->aoc_s_entries[idx].rate.special_code);
break;
default:
break;
}
}
}
static void aoc_d_event(const struct ast_aoc_decoded *decoded, struct ast_channel *chan, struct ast_str **msg)
{
const char *charge_str;
int idx;
char prefix[32];
if (chan) {
ast_str_append(msg, 0, "Channel: %s\r\n", chan->name);
ast_str_append(msg, 0, "UniqueID: %s\r\n", chan->uniqueid);
}
charge_str = aoc_charge_type_str(decoded->charge_type);
ast_str_append(msg, 0, "Type: %s\r\n", charge_str);
switch (decoded->charge_type) {
case AST_AOC_CHARGE_CURRENCY:
case AST_AOC_CHARGE_UNIT:
ast_str_append(msg, 0, "BillingID: %s\r\n",
aoc_billingid_str(decoded->billing_id));
ast_str_append(msg, 0, "TypeOfCharging: %s\r\n",
aoc_type_of_totaling_str(decoded->total_type));
break;
default:
break;
}
switch (decoded->charge_type) {
case AST_AOC_CHARGE_CURRENCY:
ast_str_append(msg, 0, "%s: %s\r\n", charge_str,
decoded->currency_name);
aoc_amount_str(msg, charge_str,
decoded->currency_amount,
decoded->multiplier);
break;
case AST_AOC_CHARGE_UNIT:
ast_str_append(msg, 0, "%s/NumberItems: %d\r\n", charge_str,
decoded->unit_count);
for (idx = 0; idx < decoded->unit_count; ++idx) {
snprintf(prefix, sizeof(prefix), "%s/Item(%d)", charge_str, idx);
if (decoded->unit_list[idx].valid_amount) {
ast_str_append(msg, 0, "%s/NumberOf: %u\r\n", prefix,
decoded->unit_list[idx].amount);
}
if (decoded->unit_list[idx].valid_type) {
ast_str_append(msg, 0, "%s/TypeOf: %d\r\n", prefix,
decoded->unit_list[idx].type);
}
}
break;
default:
break;
}
}
static void aoc_e_event(const struct ast_aoc_decoded *decoded, struct ast_channel *chan, struct ast_str **msg)
{
const char *charge_str;
int idx;
char prefix[32];
if (chan) {
ast_str_append(msg, 0, "Channel: %s\r\n", chan->name);
ast_str_append(msg, 0, "UniqueID: %s\r\n", chan->uniqueid);
}
charge_str = "ChargingAssociation";
switch (decoded->charging_association.charging_type) {
case AST_AOC_CHARGING_ASSOCIATION_NUMBER:
snprintf(prefix, sizeof(prefix), "%s/Number", charge_str);
ast_str_append(msg, 0, "%s: %s\r\n", prefix,
decoded->charging_association.charge.number.number);
ast_str_append(msg, 0, "%s/Plan: %d\r\n", prefix,
decoded->charging_association.charge.number.plan);
break;
case AST_AOC_CHARGING_ASSOCIATION_ID:
ast_str_append(msg, 0, "%s/ID: %d\r\n", charge_str, decoded->charging_association.charge.id);
break;
case AST_AOC_CHARGING_ASSOCIATION_NA:
default:
break;
}
charge_str = aoc_charge_type_str(decoded->charge_type);
ast_str_append(msg, 0, "Type: %s\r\n", charge_str);
switch (decoded->charge_type) {
case AST_AOC_CHARGE_CURRENCY:
case AST_AOC_CHARGE_UNIT:
ast_str_append(msg, 0, "BillingID: %s\r\n",
aoc_billingid_str(decoded->billing_id));
break;
default:
break;
}
switch (decoded->charge_type) {
case AST_AOC_CHARGE_CURRENCY:
ast_str_append(msg, 0, "%s: %s\r\n", charge_str,
decoded->currency_name);
aoc_amount_str(msg, charge_str,
decoded->currency_amount,
decoded->multiplier);
break;
case AST_AOC_CHARGE_UNIT:
ast_str_append(msg, 0, "%s/NumberItems: %d\r\n", charge_str,
decoded->unit_count);
for (idx = 0; idx < decoded->unit_count; ++idx) {
snprintf(prefix, sizeof(prefix), "%s/Item(%d)", charge_str, idx);
if (decoded->unit_list[idx].valid_amount) {
ast_str_append(msg, 0, "%s/NumberOf: %u\r\n", prefix,
decoded->unit_list[idx].amount);
}
if (decoded->unit_list[idx].valid_type) {
ast_str_append(msg, 0, "%s/TypeOf: %d\r\n", prefix,
decoded->unit_list[idx].type);
}
}
break;
default:
break;
}
}
int ast_aoc_manager_event(const struct ast_aoc_decoded *decoded, struct ast_channel *chan)
{
struct ast_str *msg;
if (!decoded || !(msg = ast_str_create(1024))) {
return -1;
}
switch (decoded->msg_type) {
case AST_AOC_S:
if (chan) {
aoc_s_event(decoded, chan, &msg);
ast_manager_event(chan, EVENT_FLAG_AOC, "AOC-S", "%s", ast_str_buffer(msg));
}
break;
case AST_AOC_D:
if (chan) {
aoc_d_event(decoded, chan, &msg);
ast_manager_event(chan, EVENT_FLAG_AOC, "AOC-D", "%s", ast_str_buffer(msg));
}
break;
case AST_AOC_E:
{
struct ast_channel *chans[1];
aoc_e_event(decoded, chan, &msg);
chans[0] = chan;
ast_manager_event_multichan(EVENT_FLAG_AOC, "AOC-E", chan ? 1 : 0, chans, "%s", ast_str_buffer(msg));
}
break;
default:
/* events for AST_AOC_REQUEST are not generated here */
break;
}
ast_free(msg);
return 0;
}
int ast_aoc_decoded2str(const struct ast_aoc_decoded *decoded, struct ast_str **msg)
{
if (!decoded || !msg) {
return -1;
}
switch (decoded->msg_type) {
case AST_AOC_S:
ast_str_append(msg, 0, "AOC-S\r\n");
aoc_s_event(decoded, NULL, msg);
break;
case AST_AOC_D:
ast_str_append(msg, 0, "AOC-D\r\n");
aoc_d_event(decoded, NULL, msg);
break;
case AST_AOC_E:
ast_str_append(msg, 0, "AOC-E\r\n");
aoc_e_event(decoded, NULL, msg);
break;
case AST_AOC_REQUEST:
ast_str_append(msg, 0, "AOC-Request\r\n");
aoc_request_event(decoded, NULL, msg);
break;
}
return 0;
}
static void aoc_display_decoded_debug(const struct ast_aoc_decoded *decoded, int decoding, struct ast_channel *chan)
{
struct ast_str *msg;
if (!decoded || !(msg = ast_str_create(1024))) {
return;
}
if (decoding) {
ast_str_append(&msg, 0, "---- DECODED AOC MSG ----\r\n");
} else {
ast_str_append(&msg, 0, "---- ENCODED AOC MSG ----\r\n");
}
if (chan) {
ast_str_append(&msg, 0, "CHANNEL: %s\r\n", chan->name);
}
if (ast_aoc_decoded2str(decoded, &msg)) {
ast_free(msg);
return;
}
ast_verb(1, "%s\r\n", ast_str_buffer(msg));
ast_free(msg);
}
static struct ast_cli_entry aoc_cli[] = {
AST_CLI_DEFINE(aoc_cli_debug_enable, "enable cli debugging of AOC messages"),
};
int ast_aoc_cli_init(void)
{
return ast_cli_register_multiple(aoc_cli, ARRAY_LEN(aoc_cli));
}