Generic Advice of Charge.
Asterisk Generic AOC Representation - Generic AOC encode/decode routines. (Generic AOC must be encoded to be passed on the wire in the AST_CONTROL_AOC frame) - AST_CONTROL_AOC frame type to represent generic encoded AOC data - Manager events for AOC-S, AOC-D, and AOC-E messages Asterisk App Support - app_dial AOC-S pass-through support on call setup - app_queue AOC-S pass-through support on call setup AOC Unit Tests - AOC Unit Tests for encode/decode routines - AOC Unit Test for manager event representation. SIP AOC Support - Pass-through of generic AOC-D and AOC-E messages to snom phones via the snom AOC specification. - Creation of chan_sip page3 flags for the addition of the new 'snom_aoc_enabled' sip.conf option. IAX AOC Support - Natively supports AOC pass-through through the use of the new AST_CONTROL_AOC frame type DAHDI AOC Support - ETSI PRI full AOC Pass-through support - 'aoc_enable' chan_dahdi.conf option for independently enabling pass-through of AOC-S, AOC-D, AOC-E. - 'aoce_delayhangup' option for retrieving AOC-E on disconnect. - DAHDI A() dial string option for requesting AOC services. example usage: ;requests AOC-S, AOC-D, and AOC-E on call setup exten=>1111,1,Dial(DAHDI/g1/1112/A(s,d,e)) Review: https://reviewboard.asterisk.org/r/552/ git-svn-id: http://svn.digium.com/svn/asterisk/trunk@267096 f38db490-d61c-443f-a65b-d21fe96a405b
This commit is contained in:
parent
79137525f0
commit
245c5d9eb8
|
@ -62,6 +62,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
#include "asterisk/global_datastores.h"
|
||||
#include "asterisk/dsp.h"
|
||||
#include "asterisk/cel.h"
|
||||
#include "asterisk/aoc.h"
|
||||
#include "asterisk/ccss.h"
|
||||
#include "asterisk/indications.h"
|
||||
|
||||
|
@ -638,6 +639,7 @@ struct chanlist {
|
|||
struct ast_party_connected_line connected;
|
||||
/*! TRUE if an AST_CONTROL_CONNECTED_LINE update was saved to the connected element. */
|
||||
unsigned int pending_connected_update:1;
|
||||
struct ast_aoc_decoded *aoc_s_rate_list;
|
||||
};
|
||||
|
||||
static int detect_disconnect(struct ast_channel *chan, char code, struct ast_str *featurecode);
|
||||
|
@ -645,6 +647,7 @@ static int detect_disconnect(struct ast_channel *chan, char code, struct ast_str
|
|||
static void chanlist_free(struct chanlist *outgoing)
|
||||
{
|
||||
ast_party_connected_line_free(&outgoing->connected);
|
||||
ast_aoc_destroy_decoded(outgoing->aoc_s_rate_list);
|
||||
ast_free(outgoing);
|
||||
}
|
||||
|
||||
|
@ -1053,6 +1056,14 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
ast_party_connected_line_free(&connected_caller);
|
||||
}
|
||||
}
|
||||
if (o->aoc_s_rate_list) {
|
||||
size_t encoded_size;
|
||||
struct ast_aoc_encoded *encoded;
|
||||
if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
|
||||
ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
|
||||
ast_aoc_destroy_encoded(encoded);
|
||||
}
|
||||
}
|
||||
peer = c;
|
||||
ast_copy_flags64(peerflags, o,
|
||||
OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
|
||||
|
@ -1115,6 +1126,14 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
ast_party_connected_line_free(&connected_caller);
|
||||
}
|
||||
}
|
||||
if (o->aoc_s_rate_list) {
|
||||
size_t encoded_size;
|
||||
struct ast_aoc_encoded *encoded;
|
||||
if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
|
||||
ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
|
||||
ast_aoc_destroy_encoded(encoded);
|
||||
}
|
||||
}
|
||||
peer = c;
|
||||
if (peer->cdr) {
|
||||
peer->cdr->answer = ast_tvnow();
|
||||
|
@ -1230,6 +1249,17 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
}
|
||||
}
|
||||
break;
|
||||
case AST_CONTROL_AOC:
|
||||
{
|
||||
struct ast_aoc_decoded *decoded = ast_aoc_decode(f->data.ptr, f->datalen, o->chan);
|
||||
if (decoded && (ast_aoc_get_msg_type(decoded) == AST_AOC_S)) {
|
||||
ast_aoc_destroy_decoded(o->aoc_s_rate_list);
|
||||
o->aoc_s_rate_list = decoded;
|
||||
} else {
|
||||
ast_aoc_destroy_decoded(decoded);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AST_CONTROL_REDIRECTING:
|
||||
if (ast_test_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE)) {
|
||||
ast_verb(3, "Redirecting update to %s prevented.\n", in->name);
|
||||
|
|
|
@ -94,6 +94,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
#include "asterisk/strings.h"
|
||||
#include "asterisk/global_datastores.h"
|
||||
#include "asterisk/taskprocessor.h"
|
||||
#include "asterisk/aoc.h"
|
||||
#include "asterisk/callerid.h"
|
||||
#include "asterisk/cel.h"
|
||||
#include "asterisk/data.h"
|
||||
|
@ -820,6 +821,7 @@ struct callattempt {
|
|||
unsigned int pending_connected_update:1;
|
||||
/*! TRUE if caller id is not available for connected line */
|
||||
unsigned int dial_callerid_absent:1;
|
||||
struct ast_aoc_decoded *aoc_s_rate_list;
|
||||
};
|
||||
|
||||
|
||||
|
@ -2654,6 +2656,7 @@ static void hangupcalls(struct callattempt *outgoing, struct ast_channel *except
|
|||
}
|
||||
oo = outgoing;
|
||||
outgoing = outgoing->q_next;
|
||||
ast_aoc_destroy_decoded(oo->aoc_s_rate_list);
|
||||
callattempt_free(oo);
|
||||
}
|
||||
}
|
||||
|
@ -3335,6 +3338,14 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
|
|||
ast_party_connected_line_free(&connected_caller);
|
||||
}
|
||||
}
|
||||
if (o->aoc_s_rate_list) {
|
||||
size_t encoded_size;
|
||||
struct ast_aoc_encoded *encoded;
|
||||
if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
|
||||
ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
|
||||
ast_aoc_destroy_encoded(encoded);
|
||||
}
|
||||
}
|
||||
peer = o;
|
||||
}
|
||||
} else if (o->chan && (o->chan == winner)) {
|
||||
|
@ -3454,6 +3465,14 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
|
|||
ast_party_connected_line_free(&connected_caller);
|
||||
}
|
||||
}
|
||||
if (o->aoc_s_rate_list) {
|
||||
size_t encoded_size;
|
||||
struct ast_aoc_encoded *encoded;
|
||||
if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
|
||||
ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
|
||||
ast_aoc_destroy_encoded(encoded);
|
||||
}
|
||||
}
|
||||
peer = o;
|
||||
}
|
||||
break;
|
||||
|
@ -3523,6 +3542,17 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
|
|||
}
|
||||
}
|
||||
break;
|
||||
case AST_CONTROL_AOC:
|
||||
{
|
||||
struct ast_aoc_decoded *decoded = ast_aoc_decode(f->data.ptr, f->datalen, o->chan);
|
||||
if (decoded && (ast_aoc_get_msg_type(decoded) == AST_AOC_S)) {
|
||||
ast_aoc_destroy_decoded(o->aoc_s_rate_list);
|
||||
o->aoc_s_rate_list = decoded;
|
||||
} else {
|
||||
ast_aoc_destroy_decoded(decoded);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AST_CONTROL_REDIRECTING:
|
||||
if (!update_connectedline) {
|
||||
ast_verb(3, "Redirecting update to %s prevented\n", inchan_name);
|
||||
|
|
|
@ -11779,6 +11779,10 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
|
|||
#endif /* defined(HAVE_PRI_CCSS) */
|
||||
pris[span].pri.transfer = conf->chan.transfer;
|
||||
pris[span].pri.facilityenable = conf->pri.pri.facilityenable;
|
||||
#if defined(HAVE_PRI_AOC_EVENTS)
|
||||
pris[span].pri.aoc_passthrough_flag = conf->pri.pri.aoc_passthrough_flag;
|
||||
pris[span].pri.aoce_delayhangup = conf->pri.pri.aoce_delayhangup;
|
||||
#endif /* defined(HAVE_PRI_AOC_EVENTS) */
|
||||
ast_copy_string(pris[span].pri.msn_list, conf->pri.pri.msn_list, sizeof(pris[span].pri.msn_list));
|
||||
ast_copy_string(pris[span].pri.idledial, conf->pri.pri.idledial, sizeof(pris[span].pri.idledial));
|
||||
ast_copy_string(pris[span].pri.idleext, conf->pri.pri.idleext, sizeof(pris[span].pri.idleext));
|
||||
|
@ -17195,6 +17199,21 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
|
|||
#endif /* PRI_GETSET_TIMERS */
|
||||
} else if (!strcasecmp(v->name, "facilityenable")) {
|
||||
confp->pri.pri.facilityenable = ast_true(v->value);
|
||||
#if defined(HAVE_PRI_AOC_EVENTS)
|
||||
} else if (!strcasecmp(v->name, "aoc_enable")) {
|
||||
confp->pri.pri.aoc_passthrough_flag = 0;
|
||||
if (strchr(v->value, 's') || strchr(v->value, 'S')) {
|
||||
confp->pri.pri.aoc_passthrough_flag |= SIG_PRI_AOC_GRANT_S;
|
||||
}
|
||||
if (strchr(v->value, 'd') || strchr(v->value, 'D')) {
|
||||
confp->pri.pri.aoc_passthrough_flag |= SIG_PRI_AOC_GRANT_D;
|
||||
}
|
||||
if (strchr(v->value, 'e') || strchr(v->value, 'E')) {
|
||||
confp->pri.pri.aoc_passthrough_flag |= SIG_PRI_AOC_GRANT_E;
|
||||
}
|
||||
} else if (!strcasecmp(v->name, "aoce_delayhangup")) {
|
||||
confp->pri.pri.aoce_delayhangup = ast_true(v->value);
|
||||
#endif /* defined(HAVE_PRI_AOC_EVENTS) */
|
||||
#if defined(HAVE_PRI_CALL_HOLD)
|
||||
} else if (!strcasecmp(v->name, "hold_disconnect_transfer")) {
|
||||
confp->pri.pri.hold_disconnect_transfer = ast_true(v->value);
|
||||
|
|
|
@ -261,6 +261,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
#include "asterisk/event.h"
|
||||
#include "asterisk/stun.h"
|
||||
#include "asterisk/cel.h"
|
||||
#include "asterisk/aoc.h"
|
||||
#include "sip/include/sip.h"
|
||||
#include "sip/include/globals.h"
|
||||
#include "sip/include/config_parser.h"
|
||||
|
@ -804,7 +805,7 @@ static int apeerobjs = 0; /*!< Autocreated peer objects */
|
|||
static int regobjs = 0; /*!< Registry objects */
|
||||
/* }@ */
|
||||
|
||||
static struct ast_flags global_flags[2] = {{0}}; /*!< global SIP_ flags */
|
||||
static struct ast_flags global_flags[3] = {{0}}; /*!< global SIP_ flags */
|
||||
static int global_t38_maxdatagram; /*!< global T.38 FaxMaxDatagram override */
|
||||
|
||||
static char used_context[AST_MAX_CONTEXT]; /*!< name of automatically created context for unloading */
|
||||
|
@ -1275,6 +1276,7 @@ static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, int seqn
|
|||
static int transmit_publish(struct sip_epa_entry *epa_entry, enum sip_publish_type publish_type, const char * const explicit_uri);
|
||||
static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init, const char * const explicit_uri);
|
||||
static int transmit_reinvite_with_sdp(struct sip_pvt *p, int t38version, int oldsdp);
|
||||
static int transmit_info_with_aoc(struct sip_pvt *p, struct ast_aoc_decoded *decoded);
|
||||
static int transmit_info_with_digit(struct sip_pvt *p, const char digit, unsigned int duration);
|
||||
static int transmit_info_with_vidupdate(struct sip_pvt *p);
|
||||
static int transmit_message_with_text(struct sip_pvt *p, const char *text);
|
||||
|
@ -4702,6 +4704,7 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer)
|
|||
|
||||
ast_copy_flags(&dialog->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY);
|
||||
ast_copy_flags(&dialog->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
|
||||
ast_copy_flags(&dialog->flags[2], &peer->flags[2], SIP_PAGE3_FLAGS_TO_COPY);
|
||||
dialog->capability = peer->capability;
|
||||
dialog->prefs = peer->prefs;
|
||||
if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_T38SUPPORT)) {
|
||||
|
@ -6249,6 +6252,41 @@ static int sip_indicate(struct ast_channel *ast, int condition, const void *data
|
|||
case AST_CONTROL_REDIRECTING:
|
||||
update_redirecting(p, data, datalen);
|
||||
break;
|
||||
case AST_CONTROL_AOC:
|
||||
{
|
||||
struct ast_aoc_decoded *decoded = ast_aoc_decode((struct ast_aoc_encoded *) data, datalen, ast);
|
||||
if (!decoded) {
|
||||
ast_log(LOG_ERROR, "Error decoding indicated AOC data\n");
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
switch (ast_aoc_get_msg_type(decoded)) {
|
||||
case AST_AOC_REQUEST:
|
||||
if (ast_aoc_get_termination_request(decoded)) {
|
||||
/* TODO, once there is a way to get AOC-E on hangup, attempt that here
|
||||
* before hanging up the channel.*/
|
||||
|
||||
/* The other side has already initiated the hangup. This frame
|
||||
* just says they are waiting to get AOC-E before completely tearing
|
||||
* the call down. Since SIP does not support this at the moment go
|
||||
* ahead and terminate the call here to avoid an unnecessary timeout. */
|
||||
ast_log(LOG_DEBUG, "AOC-E termination request received on %s. This is not yet supported on sip. Continue with hangup \n", p->owner->name);
|
||||
ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV);
|
||||
}
|
||||
break;
|
||||
case AST_AOC_D:
|
||||
case AST_AOC_E:
|
||||
if (ast_test_flag(&p->flags[2], SIP_PAGE3_SNOM_AOC)) {
|
||||
transmit_info_with_aoc(p, decoded);
|
||||
}
|
||||
break;
|
||||
case AST_AOC_S: /* S not supported yet */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ast_aoc_destroy_decoded(decoded);
|
||||
}
|
||||
break;
|
||||
case -1:
|
||||
res = -1;
|
||||
break;
|
||||
|
@ -6888,6 +6926,7 @@ struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *sin,
|
|||
/* Copy global flags to this PVT at setup. */
|
||||
ast_copy_flags(&p->flags[0], &global_flags[0], SIP_FLAGS_TO_COPY);
|
||||
ast_copy_flags(&p->flags[1], &global_flags[1], SIP_PAGE2_FLAGS_TO_COPY);
|
||||
ast_copy_flags(&p->flags[2], &global_flags[2], SIP_PAGE3_FLAGS_TO_COPY);
|
||||
|
||||
p->do_history = recordhistory;
|
||||
|
||||
|
@ -11856,6 +11895,53 @@ static int transmit_refer(struct sip_pvt *p, const char *dest)
|
|||
*/
|
||||
}
|
||||
|
||||
/*! \brief Send SIP INFO advice of charge message */
|
||||
static int transmit_info_with_aoc(struct sip_pvt *p, struct ast_aoc_decoded *decoded)
|
||||
{
|
||||
struct sip_request req;
|
||||
struct ast_str *str = ast_str_alloca(512);
|
||||
const struct ast_aoc_unit_entry *unit_entry = ast_aoc_get_unit_info(decoded, 0);
|
||||
enum ast_aoc_charge_type charging = ast_aoc_get_charge_type(decoded);
|
||||
|
||||
reqprep(&req, p, SIP_INFO, 0, 1);
|
||||
|
||||
if (ast_aoc_get_msg_type(decoded) == AST_AOC_D) {
|
||||
ast_str_append(&str, 0, "type=active;");
|
||||
} else if (ast_aoc_get_msg_type(decoded) == AST_AOC_E) {
|
||||
ast_str_append(&str, 0, "type=terminated;");
|
||||
} else {
|
||||
/* unsupported message type */
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (charging) {
|
||||
case AST_AOC_CHARGE_FREE:
|
||||
ast_str_append(&str, 0, "free-of-charge;");
|
||||
break;
|
||||
case AST_AOC_CHARGE_CURRENCY:
|
||||
ast_str_append(&str, 0, "charging;");
|
||||
ast_str_append(&str, 0, "charging-info=currency;");
|
||||
ast_str_append(&str, 0, "amount=%u;", ast_aoc_get_currency_amount(decoded));
|
||||
ast_str_append(&str, 0, "multiplier=%s;", ast_aoc_get_currency_multiplier_decimal(decoded));
|
||||
if (!ast_strlen_zero(ast_aoc_get_currency_name(decoded))) {
|
||||
ast_str_append(&str, 0, "currency=%s;", ast_aoc_get_currency_name(decoded));
|
||||
}
|
||||
break;
|
||||
case AST_AOC_CHARGE_UNIT:
|
||||
ast_str_append(&str, 0, "charging;");
|
||||
ast_str_append(&str, 0, "charging-info=pulse;");
|
||||
if (unit_entry) {
|
||||
ast_str_append(&str, 0, "recorded-units=%u;", unit_entry->amount);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ast_str_append(&str, 0, "not-available;");
|
||||
};
|
||||
|
||||
add_header(&req, "AOC", ast_str_buffer(str));
|
||||
|
||||
return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
|
||||
}
|
||||
|
||||
/*! \brief Send SIP INFO dtmf message, see Cisco documentation on cisco.com */
|
||||
static int transmit_info_with_digit(struct sip_pvt *p, const char digit, unsigned int duration)
|
||||
|
@ -14037,6 +14123,7 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
|
|||
/* Take the peer */
|
||||
ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY);
|
||||
ast_copy_flags(&p->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
|
||||
ast_copy_flags(&p->flags[2], &peer->flags[2], SIP_PAGE3_FLAGS_TO_COPY);
|
||||
|
||||
if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && p->udptl) {
|
||||
p->t38_maxdatagram = peer->t38_maxdatagram;
|
||||
|
@ -14081,6 +14168,7 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
|
|||
if (!(res = check_auth(p, req, peer->name, p->peersecret, p->peermd5secret, sipmethod, uri2, reliable, req->ignore))) {
|
||||
ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY);
|
||||
ast_copy_flags(&p->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
|
||||
ast_copy_flags(&p->flags[2], &peer->flags[2], SIP_PAGE3_FLAGS_TO_COPY);
|
||||
/* If we have a call limit, set flag */
|
||||
if (peer->call_limit)
|
||||
ast_set_flag(&p->flags[0], SIP_CALL_LIMIT);
|
||||
|
@ -24000,6 +24088,7 @@ static int sip_poke_peer(struct sip_peer *peer, int force)
|
|||
copy_socket_data(&p->socket, &peer->socket);
|
||||
ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY);
|
||||
ast_copy_flags(&p->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
|
||||
ast_copy_flags(&p->flags[2], &peer->flags[2], SIP_PAGE3_FLAGS_TO_COPY);
|
||||
|
||||
/* Send OPTIONs to peer's fullcontact */
|
||||
if (!ast_strlen_zero(peer->fullcontact))
|
||||
|
@ -24757,6 +24846,7 @@ static void set_peer_defaults(struct sip_peer *peer)
|
|||
peer->type = SIP_TYPE_PEER;
|
||||
ast_copy_flags(&peer->flags[0], &global_flags[0], SIP_FLAGS_TO_COPY);
|
||||
ast_copy_flags(&peer->flags[1], &global_flags[1], SIP_PAGE2_FLAGS_TO_COPY);
|
||||
ast_copy_flags(&peer->flags[2], &global_flags[2], SIP_PAGE3_FLAGS_TO_COPY);
|
||||
ast_string_field_set(peer, context, sip_cfg.default_context);
|
||||
ast_string_field_set(peer, subscribecontext, sip_cfg.default_subscribecontext);
|
||||
ast_string_field_set(peer, language, default_language);
|
||||
|
@ -24863,8 +24953,8 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
|
|||
int format = 0; /* Ama flags */
|
||||
int timerb_set = 0, timert1_set = 0;
|
||||
time_t regseconds = 0;
|
||||
struct ast_flags peerflags[2] = {{(0)}};
|
||||
struct ast_flags mask[2] = {{(0)}};
|
||||
struct ast_flags peerflags[3] = {{(0)}};
|
||||
struct ast_flags mask[3] = {{(0)}};
|
||||
char callback[256] = "";
|
||||
struct sip_peer tmp_peer;
|
||||
const char *srvlookup = NULL;
|
||||
|
@ -25255,6 +25345,8 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
|
|||
ast_string_field_set(peer, unsolicited_mailbox, v->value);
|
||||
} else if (!strcasecmp(v->name, "use_q850_reason")) {
|
||||
ast_set2_flag(&peer->flags[1], ast_true(v->value), SIP_PAGE2_Q850_REASON);
|
||||
} else if (!strcasecmp(v->name, "snom_aoc_enabled")) {
|
||||
ast_set2_flag(&peer->flags[2], ast_true(v->value), SIP_PAGE3_SNOM_AOC);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25424,6 +25516,7 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
|
|||
|
||||
ast_copy_flags(&peer->flags[0], &peerflags[0], mask[0].flags);
|
||||
ast_copy_flags(&peer->flags[1], &peerflags[1], mask[1].flags);
|
||||
ast_copy_flags(&peer->flags[2], &peerflags[2], mask[2].flags);
|
||||
if (ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE)) {
|
||||
sip_cfg.allowsubscribe = TRUE; /* No global ban any more */
|
||||
}
|
||||
|
@ -26163,6 +26256,8 @@ static int reload_config(enum channelreloadreason reason)
|
|||
}
|
||||
} else if (!strcasecmp(v->name, "use_q850_reason")) {
|
||||
ast_set2_flag(&global_flags[1], ast_true(v->value), SIP_PAGE2_Q850_REASON);
|
||||
} else if (!strcasecmp(v->name, "snom_aoc_enabled")) {
|
||||
ast_set2_flag(&global_flags[2], ast_true(v->value), SIP_PAGE3_SNOM_AOC);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
1415
channels/sig_pri.c
1415
channels/sig_pri.c
File diff suppressed because it is too large
Load Diff
|
@ -31,6 +31,10 @@
|
|||
#include <libpri.h>
|
||||
#include <dahdi/user.h>
|
||||
|
||||
#define SIG_PRI_AOC_GRANT_S (1 << 0)
|
||||
#define SIG_PRI_AOC_GRANT_D (1 << 1)
|
||||
#define SIG_PRI_AOC_GRANT_E (1 << 2)
|
||||
|
||||
#if defined(HAVE_PRI_CCSS)
|
||||
/*! PRI debug message flags when normal PRI debugging is turned on at the command line. */
|
||||
#define SIG_PRI_DEBUG_NORMAL \
|
||||
|
@ -192,6 +196,13 @@ struct sig_pri_chan {
|
|||
char keypad_digits[AST_MAX_EXTENSION];
|
||||
#endif /* defined(HAVE_PRI_SETUP_KEYPAD) */
|
||||
|
||||
#if defined(HAVE_PRI_AOC_EVENTS)
|
||||
struct pri_subcmd_aoc_e aoc_e;
|
||||
int aoc_s_request_invoke_id; /*!< If an AOC-S request was present for the call, this is the invoke_id to use for the response */
|
||||
unsigned int aoc_s_request_invoke_id_valid:1; /*!< This is set when the AOC-S invoke id is present */
|
||||
unsigned int waiting_for_aoce:1; /*!< Delaying hangup for AOC-E msg. If this is set and AOC-E is recieved, continue with hangup before timeout period. */
|
||||
unsigned int holding_aoce:1; /*!< received AOC-E msg from asterisk. holding for disconnect/release */
|
||||
#endif /* defined(HAVE_PRI_AOC_EVENTS) */
|
||||
unsigned int inalarm:1;
|
||||
unsigned int alerting:1; /*!< TRUE if channel is alerting/ringing */
|
||||
unsigned int alreadyhungup:1; /*!< TRUE if the call has already gone/hungup */
|
||||
|
@ -243,6 +254,12 @@ struct sig_pri_pri {
|
|||
int facilityenable; /*!< Enable facility IEs */
|
||||
int dchan_logical_span[SIG_PRI_NUM_DCHANS]; /*!< Logical offset the DCHAN sits in */
|
||||
int fds[SIG_PRI_NUM_DCHANS]; /*!< FD's for d-channels */
|
||||
|
||||
#if defined(HAVE_PRI_AOC_EVENTS)
|
||||
int aoc_passthrough_flag; /*!< Represents what AOC messages (S,D,E) are allowed to pass-through */
|
||||
int aoce_delayhangup:1; /*!< defines whether the aoce_delayhangup option is enabled or not */
|
||||
#endif /* defined(HAVE_PRI_AOC_EVENTS) */
|
||||
|
||||
#if defined(HAVE_PRI_SERVICE_MESSAGES)
|
||||
unsigned int enable_service_message_support:1; /*!< enable SERVICE message support */
|
||||
#endif /* defined(HAVE_PRI_SERVICE_MESSAGES) */
|
||||
|
|
|
@ -354,6 +354,12 @@
|
|||
SIP_PAGE2_RPID_IMMEDIATE | SIP_PAGE2_RPID_UPDATE | SIP_PAGE2_SYMMETRICRTP |\
|
||||
SIP_PAGE2_Q850_REASON | SIP_PAGE2_HAVEPEERCONTEXT)
|
||||
|
||||
|
||||
#define SIP_PAGE3_SNOM_AOC (1 << 0) /*!< DPG: Allow snom aoc messages */
|
||||
|
||||
#define SIP_PAGE3_FLAGS_TO_COPY \
|
||||
(SIP_PAGE3_SNOM_AOC)
|
||||
|
||||
/*@}*/
|
||||
|
||||
/*----------------------------------------------------------*/
|
||||
|
@ -943,7 +949,7 @@ struct sip_pvt {
|
|||
ast_group_t callgroup; /*!< Call group */
|
||||
ast_group_t pickupgroup; /*!< Pickup group */
|
||||
int lastinvite; /*!< Last Cseq of invite */
|
||||
struct ast_flags flags[2]; /*!< SIP_ flags */
|
||||
struct ast_flags flags[3]; /*!< SIP_ flags */
|
||||
|
||||
/* boolean flags that don't belong in flags */
|
||||
unsigned short do_history:1; /*!< Set if we want to record history */
|
||||
|
@ -1172,7 +1178,7 @@ struct sip_peer {
|
|||
struct ast_codec_pref prefs; /*!< codec prefs */
|
||||
int lastmsgssent;
|
||||
unsigned int sipoptions; /*!< Supported SIP options */
|
||||
struct ast_flags flags[2]; /*!< SIP_ flags */
|
||||
struct ast_flags flags[3]; /*!< SIP_ flags */
|
||||
|
||||
/*! Mailboxes that this peer cares about */
|
||||
AST_LIST_HEAD_NOLOCK(, sip_mailbox) mailboxes;
|
||||
|
|
|
@ -289,6 +289,24 @@
|
|||
;
|
||||
;facilityenable = yes
|
||||
;
|
||||
|
||||
; This option enables Advice of Charge pass-through between the ISDN PRI and
|
||||
; Asterisk. This option can be set to any combination of 's', 'd', and 'e' which
|
||||
; represent the different variants of Advice of Charge, AOC-S, AOC-D, and AOC-E.
|
||||
; Advice of Charge pass-through is currently only supported for ETSI. Since most
|
||||
; AOC messages are sent on facility messages, the 'facilityenable' option must
|
||||
; also be enabled to fully support AOC pass-through.
|
||||
;
|
||||
;aoc_enable=s,d,e
|
||||
;
|
||||
; When this option is enabled, a hangup initiated by the ISDN PRI side of the
|
||||
; asterisk channel will result in the channel delaying its hangup in an
|
||||
; attempt to receive the final AOC-E message from its bridge. The delay
|
||||
; period is configured as one half the T305 timer length. If the channel
|
||||
; is not bridged the hangup will occur immediatly without delay.
|
||||
;
|
||||
;aoce_delayhangup=yes
|
||||
|
||||
; pritimer cannot be changed on a reload.
|
||||
;
|
||||
; Signalling method. The default is "auto". Valid values:
|
||||
|
|
|
@ -107,7 +107,8 @@ bindaddr = 0.0.0.0
|
|||
; originate - Permission to originate new calls. Write-only.
|
||||
; agi - Output AGI commands executed. Input AGI command to execute.
|
||||
; cc - Call Completion events. Read-only.
|
||||
; aoc - Advice Of Charge events. Read-only.
|
||||
; aoc - Permission to send Advice Of Charge messages and receive Advice
|
||||
; - Of Charge events.
|
||||
;
|
||||
;read = system,call,log,verbose,agent,user,config,dtmf,reporting,cdr,dialplan
|
||||
;write = system,call,agent,user,config,command,reporting,originate
|
||||
|
|
|
@ -886,6 +886,12 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls
|
|||
; destinations which do not have a prior
|
||||
; account relationship with your server.
|
||||
|
||||
;------------------------------ Advice of Charge CONFIGURATION --------------------------
|
||||
; snom_aoc_enabled = yes; ; This options turns on and off support for sending AOC-D and
|
||||
; AOC-E to snom endpoints. This option can be used both in the
|
||||
; peer and global scope. The default for this option is off.
|
||||
|
||||
|
||||
;------------------------------ JITTER BUFFER CONFIGURATION --------------------------
|
||||
; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of a
|
||||
; SIP channel. Defaults to "no". An enabled jitterbuffer will
|
||||
|
|
|
@ -0,0 +1,180 @@
|
|||
================
|
||||
Advice of Charge
|
||||
================
|
||||
|
||||
Written by: David Vossel
|
||||
Initial version: 04-19-2010
|
||||
|
||||
This document is designed to give an overview of how to configure and
|
||||
generate Advice of Charge along with a detailed explanation of how each
|
||||
option works.
|
||||
|
||||
--------------------------------------
|
||||
| Terminology |
|
||||
--------------------------------------
|
||||
AOC: Advice of Charge
|
||||
|
||||
AOC-S: Advice of Charge message sent at the beginning of a call during
|
||||
call setup. This message contains a list of rates associated with the
|
||||
call.
|
||||
|
||||
AOC-D: Advice of Charge message sent during the call. This message
|
||||
is typically used to update the endpoint with the current call charge.
|
||||
|
||||
AOC-E: Advice of Charge message sent at the end of a call. This
|
||||
message is used to indicate to the endpoint the final call charge.
|
||||
|
||||
AMI: Asterisk Manager Interface. This interface is used to generate
|
||||
AOC messages and listen for AOC events.
|
||||
|
||||
--------------------------------------
|
||||
| AOC in chan_dahdi |
|
||||
--------------------------------------
|
||||
----- LibPRI Support:
|
||||
ETSI, or euroisdn, is the only switchtype that LibPRI currently supports
|
||||
for AOC.
|
||||
|
||||
----- Enable AOC Pass-through in chan_dahdi
|
||||
To enable AOC pass-through between the ISDN and Asterisk use the
|
||||
'aoc_enable' config option. This option allows for any combination
|
||||
of AOC-S, AOC-D, and AOC-E to be enabled or disabled.
|
||||
|
||||
For example:
|
||||
aoc_enable=s,d,e ; enables pass-through of AOC-S, AOC-D, and AOC-E
|
||||
|
||||
aoc_enable=s,d ; enables pass-through of AOC-S and AOC-D. Rejects
|
||||
; AOC-E and AOC-E request messages
|
||||
|
||||
Since AOC messages are often transported on facility messages, the
|
||||
'facilityenable' option must be enabled as well to fully support AOC
|
||||
pass-through.
|
||||
|
||||
----- Handling AOC-E in chan_dahdi
|
||||
Whenever a dahdi channel receives an AOC-E message from Asterisk, it
|
||||
stores that message to deliver it at the appropriate time during call
|
||||
termination. This means that if two AOC-E messages are received on the
|
||||
same call, the last one will override the first one and only one AOC-E
|
||||
message will be sent during call termination.
|
||||
|
||||
There are some tricky situations involving the final AOC-E message. During
|
||||
a bridged call, if the endpoint receiving the AOC messages terminates
|
||||
the call before the endpoint delivering the AOC does, the final AOC-E
|
||||
message sent by the sending side during termination will never make it to
|
||||
the receiving end because Asterisk will have already torn down that channel.
|
||||
This is where the chan_dahdi.conf 'aoce_delayhangup' option comes into play.
|
||||
|
||||
By enabling 'aoce_delayhangup', anytime a hangup is initiated by the
|
||||
ISDN side of an Asterisk channel, instead of hanging up the channel,
|
||||
the channel sends a unique internal AOC-E termination request to its bridge
|
||||
channel. This indicates it is about to hangup and wishes to receive the
|
||||
final AOC-E message from the bridged channel before completely tearing
|
||||
down. If the bridged channel knows what to do with this AOC-E termination
|
||||
request, it will do whatever is necessary to indicate to its endpoint that
|
||||
the call is being terminated without actually hanging up the Asterisk channel.
|
||||
This allows the final AOC-E message to come in and be sent across the bridge
|
||||
while both channels are still up. If the channel delaying its hangup for
|
||||
the final AOC-E message times out, the call will be torn down just as it
|
||||
normally would. In chan_dahdi the timeout period is 1/2 the T305 timer
|
||||
which by default is 15 seconds.
|
||||
|
||||
'aoce_delayhangup' currently only works when both bridged channels are
|
||||
dahdi_channels. If a SIP channel receives an AOC-E termination request, it
|
||||
just responds by immediately hanging up the channel. Using this option when
|
||||
bridged to any channel technology besides SIP or DAHDI will result in the
|
||||
15 second timeout period before tearing down the call completely.
|
||||
|
||||
----- Requesting AOC services
|
||||
AOC can be requested on a call by call basis using the DAHDI dialstring
|
||||
option, A(). The A() option takes in 's', 'd', and 'e' parameters which
|
||||
represent the three types of AOC messages, AOC-S, AOC-D, and AOC-E. By using
|
||||
this option Asterisk will indicate to the endpoint during call setup that it
|
||||
wishes to receive the specified forms of AOC during the call.
|
||||
|
||||
Example Usage in extensions.conf
|
||||
exten => 1111,1,Dial(DAHDI/g1/1112/A(s,d,e) ; requests AOC-S, AOC-D, and AOC-E on
|
||||
; call setup
|
||||
exten => 1111,1,Dial(DAHDI/g1/1112/A(d,e) ; requests only AOC-D, and AOC-E on
|
||||
; call setup
|
||||
|
||||
--------------------------------------
|
||||
| AOC in chan_sip |
|
||||
--------------------------------------
|
||||
Asterisk supports a very basic way of sending AOC on a SIP channel to Snom
|
||||
phones using an AOC specification designed by Snom. This support is limited
|
||||
to the sending of AOC-D and AOC-E pass-through messages. No support for
|
||||
AOC-E on call termination is present, so if the Snom endpoint receiving the
|
||||
AOC messages from Asterisk terminates the call, the channel will be torn
|
||||
down before the phone can receive the final AOC-E message.
|
||||
|
||||
To enable passthrough of AOC messages via the snom specification, use
|
||||
the 'snom_aoc_enabled' option in sip.conf.
|
||||
|
||||
--------------------------------------
|
||||
| Generate AOC Messages via AMI |
|
||||
--------------------------------------
|
||||
Asterisk supports a way to generate AOC messages on a channel via
|
||||
the AMI action AOCMessage. At the moment the AOCMessage action is limited
|
||||
to AOC-D and AOC-E message generation. There are some limitations
|
||||
involved with delivering the final AOC-E message as well. The AOCMessage
|
||||
action has its own detailed parameter documentation so this discussion will
|
||||
focus on higher level use. When generating AOC messages on a Dahdi channel
|
||||
first make sure the appropriate chan_dahdi.conf options are enabled. Without
|
||||
enabling 'aoc_enable' correctly for pass-through the AOC messages will never
|
||||
make it out the pri. The same goes with SIP, the 'snom_aoc_enabled' option
|
||||
must be configured before messages can successfully be set to the endpoint.
|
||||
|
||||
----- AOC-D Message Generation
|
||||
AOC-D message generation can happen anytime throughout the call. This
|
||||
message type is very straight forward.
|
||||
|
||||
Example: AOCMessage action generating AOC-D currency message with Success
|
||||
response.
|
||||
|
||||
Action: AOCMessage
|
||||
Channel: DAHDI/i1/1111-1
|
||||
MsgType: d
|
||||
ChargeType: Currency
|
||||
CurrencyAmount: 16
|
||||
CurrencyName: USD
|
||||
CurrencyMultiplier: OneThousandth
|
||||
AOCBillingId: Normal
|
||||
ActionID: 1234
|
||||
|
||||
Response: Success
|
||||
ActionID: 1234
|
||||
Message: AOC Message successfully queued on channel
|
||||
|
||||
----- AOC-E Message Generation
|
||||
AOC-E messages are sent during call termination and represent the final charge
|
||||
total for the call. Since Asterisk call termination results in the channel
|
||||
being destroyed, it is currently not possible for the AOCMessage AMI action to
|
||||
be used to send the final AOC-E message on call hangup. There is however a
|
||||
work around for this issue that can be used for Dahdi channels. By default
|
||||
chan_dahdi saves any AOC-E message it receives from Asterisk during a call and
|
||||
waits to deliver that message during call termination. If multiple AOC-E messages
|
||||
are received from Asterisk on the same Dahdi channel, only the last message received
|
||||
is stored for delivery. This means that each new AOC-E message received on the
|
||||
channel overrides the previous one. Knowing this the final AOC-E message can be
|
||||
continually updated on a Dahdi channel until call termination occurs allowing
|
||||
the last update to be sent on hangup. This method is only as accurate as the
|
||||
intervals in which it is updated, but allows some form of AOC-E to be generated.
|
||||
|
||||
Example: AOCMessage action generating AOC-E unit message with Success response.
|
||||
|
||||
Action: AOCMessage
|
||||
Channel: DAHDI/i1/1111-1
|
||||
MsgType: e
|
||||
ChargeType: Unit
|
||||
UnitAmount(0): 111
|
||||
UnitType(0): 6
|
||||
UnitAmount(1): 222
|
||||
UnitType(1): 5
|
||||
UnitAmount(2): 333
|
||||
UnitType(3): 4
|
||||
UnitAmount(4): 444
|
||||
AOCBillingId: Normal
|
||||
ActionID: 1234
|
||||
|
||||
Response: Success
|
||||
ActionID: 1234
|
||||
Message: AOC Message successfully queued on channel
|
|
@ -0,0 +1,584 @@
|
|||
/*
|
||||
* 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 Advice of Charge encode and decode routines
|
||||
*
|
||||
* \author David Vossel <dvossel@digium.com>
|
||||
*/
|
||||
|
||||
#ifndef _AST_AOC_H_
|
||||
#define _AST_AOC_H_
|
||||
|
||||
#include "asterisk/channel.h"
|
||||
|
||||
#define AOC_CURRENCY_NAME_SIZE (10 + 1)
|
||||
|
||||
/*! \brief Defines the currency multiplier for an aoc message. */
|
||||
enum ast_aoc_currency_multiplier {
|
||||
AST_AOC_MULT_ONETHOUSANDTH = 1,
|
||||
AST_AOC_MULT_ONEHUNDREDTH,
|
||||
AST_AOC_MULT_ONETENTH,
|
||||
AST_AOC_MULT_ONE,
|
||||
AST_AOC_MULT_TEN,
|
||||
AST_AOC_MULT_HUNDRED,
|
||||
AST_AOC_MULT_THOUSAND,
|
||||
AST_AOC_MULT_NUM_ENTRIES, /* must remain the last item in enum, this is not a valid type */
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Defines the billing id options for an aoc message.
|
||||
* \note AOC-D is limited to NORMAL, REVERSE_CHARGE, and CREDIT_CARD.
|
||||
*/
|
||||
enum ast_aoc_billing_id {
|
||||
AST_AOC_BILLING_NA = 0,
|
||||
AST_AOC_BILLING_NORMAL,
|
||||
AST_AOC_BILLING_REVERSE_CHARGE,
|
||||
AST_AOC_BILLING_CREDIT_CARD,
|
||||
AST_AOC_BILLING_CALL_FWD_UNCONDITIONAL,
|
||||
AST_AOC_BILLING_CALL_FWD_BUSY,
|
||||
AST_AOC_BILLING_CALL_FWD_NO_REPLY,
|
||||
AST_AOC_BILLING_CALL_DEFLECTION,
|
||||
AST_AOC_BILLING_CALL_TRANSFER,
|
||||
AST_AOC_BILLING_NUM_ENTRIES /* must remain the last item in enum, not a valid billing id */
|
||||
};
|
||||
|
||||
enum ast_aoc_type {
|
||||
AST_AOC_REQUEST = 0,
|
||||
AST_AOC_S,
|
||||
AST_AOC_D,
|
||||
AST_AOC_E, /* aoc-e must remain the last item in this enum */
|
||||
};
|
||||
|
||||
enum ast_aoc_charge_type {
|
||||
AST_AOC_CHARGE_NA = 0,
|
||||
AST_AOC_CHARGE_FREE,
|
||||
AST_AOC_CHARGE_CURRENCY,
|
||||
AST_AOC_CHARGE_UNIT, /* unit must remain the last item in enum */
|
||||
};
|
||||
|
||||
enum ast_aoc_request {
|
||||
AST_AOC_REQUEST_S = (1 << 0),
|
||||
AST_AOC_REQUEST_D = (1 << 1),
|
||||
AST_AOC_REQUEST_E = (1 << 2),
|
||||
};
|
||||
|
||||
enum ast_aoc_total_type {
|
||||
AST_AOC_TOTAL = 0,
|
||||
AST_AOC_SUBTOTAL = 1,
|
||||
};
|
||||
|
||||
enum ast_aoc_time_scale {
|
||||
AST_AOC_TIME_SCALE_HUNDREDTH_SECOND,
|
||||
AST_AOC_TIME_SCALE_TENTH_SECOND,
|
||||
AST_AOC_TIME_SCALE_SECOND,
|
||||
AST_AOC_TIME_SCALE_TEN_SECOND,
|
||||
AST_AOC_TIME_SCALE_MINUTE,
|
||||
AST_AOC_TIME_SCALE_HOUR,
|
||||
AST_AOC_TIME_SCALE_DAY,
|
||||
};
|
||||
|
||||
struct ast_aoc_time {
|
||||
/*! LengthOfTimeUnit (Not valid if length is zero.) */
|
||||
uint32_t length;
|
||||
uint16_t scale;
|
||||
};
|
||||
|
||||
struct ast_aoc_duration_rate {
|
||||
uint32_t amount;
|
||||
uint32_t time;
|
||||
/*! Not present if the granularity time is zero. */
|
||||
uint32_t granularity_time;
|
||||
|
||||
uint16_t multiplier;
|
||||
uint16_t time_scale;
|
||||
uint16_t granularity_time_scale;
|
||||
|
||||
/*! Name of currency involved. Null terminated. */
|
||||
char currency_name[AOC_CURRENCY_NAME_SIZE];
|
||||
|
||||
/*!
|
||||
* \brief Charging interval type
|
||||
* \details
|
||||
* continuousCharging(0),
|
||||
* stepFunction(1)
|
||||
*/
|
||||
uint8_t charging_type;
|
||||
};
|
||||
|
||||
enum ast_aoc_volume_unit {
|
||||
AST_AOC_VOLUME_UNIT_OCTET,
|
||||
AST_AOC_VOLUME_UNIT_SEGMENT,
|
||||
AST_AOC_VOLUME_UNIT_MESSAGE,
|
||||
};
|
||||
|
||||
struct ast_aoc_volume_rate {
|
||||
uint32_t amount;
|
||||
uint16_t multiplier;
|
||||
uint16_t volume_unit;
|
||||
char currency_name[AOC_CURRENCY_NAME_SIZE];
|
||||
};
|
||||
|
||||
struct ast_aoc_flat_rate {
|
||||
uint32_t amount;
|
||||
uint16_t multiplier;
|
||||
/*! Name of currency involved. Null terminated. */
|
||||
char currency_name[AOC_CURRENCY_NAME_SIZE];
|
||||
};
|
||||
|
||||
enum ast_aoc_s_charged_item {
|
||||
AST_AOC_CHARGED_ITEM_NA,
|
||||
AST_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT,
|
||||
AST_AOC_CHARGED_ITEM_BASIC_COMMUNICATION,
|
||||
AST_AOC_CHARGED_ITEM_CALL_ATTEMPT,
|
||||
AST_AOC_CHARGED_ITEM_CALL_SETUP,
|
||||
AST_AOC_CHARGED_ITEM_USER_USER_INFO,
|
||||
AST_AOC_CHARGED_ITEM_SUPPLEMENTARY_SERVICE,
|
||||
};
|
||||
|
||||
enum ast_aoc_s_rate_type {
|
||||
AST_AOC_RATE_TYPE_NA,
|
||||
AST_AOC_RATE_TYPE_FREE,
|
||||
AST_AOC_RATE_TYPE_FREE_FROM_BEGINNING,
|
||||
AST_AOC_RATE_TYPE_DURATION,
|
||||
AST_AOC_RATE_TYPE_FLAT,
|
||||
AST_AOC_RATE_TYPE_VOLUME,
|
||||
AST_AOC_RATE_TYPE_SPECIAL_CODE,
|
||||
};
|
||||
|
||||
struct ast_aoc_s_entry {
|
||||
uint16_t charged_item;
|
||||
uint16_t rate_type;
|
||||
|
||||
/*! \brief Charge rate being applied. */
|
||||
union {
|
||||
struct ast_aoc_duration_rate duration;
|
||||
struct ast_aoc_flat_rate flat;
|
||||
struct ast_aoc_volume_rate volume;
|
||||
uint16_t special_code; /* 1...10 */
|
||||
} rate;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ast_aoc_unit_entry {
|
||||
char valid_amount;
|
||||
unsigned int amount;
|
||||
char valid_type;
|
||||
unsigned int type; /* 1 - 16 by ETSI standard */
|
||||
};
|
||||
|
||||
enum AST_AOC_CHARGING_ASSOCIATION {
|
||||
AST_AOC_CHARGING_ASSOCIATION_NA,
|
||||
AST_AOC_CHARGING_ASSOCIATION_NUMBER,
|
||||
AST_AOC_CHARGING_ASSOCIATION_ID,
|
||||
};
|
||||
struct ast_aoc_charging_association_number {
|
||||
uint8_t plan;
|
||||
char number[32];
|
||||
} __attribute__((packed));
|
||||
struct ast_aoc_charging_association {
|
||||
union {
|
||||
int32_t id;
|
||||
struct ast_aoc_charging_association_number number;
|
||||
} charge;
|
||||
/*! \see enum AST_AOC_CHARGING_ASSOCIATION */
|
||||
uint8_t charging_type;
|
||||
} __attribute__((packed));
|
||||
|
||||
/*! \brief AOC Payload Header. Holds all the encoded AOC data to pass on the wire */
|
||||
struct ast_aoc_encoded;
|
||||
|
||||
/*! \brief Decoded AOC data. This value is used to set all the values in an AOC message before encoding.*/
|
||||
struct ast_aoc_decoded;
|
||||
|
||||
/*!
|
||||
* \brief creates a ast_aoc_decode object of a specific message type
|
||||
* \since 1.8
|
||||
*
|
||||
* \param msg_type AOC-D, AOC-E, or AOC Request
|
||||
* \param charge_type this is ignored if message type is not AOC-D or AOC-E.
|
||||
* \param requests flags. This defines the types of AOC requested. This
|
||||
* field should only be set when the message type is AOC Request,
|
||||
* the value is ignored otherwise.
|
||||
*
|
||||
* \retval heap allocated ast_aoc_decoded object ptr on success
|
||||
* \retval NULL failure
|
||||
*/
|
||||
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);
|
||||
|
||||
|
||||
/*! \brief free an ast_aoc_decoded object */
|
||||
void *ast_aoc_destroy_decoded(struct ast_aoc_decoded *decoded);
|
||||
|
||||
/*! \brief free an ast_aoc_encoded object */
|
||||
void *ast_aoc_destroy_encoded(struct ast_aoc_encoded *encoded);
|
||||
|
||||
/*!
|
||||
* \brief decodes an encoded aoc payload.
|
||||
* \since 1.8
|
||||
*
|
||||
* \param encoded the encoded payload to decode.
|
||||
* \param size total size of encoded payload
|
||||
* \param chan ast channel, Optional for DEBUG output purposes
|
||||
*
|
||||
* \retval heap allocated ast_aoc_decoded object ptr on success
|
||||
* \retval NULL failure
|
||||
*/
|
||||
struct ast_aoc_decoded *ast_aoc_decode(struct ast_aoc_encoded *encoded, size_t size, struct ast_channel *chan);
|
||||
|
||||
/*!
|
||||
* \brief encodes a decoded aoc structure so it can be passed on the wire
|
||||
* \since 1.8
|
||||
*
|
||||
* \param decoded the decoded struct to be encoded
|
||||
* \param out_size output parameter representing size of encoded data
|
||||
* \param chan ast channel, Optional for DEBUG output purposes
|
||||
*
|
||||
* \retval pointer to encoded data
|
||||
* \retval NULL failure
|
||||
*/
|
||||
struct ast_aoc_encoded *ast_aoc_encode(struct ast_aoc_decoded *decoded, size_t *out_size, struct ast_channel *chan);
|
||||
|
||||
/*!
|
||||
* \brief Sets the type of total for a AOC-D message
|
||||
* \since 1.8
|
||||
*
|
||||
* \param decoded ast_aoc_decoded struct to set values on
|
||||
* \param type total type: TOTAL or SUBTOTAL
|
||||
*
|
||||
* \note If this value is not set, the default for the message is TOTAL
|
||||
*
|
||||
* \retval 0 success
|
||||
*/
|
||||
int ast_aoc_set_total_type(struct ast_aoc_decoded *decoded, const enum ast_aoc_total_type type);
|
||||
|
||||
/*!
|
||||
* \brief Sets the currency values for a AOC-D or AOC-E message
|
||||
* \since 1.8
|
||||
*
|
||||
* \param decoded ast_aoc_decoded struct to set values on
|
||||
* \param amount currency amount REQUIRED
|
||||
* \param multiplier currency multiplier REQUIRED, 0 or undefined value defaults to AST_AOC_MULT_ONE.
|
||||
* \param name currency name OPTIONAL
|
||||
*
|
||||
* \retval 0 success
|
||||
*/
|
||||
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);
|
||||
|
||||
/*!
|
||||
* \brief Adds a unit entry into the list of units
|
||||
* \since 1.8
|
||||
*
|
||||
* \param decoded ast_aoc_decoded struct to set values on
|
||||
* \param amount_is_present set this if the number of units is actually present.
|
||||
* \param amount number of units
|
||||
* \param type_is_present set this if the type value is present
|
||||
* \param type unit type
|
||||
*
|
||||
* \note If neither the amount nor the type is present, the entry will
|
||||
* not be added.
|
||||
*
|
||||
* \retval 0 success
|
||||
*/
|
||||
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);
|
||||
|
||||
/*!
|
||||
* \brief set the billing id for a AOC-D or AST_AOC_E message
|
||||
* \since 1.8
|
||||
*
|
||||
* \param decoded ast_aoc_decoded struct to set values on
|
||||
* \param id billing id
|
||||
*
|
||||
* \retval 0 success
|
||||
*/
|
||||
int ast_aoc_set_billing_id(struct ast_aoc_decoded *decoded, const enum ast_aoc_billing_id id);
|
||||
|
||||
/*!
|
||||
* \brief set the charging association id for an AST_AOC_E message
|
||||
* \since 1.8
|
||||
*
|
||||
* \param decoded ast_aoc_decoded struct to set values on
|
||||
* \param id charging association identifier
|
||||
*
|
||||
* \note If the association number was set, this will override that value. Only the id OR the
|
||||
* number can be set at a time, not both.
|
||||
*
|
||||
* \retval 0 success
|
||||
*/
|
||||
int ast_aoc_set_association_id(struct ast_aoc_decoded *decoded, const int id);
|
||||
|
||||
/*!
|
||||
* \brief set the charging accociation number for an AOC-E message
|
||||
* \since 1.8
|
||||
*
|
||||
* \param decoded ast_aoc_decoded struct to set values on
|
||||
* \param num charging association number
|
||||
* \param plan charging association number plan and type-of-number fields
|
||||
*
|
||||
* \note If the association id was set, this will override that value. Only the id OR the
|
||||
* number can be set at a time, not both.
|
||||
*
|
||||
* \retval 0 success
|
||||
*/
|
||||
int ast_aoc_set_association_number(struct ast_aoc_decoded *decoded, const char *num, uint8_t plan);
|
||||
|
||||
/*!
|
||||
* \brief Mark the AST_AOC_REQUEST message as a termination request.
|
||||
* \since 1.8
|
||||
*
|
||||
* \param decoded ast_aoc_decoded struct to set values on
|
||||
*
|
||||
* \note A termination request indicates that the call has terminated,
|
||||
* but that the other side is waiting for a short period of time before
|
||||
* hanging up so it can get the final AOC-E message.
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*/
|
||||
int ast_aoc_set_termination_request(struct ast_aoc_decoded *decoded);
|
||||
|
||||
/*!
|
||||
* \brief Add AOC-S duration rate entry
|
||||
* \since 1.8
|
||||
*
|
||||
* \param decoded aoc decoded object to add entry to
|
||||
* \param charged_item ast_aoc_s_charged_item
|
||||
* \param amount currency amount
|
||||
* \param multiplier currency multiplier
|
||||
* \param currency_name truncated after 10 characters
|
||||
* \param time
|
||||
* \param time_scale from ast_aoc_time_scale enum
|
||||
* \param granularity_time (optional, set to 0 if not present);
|
||||
* \param granularity_time_scale (optional, set to 0 if not present);
|
||||
* \param step_function set to 1 if this is to use a step function, 0 if continuious
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*/
|
||||
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);
|
||||
|
||||
/*!
|
||||
* \brief Add AOC-S flat rate entry
|
||||
* \since 1.8
|
||||
*
|
||||
* \param decoded aoc decoded object to add entry to
|
||||
* \param charged_item ast_aoc_s_charged_item
|
||||
* \param amount currency amount
|
||||
* \param multiplier currency multiplier
|
||||
* \param currency_name truncated after 10 characters
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*/
|
||||
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);
|
||||
|
||||
/*!
|
||||
* \brief Add AOC-S volume rate entry
|
||||
* \since 1.8
|
||||
*
|
||||
* \param decoded aoc decoded object to add entry to
|
||||
* \param charged_item ast_aoc_s_charged_item
|
||||
* \param volume_unit from ast_aoc_volume_unit enum
|
||||
* \param amount currency amount
|
||||
* \param multiplier currency multiplier
|
||||
* \param currency_name truncated after 10 characters
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*/
|
||||
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);
|
||||
|
||||
/*!
|
||||
* \brief Add AOC-S special rate entry
|
||||
* \since 1.8
|
||||
*
|
||||
* \param decoded aoc decoded object to add entry to
|
||||
* \param charged_item ast_aoc_s_charged_item
|
||||
* \param code special charging code
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*/
|
||||
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);
|
||||
|
||||
/*!
|
||||
* \brief Add AOC-S indicating charge item is free
|
||||
* \since 1.8
|
||||
*
|
||||
* \param decoded aoc decoded object to add entry to
|
||||
* \param charged_item ast_aoc_s_charged_item
|
||||
* \param from_beginning TRUE if the rate is free from beginning.
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*/
|
||||
int ast_aoc_s_add_rate_free(struct ast_aoc_decoded *decoded,
|
||||
enum ast_aoc_s_charged_item charged_item, int from_beginning);
|
||||
|
||||
/*!
|
||||
* \brief Add AOC-S entry indicating charge item is not available
|
||||
* \since 1.8
|
||||
*
|
||||
* \param decoded aoc decoded object to add entry to
|
||||
* \param charged_item ast_aoc_s_charged_item
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*/
|
||||
int ast_aoc_s_add_rate_na(struct ast_aoc_decoded *decoded,
|
||||
enum ast_aoc_s_charged_item charged_item);
|
||||
|
||||
/*!
|
||||
* \brief Add AOC-S special arrangement entry
|
||||
* \since 1.8
|
||||
*
|
||||
* \param decoded aoc decoded object to add entry to
|
||||
* \param code special arrangement code
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*/
|
||||
int ast_aoc_s_add_special_arrangement(struct ast_aoc_decoded *decoded,
|
||||
unsigned int code);
|
||||
|
||||
/*!
|
||||
* \brief Convert decoded aoc msg to string representation
|
||||
* \since 1.8
|
||||
*
|
||||
* \param decoded ast_aoc_decoded struct to convert to string
|
||||
* \param msg dynamic heap allocated ast_str object to store string representation in
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*/
|
||||
int ast_aoc_decoded2str(const struct ast_aoc_decoded *decoded, struct ast_str **msg);
|
||||
|
||||
/*! \brief generate AOC manager event for an AOC-S, AOC-D, or AOC-E msg */
|
||||
int ast_aoc_manager_event(const struct ast_aoc_decoded *decoded, struct ast_channel *chan);
|
||||
|
||||
/*! \brief get the message type, AOC-D, AOC-E, or AOC Request */
|
||||
enum ast_aoc_type ast_aoc_get_msg_type(struct ast_aoc_decoded *decoded);
|
||||
|
||||
/*! \brief get the charging type for an AOC-D or AOC-E message */
|
||||
enum ast_aoc_charge_type ast_aoc_get_charge_type(struct ast_aoc_decoded *decoded);
|
||||
|
||||
/*! \brief get the types of AOC requested for when message type is AOC Request */
|
||||
enum ast_aoc_request ast_aoc_get_request(struct ast_aoc_decoded *decoded);
|
||||
|
||||
/*! \brief get the type of total for a AOC-D message */
|
||||
enum ast_aoc_total_type ast_aoc_get_total_type(struct ast_aoc_decoded *decoded);
|
||||
|
||||
/*! \brief get the currency amount for AOC-D and AOC-E messages*/
|
||||
unsigned int ast_aoc_get_currency_amount(struct ast_aoc_decoded *decoded);
|
||||
|
||||
/*! \brief get the number rates associated with an AOC-S message */
|
||||
unsigned int ast_aoc_s_get_count(struct ast_aoc_decoded *decoded);
|
||||
|
||||
/*!
|
||||
* \brief get a specific AOC-S rate entry.
|
||||
* \since 1.8
|
||||
*
|
||||
* \note This can be used in conjunction with ast_aoc_s_get_count to create
|
||||
* a unit entry iterator.
|
||||
*/
|
||||
const struct ast_aoc_s_entry *ast_aoc_s_get_rate_info(struct ast_aoc_decoded *decoded, unsigned int entry_number);
|
||||
|
||||
/*! \brief get the number of unit entries for AOC-D and AOC-E messages*/
|
||||
unsigned int ast_aoc_get_unit_count(struct ast_aoc_decoded *decoded);
|
||||
|
||||
/*!
|
||||
* \brief get a specific unit entry.
|
||||
* \since 1.8
|
||||
*
|
||||
* \note This can be used in conjunction with ast_aoc_get_unit_count to create
|
||||
* a unit entry iterator.
|
||||
*/
|
||||
const struct ast_aoc_unit_entry *ast_aoc_get_unit_info(struct ast_aoc_decoded *decoded, unsigned int entry_number);
|
||||
|
||||
/*! \brief get the currency multiplier for AOC-D and AOC-E messages */
|
||||
enum ast_aoc_currency_multiplier ast_aoc_get_currency_multiplier(struct ast_aoc_decoded *decoded);
|
||||
|
||||
/*! \brief get the currency multiplier for AOC-D and AOC-E messages in decimal format */
|
||||
const char *ast_aoc_get_currency_multiplier_decimal(struct ast_aoc_decoded *decoded);
|
||||
|
||||
/*! \brief get the currency name for AOC-D and AOC-E messages*/
|
||||
const char *ast_aoc_get_currency_name(struct ast_aoc_decoded *decoded);
|
||||
|
||||
/*! \brief get the billing id for AOC-D and AOC-E messages*/
|
||||
enum ast_aoc_billing_id ast_aoc_get_billing_id(struct ast_aoc_decoded *decoded);
|
||||
|
||||
/*! \brief get the charging association info for AOC-E messages*/
|
||||
const struct ast_aoc_charging_association *ast_aoc_get_association_info(struct ast_aoc_decoded *decoded);
|
||||
|
||||
/*!
|
||||
* \brief get whether or not the AST_AOC_REQUEST message as a termination request.
|
||||
* \since 1.8
|
||||
*
|
||||
* \note a termination request indicates that the call has terminated,
|
||||
* but that the other side is waiting for a short period of time
|
||||
* before hanging up so it can get the final AOC-E message.
|
||||
*
|
||||
* \param decoded ast_aoc_decoded struct to get values on
|
||||
*
|
||||
* \retval 0 not a termination request
|
||||
* \retval 1 is a termination request
|
||||
*/
|
||||
int ast_aoc_get_termination_request(struct ast_aoc_decoded *decoded);
|
||||
|
||||
/*!
|
||||
* \brief test aoc encode decode routines.
|
||||
* \since 1.8
|
||||
*
|
||||
* \note This function verifies that a decoded message matches itself after
|
||||
* the encode decode routine.
|
||||
*/
|
||||
int ast_aoc_test_encode_decode_match(struct ast_aoc_decoded *decoded);
|
||||
|
||||
/*! \brief enable aoc cli options */
|
||||
int ast_aoc_cli_init(void);
|
||||
|
||||
#endif /* _AST_AOC_H_ */
|
|
@ -327,6 +327,7 @@ enum ast_control_frame_type {
|
|||
AST_CONTROL_CC = 25, /*!< Indication that Call completion service is possible */
|
||||
AST_CONTROL_SRCCHANGE = 26, /*!< Media source has changed and requires a new RTP SSRC */
|
||||
AST_CONTROL_READ_ACTION = 27, /*!< Tell ast_read to take a specific action */
|
||||
AST_CONTROL_AOC = 28, /*!< Advice of Charge with encoded generic AOC payload */
|
||||
};
|
||||
|
||||
enum ast_frame_read_action {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -142,6 +142,7 @@ int daemon(int, int); /* defined in libresolv of all places */
|
|||
#include "asterisk/poll-compat.h"
|
||||
#include "asterisk/ccss.h"
|
||||
#include "asterisk/test.h"
|
||||
#include "asterisk/aoc.h"
|
||||
|
||||
#include "../defaults.h"
|
||||
|
||||
|
@ -3602,6 +3603,8 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
#endif
|
||||
|
||||
ast_aoc_cli_init();
|
||||
|
||||
ast_makesocket();
|
||||
sigemptyset(&sigs);
|
||||
sigaddset(&sigs, SIGHUP);
|
||||
|
|
|
@ -3796,6 +3796,7 @@ static int attribute_const is_visible_indication(enum ast_control_frame_type con
|
|||
case _XXX_AST_CONTROL_T38:
|
||||
case AST_CONTROL_CC:
|
||||
case AST_CONTROL_READ_ACTION:
|
||||
case AST_CONTROL_AOC:
|
||||
break;
|
||||
|
||||
case AST_CONTROL_CONGESTION:
|
||||
|
@ -3941,6 +3942,7 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
|
|||
case AST_CONTROL_REDIRECTING:
|
||||
case AST_CONTROL_CC:
|
||||
case AST_CONTROL_READ_ACTION:
|
||||
case AST_CONTROL_AOC:
|
||||
/* Nothing left to do for these. */
|
||||
res = 0;
|
||||
break;
|
||||
|
@ -6003,6 +6005,9 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct
|
|||
int bridge_exit = 0;
|
||||
|
||||
switch (f->subclass.integer) {
|
||||
case AST_CONTROL_AOC:
|
||||
ast_indicate_data(other, f->subclass.integer, f->data.ptr, f->datalen);
|
||||
break;
|
||||
case AST_CONTROL_REDIRECTING:
|
||||
if (ast_channel_redirecting_macro(who, other, f, other == c0, 1)) {
|
||||
ast_indicate_data(other, f->subclass.integer, f->data.ptr, f->datalen);
|
||||
|
|
|
@ -3208,6 +3208,7 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
|
|||
}
|
||||
ast_indicate_data(other, f->subclass.integer, f->data.ptr, f->datalen);
|
||||
break;
|
||||
case AST_CONTROL_AOC:
|
||||
case AST_CONTROL_HOLD:
|
||||
case AST_CONTROL_UNHOLD:
|
||||
ast_indicate_data(other, f->subclass.integer, f->data.ptr, f->datalen);
|
||||
|
|
338
main/manager.c
338
main/manager.c
|
@ -74,6 +74,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
#include "asterisk/astobj2.h"
|
||||
#include "asterisk/features.h"
|
||||
#include "asterisk/security_events.h"
|
||||
#include "asterisk/aoc.h"
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<manager name="Ping" language="en_US">
|
||||
|
@ -694,6 +695,112 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
For success returns, the module revision number is included.</para>
|
||||
</description>
|
||||
</manager>
|
||||
<manager name="AOCMessage" language="en_US">
|
||||
<synopsis>
|
||||
Generate an Advice of Charge message on a channel.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
|
||||
<parameter name="Channel" required="true">
|
||||
<para>Channel name to generate the AOC message on.</para>
|
||||
</parameter>
|
||||
<parameter name="ChannelPrefix">
|
||||
<para>Partial channel prefix. By using this option one can match the beginning part
|
||||
of a channel name without having to put the entire name in. For example
|
||||
if a channel name is SIP/snom-00000001 and this value is set to SIP/snom, then
|
||||
that channel matches and the message will be sent. Note however that only
|
||||
the first matched channel has the message sent on it. </para>
|
||||
</parameter>
|
||||
<parameter name="MsgType" required="true">
|
||||
<para>Defines what type of AOC message to create, AOC-D or AOC-E</para>
|
||||
<enumlist>
|
||||
<enum name="D" />
|
||||
<enum name="E" />
|
||||
</enumlist>
|
||||
</parameter>
|
||||
<parameter name="ChargeType" required="true">
|
||||
<para>Defines what kind of charge this message represents.</para>
|
||||
<enumlist>
|
||||
<enum name="NA" />
|
||||
<enum name="FREE" />
|
||||
<enum name="Currency" />
|
||||
<enum name="Unit" />
|
||||
</enumlist>
|
||||
</parameter>
|
||||
<parameter name="UnitAmount(0)">
|
||||
<para>This represents the amount of units charged. The ETSI AOC standard specifies that
|
||||
this value along with the optional UnitType value are entries in a list. To accommodate this
|
||||
these values take an index value starting at 0 which can be used to generate this list of
|
||||
unit entries. For Example, If two unit entires were required this could be achieved by setting the
|
||||
paramter UnitAmount(0)=1234 and UnitAmount(1)=5678. Note that UnitAmount at index 0 is
|
||||
required when ChargeType=Unit, all other entries in the list are optional.
|
||||
</para>
|
||||
</parameter>
|
||||
<parameter name="UnitType(0)">
|
||||
<para>Defines the type of unit. ETSI AOC standard specifies this as an integer
|
||||
value between 1 and 16, but this value is left open to accept any positive
|
||||
integer. Like the UnitAmount parameter, this value represents a list entry
|
||||
and has an index parameter that starts at 0.
|
||||
</para>
|
||||
</parameter>
|
||||
<parameter name="CurrencyName">
|
||||
<para>Specifies the currency's name. Note that this value is truncated after 10 characters.</para>
|
||||
</parameter>
|
||||
<parameter name="CurrencyAmount">
|
||||
<para>Specifies the charge unit amount as a positive integer. This value is required
|
||||
when ChargeType==Currency.</para>
|
||||
</parameter>
|
||||
<parameter name="CurrencyMultiplier">
|
||||
<para>Specifies the currency multiplier. This value is required when ChargeType==Currency.</para>
|
||||
<enumlist>
|
||||
<enum name="OneThousandth" />
|
||||
<enum name="OneHundredth" />
|
||||
<enum name="OneTenth" />
|
||||
<enum name="One" />
|
||||
<enum name="Ten" />
|
||||
<enum name="Hundred" />
|
||||
<enum name="Thousand" />
|
||||
</enumlist>
|
||||
</parameter>
|
||||
<parameter name="TotalType" default="Total">
|
||||
<para>Defines what kind of AOC-D total is represented.</para>
|
||||
<enumlist>
|
||||
<enum name="Total" />
|
||||
<enum name="SubTotal" />
|
||||
</enumlist>
|
||||
</parameter>
|
||||
<parameter name="AOCBillingId">
|
||||
<para>Represents a billing ID associated with an AOC-D or AOC-E message. Note
|
||||
that only the first 3 items of the enum are valid AOC-D billing IDs</para>
|
||||
<enumlist>
|
||||
<enum name="Normal" />
|
||||
<enum name="ReverseCharge" />
|
||||
<enum name="CreditCard" />
|
||||
<enum name="CallFwdUnconditional" />
|
||||
<enum name="CallFwdBusy" />
|
||||
<enum name="CallFwdNoReply" />
|
||||
<enum name="CallDeflection" />
|
||||
<enum name="CallTransfer" />
|
||||
</enumlist>
|
||||
</parameter>
|
||||
<parameter name="ChargingAssociationId">
|
||||
<para>Charging association identifier. This is optional for AOC-E and can be
|
||||
set to any value between -32768 and 32767</para>
|
||||
</parameter>
|
||||
<parameter name="ChargingAssociationNumber">
|
||||
<para>Represents the charging association party number. This value is optional
|
||||
for AOC-E.</para>
|
||||
</parameter>
|
||||
<parameter name="ChargingAssociationPlan">
|
||||
<para>Integer representing the charging plan associated with the ChargingAssociationNumber.
|
||||
The value is bits 7 through 1 of the Q.931 octet containing the type-of-number and
|
||||
numbering-plan-identification fields.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Generates an AOC-D or AOC-E message on a channel.</para>
|
||||
</description>
|
||||
</manager>
|
||||
***/
|
||||
|
||||
enum error_type {
|
||||
|
@ -3396,6 +3503,236 @@ static void *fast_originate(void *data)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int aocmessage_get_unit_entry(const struct message *m, struct ast_aoc_unit_entry *entry, unsigned int entry_num)
|
||||
{
|
||||
const char *unitamount;
|
||||
const char *unittype;
|
||||
struct ast_str *str = ast_str_alloca(32);
|
||||
|
||||
memset(entry, 0, sizeof(*entry));
|
||||
|
||||
ast_str_set(&str, 0, "UnitAmount(%u)", entry_num);
|
||||
unitamount = astman_get_header(m, ast_str_buffer(str));
|
||||
|
||||
ast_str_set(&str, 0, "UnitType(%u)", entry_num);
|
||||
unittype = astman_get_header(m, ast_str_buffer(str));
|
||||
|
||||
if (!ast_strlen_zero(unitamount) && (sscanf(unitamount, "%30u", &entry->amount) == 1)) {
|
||||
entry->valid_amount = 1;
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(unittype) && sscanf(unittype, "%30u", &entry->type) == 1) {
|
||||
entry->valid_type = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int action_aocmessage(struct mansession *s, const struct message *m)
|
||||
{
|
||||
const char *channel = astman_get_header(m, "Channel");
|
||||
const char *pchannel = astman_get_header(m, "ChannelPrefix");
|
||||
const char *msgtype = astman_get_header(m, "MsgType");
|
||||
const char *chargetype = astman_get_header(m, "ChargeType");
|
||||
const char *currencyname = astman_get_header(m, "CurrencyName");
|
||||
const char *currencyamount = astman_get_header(m, "CurrencyAmount");
|
||||
const char *mult = astman_get_header(m, "CurrencyMultiplier");
|
||||
const char *totaltype = astman_get_header(m, "TotalType");
|
||||
const char *aocbillingid = astman_get_header(m, "AOCBillingId");
|
||||
const char *association_id= astman_get_header(m, "ChargingAssociationId");
|
||||
const char *association_num = astman_get_header(m, "ChargingAssociationNumber");
|
||||
const char *association_plan = astman_get_header(m, "ChargingAssociationPlan");
|
||||
|
||||
enum ast_aoc_type _msgtype;
|
||||
enum ast_aoc_charge_type _chargetype;
|
||||
enum ast_aoc_currency_multiplier _mult = AST_AOC_MULT_ONE;
|
||||
enum ast_aoc_total_type _totaltype = AST_AOC_TOTAL;
|
||||
enum ast_aoc_billing_id _billingid = AST_AOC_BILLING_NA;
|
||||
unsigned int _currencyamount = 0;
|
||||
int _association_id = 0;
|
||||
unsigned int _association_plan = 0;
|
||||
struct ast_channel *chan = NULL;
|
||||
|
||||
struct ast_aoc_decoded *decoded = NULL;
|
||||
struct ast_aoc_encoded *encoded = NULL;
|
||||
size_t encoded_size = 0;
|
||||
|
||||
if (ast_strlen_zero(channel) && ast_strlen_zero(pchannel)) {
|
||||
astman_send_error(s, m, "Channel and PartialChannel are not specified. Specify at least one of these.");
|
||||
goto aocmessage_cleanup;
|
||||
}
|
||||
|
||||
if (!(chan = ast_channel_get_by_name(channel)) && !ast_strlen_zero(pchannel)) {
|
||||
chan = ast_channel_get_by_name_prefix(pchannel, strlen(pchannel));
|
||||
}
|
||||
|
||||
if (!chan) {
|
||||
astman_send_error(s, m, "No such channel");
|
||||
goto aocmessage_cleanup;
|
||||
}
|
||||
|
||||
if (ast_strlen_zero(msgtype) || (strcasecmp(msgtype, "d") && strcasecmp(msgtype, "e"))) {
|
||||
astman_send_error(s, m, "Invalid MsgType");
|
||||
goto aocmessage_cleanup;
|
||||
}
|
||||
|
||||
if (ast_strlen_zero(chargetype)) {
|
||||
astman_send_error(s, m, "ChargeType not specified");
|
||||
goto aocmessage_cleanup;
|
||||
}
|
||||
|
||||
_msgtype = strcasecmp(msgtype, "d") ? AST_AOC_E : AST_AOC_D;
|
||||
|
||||
if (!strcasecmp(chargetype, "NA")) {
|
||||
_chargetype = AST_AOC_CHARGE_NA;
|
||||
} else if (!strcasecmp(chargetype, "Free")) {
|
||||
_chargetype = AST_AOC_CHARGE_FREE;
|
||||
} else if (!strcasecmp(chargetype, "Currency")) {
|
||||
_chargetype = AST_AOC_CHARGE_CURRENCY;
|
||||
} else if (!strcasecmp(chargetype, "Unit")) {
|
||||
_chargetype = AST_AOC_CHARGE_UNIT;
|
||||
} else {
|
||||
astman_send_error(s, m, "Invalid ChargeType");
|
||||
goto aocmessage_cleanup;
|
||||
}
|
||||
|
||||
if (_chargetype == AST_AOC_CHARGE_CURRENCY) {
|
||||
|
||||
if (ast_strlen_zero(currencyamount) || (sscanf(currencyamount, "%30u", &_currencyamount) != 1)) {
|
||||
astman_send_error(s, m, "Invalid CurrencyAmount, CurrencyAmount is a required when ChargeType is Currency");
|
||||
goto aocmessage_cleanup;
|
||||
}
|
||||
|
||||
if (ast_strlen_zero(mult)) {
|
||||
astman_send_error(s, m, "ChargeMultiplier unspecified, ChargeMultiplier is required when ChargeType is Currency.");
|
||||
goto aocmessage_cleanup;
|
||||
} else if (!strcasecmp(mult, "onethousandth")) {
|
||||
_mult = AST_AOC_MULT_ONETHOUSANDTH;
|
||||
} else if (!strcasecmp(mult, "onehundredth")) {
|
||||
_mult = AST_AOC_MULT_ONEHUNDREDTH;
|
||||
} else if (!strcasecmp(mult, "onetenth")) {
|
||||
_mult = AST_AOC_MULT_ONETENTH;
|
||||
} else if (!strcasecmp(mult, "one")) {
|
||||
_mult = AST_AOC_MULT_ONE;
|
||||
} else if (!strcasecmp(mult, "ten")) {
|
||||
_mult = AST_AOC_MULT_TEN;
|
||||
} else if (!strcasecmp(mult, "hundred")) {
|
||||
_mult = AST_AOC_MULT_HUNDRED;
|
||||
} else if (!strcasecmp(mult, "thousand")) {
|
||||
_mult = AST_AOC_MULT_THOUSAND;
|
||||
} else {
|
||||
astman_send_error(s, m, "Invalid ChargeMultiplier");
|
||||
goto aocmessage_cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* create decoded object and start setting values */
|
||||
if (!(decoded = ast_aoc_create(_msgtype, _chargetype, 0))) {
|
||||
astman_send_error(s, m, "Message Creation Failed");
|
||||
goto aocmessage_cleanup;
|
||||
}
|
||||
|
||||
if (_msgtype == AST_AOC_D) {
|
||||
if (!ast_strlen_zero(totaltype) && !strcasecmp(totaltype, "subtotal")) {
|
||||
_totaltype = AST_AOC_SUBTOTAL;
|
||||
}
|
||||
|
||||
if (ast_strlen_zero(aocbillingid)) {
|
||||
/* ignore this is optional */
|
||||
} else if (!strcasecmp(aocbillingid, "Normal")) {
|
||||
_billingid = AST_AOC_BILLING_NORMAL;
|
||||
} else if (!strcasecmp(aocbillingid, "ReverseCharge")) {
|
||||
_billingid = AST_AOC_BILLING_REVERSE_CHARGE;
|
||||
} else if (!strcasecmp(aocbillingid, "CreditCard")) {
|
||||
_billingid = AST_AOC_BILLING_CREDIT_CARD;
|
||||
} else {
|
||||
astman_send_error(s, m, "Invalid AOC-D AOCBillingId");
|
||||
goto aocmessage_cleanup;
|
||||
}
|
||||
} else {
|
||||
if (ast_strlen_zero(aocbillingid)) {
|
||||
/* ignore this is optional */
|
||||
} else if (!strcasecmp(aocbillingid, "Normal")) {
|
||||
_billingid = AST_AOC_BILLING_NORMAL;
|
||||
} else if (!strcasecmp(aocbillingid, "ReverseCharge")) {
|
||||
_billingid = AST_AOC_BILLING_REVERSE_CHARGE;
|
||||
} else if (!strcasecmp(aocbillingid, "CreditCard")) {
|
||||
_billingid = AST_AOC_BILLING_CREDIT_CARD;
|
||||
} else if (!strcasecmp(aocbillingid, "CallFwdUnconditional")) {
|
||||
_billingid = AST_AOC_BILLING_CALL_FWD_UNCONDITIONAL;
|
||||
} else if (!strcasecmp(aocbillingid, "CallFwdBusy")) {
|
||||
_billingid = AST_AOC_BILLING_CALL_FWD_BUSY;
|
||||
} else if (!strcasecmp(aocbillingid, "CallFwdNoReply")) {
|
||||
_billingid = AST_AOC_BILLING_CALL_FWD_NO_REPLY;
|
||||
} else if (!strcasecmp(aocbillingid, "CallDeflection")) {
|
||||
_billingid = AST_AOC_BILLING_CALL_DEFLECTION;
|
||||
} else if (!strcasecmp(aocbillingid, "CallTransfer")) {
|
||||
_billingid = AST_AOC_BILLING_CALL_TRANSFER;
|
||||
} else {
|
||||
astman_send_error(s, m, "Invalid AOC-E AOCBillingId");
|
||||
goto aocmessage_cleanup;
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(association_id) && (sscanf(association_id, "%30d", &_association_id) != 1)) {
|
||||
astman_send_error(s, m, "Invalid ChargingAssociationId");
|
||||
goto aocmessage_cleanup;
|
||||
}
|
||||
if (!ast_strlen_zero(association_plan) && (sscanf(association_plan, "%30u", &_association_plan) != 1)) {
|
||||
astman_send_error(s, m, "Invalid ChargingAssociationPlan");
|
||||
goto aocmessage_cleanup;
|
||||
}
|
||||
|
||||
if (_association_id) {
|
||||
ast_aoc_set_association_id(decoded, _association_id);
|
||||
} else if (!ast_strlen_zero(association_num)) {
|
||||
ast_aoc_set_association_number(decoded, association_num, _association_plan);
|
||||
}
|
||||
}
|
||||
|
||||
if (_chargetype == AST_AOC_CHARGE_CURRENCY) {
|
||||
ast_aoc_set_currency_info(decoded, _currencyamount, _mult, ast_strlen_zero(currencyname) ? NULL : currencyname);
|
||||
} else if (_chargetype == AST_AOC_CHARGE_UNIT) {
|
||||
struct ast_aoc_unit_entry entry;
|
||||
int i;
|
||||
|
||||
/* multiple unit entries are possible, lets get them all */
|
||||
for (i = 0; i < 32; i++) {
|
||||
if (aocmessage_get_unit_entry(m, &entry, i)) {
|
||||
break; /* that's the end then */
|
||||
}
|
||||
|
||||
ast_aoc_add_unit_entry(decoded, entry.valid_amount, entry.amount, entry.valid_type, entry.type);
|
||||
}
|
||||
|
||||
/* at least one unit entry is required */
|
||||
if (!i) {
|
||||
astman_send_error(s, m, "Invalid UnitAmount(0), At least one valid unit entry is required when ChargeType is set to Unit");
|
||||
goto aocmessage_cleanup;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ast_aoc_set_billing_id(decoded, _billingid);
|
||||
ast_aoc_set_total_type(decoded, _totaltype);
|
||||
|
||||
|
||||
if ((encoded = ast_aoc_encode(decoded, &encoded_size, NULL)) && !ast_indicate_data(chan, AST_CONTROL_AOC, encoded, encoded_size)) {
|
||||
astman_send_ack(s, m, "AOC Message successfully queued on channel");
|
||||
} else {
|
||||
astman_send_error(s, m, "Error encoding AOC message, could not queue onto channel");
|
||||
}
|
||||
|
||||
aocmessage_cleanup:
|
||||
|
||||
ast_aoc_destroy_decoded(decoded);
|
||||
ast_aoc_destroy_encoded(encoded);
|
||||
|
||||
if (chan) {
|
||||
chan = ast_channel_unref(chan);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int action_originate(struct mansession *s, const struct message *m)
|
||||
{
|
||||
const char *name = astman_get_header(m, "Channel");
|
||||
|
@ -5665,6 +6002,7 @@ static int __init_manager(int reload)
|
|||
ast_manager_register_xml("CoreShowChannels", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_coreshowchannels);
|
||||
ast_manager_register_xml("ModuleLoad", EVENT_FLAG_SYSTEM, manager_moduleload);
|
||||
ast_manager_register_xml("ModuleCheck", EVENT_FLAG_SYSTEM, manager_modulecheck);
|
||||
ast_manager_register_xml("AOCMessage", EVENT_FLAG_AOC, action_aocmessage);
|
||||
|
||||
ast_cli_register_multiple(cli_manager, ARRAY_LEN(cli_manager));
|
||||
ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
|
||||
|
|
|
@ -0,0 +1,690 @@
|
|||
/*
|
||||
* 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 encode decode tests
|
||||
*
|
||||
* \author David Vossel <dvossel@digium.com>
|
||||
*
|
||||
* \ingroup tests
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend>TEST_FRAMEWORK</depend>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/test.h"
|
||||
#include "asterisk/aoc.h"
|
||||
|
||||
|
||||
AST_TEST_DEFINE(aoc_event_generation_test)
|
||||
{
|
||||
int res = AST_TEST_PASS;
|
||||
struct ast_aoc_decoded *decoded = NULL;
|
||||
struct ast_str *msg = NULL;
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = "aoc_event_test";
|
||||
info->category = "main/aoc/";
|
||||
info->summary = "Advice of Charge event generation test";
|
||||
info->description =
|
||||
"Creates AOC messages, verify event string matches expected results";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(msg = ast_str_create(1024))) {
|
||||
goto cleanup_aoc_event_test;
|
||||
}
|
||||
|
||||
/* ---- TEST 1, AOC-D event generation */
|
||||
if (!(decoded = ast_aoc_create(AST_AOC_D, AST_AOC_CHARGE_CURRENCY, 0))) {
|
||||
|
||||
ast_test_status_update(test, "failed to create AOC-D message for event generation.\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_event_test;
|
||||
}
|
||||
/* Add billing id information */
|
||||
ast_aoc_set_billing_id(decoded, AST_AOC_BILLING_CREDIT_CARD);
|
||||
|
||||
/* Set currency information, verify results */
|
||||
if ((ast_aoc_set_currency_info(decoded, 100, AST_AOC_MULT_ONE, "usd")) ||
|
||||
(ast_aoc_set_total_type(decoded, AST_AOC_SUBTOTAL))) {
|
||||
|
||||
ast_test_status_update(test, "failed to set currency info in AOC-D msg\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_event_test;
|
||||
}
|
||||
if (ast_aoc_decoded2str(decoded, &msg)) {
|
||||
|
||||
ast_test_status_update(test, "failed to generate AOC-D msg string.\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_event_test;
|
||||
}
|
||||
if (strncmp(ast_str_buffer(msg),
|
||||
"AOC-D\r\n"
|
||||
"Type: Currency\r\n"
|
||||
"BillingID: CreditCard\r\n"
|
||||
"TypeOfCharging: SubTotal\r\n"
|
||||
"Currency: usd\r\n"
|
||||
"Currency/Amount/Cost: 100\r\n"
|
||||
"Currency/Amount/Multiplier: 1\r\n",
|
||||
strlen(ast_str_buffer(msg)))) {
|
||||
|
||||
ast_test_status_update(test, "AOC-D msg event did not match expected results\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_event_test;
|
||||
}
|
||||
decoded = ast_aoc_destroy_decoded(decoded);
|
||||
ast_str_reset(msg);
|
||||
|
||||
|
||||
/* ---- TEST 2, AOC-S event generation */
|
||||
if (!(decoded = ast_aoc_create(AST_AOC_S, 0, 0))) {
|
||||
ast_test_status_update(test, "failed to create AOC-S message for event generation.\n");
|
||||
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_event_test;
|
||||
}
|
||||
|
||||
ast_aoc_s_add_rate_flat(decoded,
|
||||
AST_AOC_CHARGED_ITEM_BASIC_COMMUNICATION,
|
||||
123,
|
||||
AST_AOC_MULT_TEN,
|
||||
"pineapple");
|
||||
|
||||
ast_aoc_s_add_rate_volume(decoded,
|
||||
AST_AOC_CHARGED_ITEM_CALL_ATTEMPT,
|
||||
AST_AOC_VOLUME_UNIT_SEGMENT,
|
||||
937,
|
||||
AST_AOC_MULT_ONEHUNDREDTH,
|
||||
"oranges");
|
||||
|
||||
ast_aoc_s_add_rate_duration(decoded,
|
||||
AST_AOC_CHARGED_ITEM_CALL_ATTEMPT,
|
||||
937,
|
||||
AST_AOC_MULT_ONETHOUSANDTH,
|
||||
"bananas",
|
||||
848,
|
||||
AST_AOC_TIME_SCALE_TENTH_SECOND,
|
||||
949,
|
||||
AST_AOC_TIME_SCALE_HOUR,
|
||||
1);
|
||||
|
||||
ast_aoc_s_add_rate_duration(decoded,
|
||||
AST_AOC_CHARGED_ITEM_USER_USER_INFO,
|
||||
937,
|
||||
AST_AOC_MULT_THOUSAND,
|
||||
"bananas",
|
||||
1111,
|
||||
AST_AOC_TIME_SCALE_SECOND,
|
||||
2222,
|
||||
AST_AOC_TIME_SCALE_DAY,
|
||||
0);
|
||||
|
||||
if (ast_aoc_decoded2str(decoded, &msg)) {
|
||||
|
||||
ast_test_status_update(test, "failed to generate AOC-D msg string.\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_event_test;
|
||||
}
|
||||
|
||||
|
||||
if (strncmp(ast_str_buffer(msg),
|
||||
"AOC-S\r\n"
|
||||
"NumberRates: 4\r\n"
|
||||
"Rate(0)/Chargeable: BasicCommunication\r\n"
|
||||
"Rate(0)/Type: Flat\r\n"
|
||||
"Rate(0)/Flat/Currency: pineapple\r\n"
|
||||
"Rate(0)/Flat/Amount/Cost: 123\r\n"
|
||||
"Rate(0)/Flat/Amount/Multiplier: 10\r\n"
|
||||
"Rate(1)/Chargeable: CallAttempt\r\n"
|
||||
"Rate(1)/Type: Volume\r\n"
|
||||
"Rate(1)/Volume/Currency: oranges\r\n"
|
||||
"Rate(1)/Volume/Amount/Cost: 937\r\n"
|
||||
"Rate(1)/Volume/Amount/Multiplier: 1/100\r\n"
|
||||
"Rate(1)/Volume/Unit: Segment\r\n"
|
||||
"Rate(2)/Chargeable: CallAttempt\r\n"
|
||||
"Rate(2)/Type: Duration\r\n"
|
||||
"Rate(2)/Duration/Currency: bananas\r\n"
|
||||
"Rate(2)/Duration/Amount/Cost: 937\r\n"
|
||||
"Rate(2)/Duration/Amount/Multiplier: 1/1000\r\n"
|
||||
"Rate(2)/Duration/ChargingType: StepFunction\r\n"
|
||||
"Rate(2)/Duration/Time/Length: 848\r\n"
|
||||
"Rate(2)/Duration/Time/Scale: OneTenthSecond\r\n"
|
||||
"Rate(2)/Duration/Granularity/Length: 949\r\n"
|
||||
"Rate(2)/Duration/Granularity/Scale: Hour\r\n"
|
||||
"Rate(3)/Chargeable: UserUserInfo\r\n"
|
||||
"Rate(3)/Type: Duration\r\n"
|
||||
"Rate(3)/Duration/Currency: bananas\r\n"
|
||||
"Rate(3)/Duration/Amount/Cost: 937\r\n"
|
||||
"Rate(3)/Duration/Amount/Multiplier: 1000\r\n"
|
||||
"Rate(3)/Duration/ChargingType: ContinuousCharging\r\n"
|
||||
"Rate(3)/Duration/Time/Length: 1111\r\n"
|
||||
"Rate(3)/Duration/Time/Scale: Second\r\n"
|
||||
"Rate(3)/Duration/Granularity/Length: 2222\r\n"
|
||||
"Rate(3)/Duration/Granularity/Scale: Day\r\n",
|
||||
strlen(ast_str_buffer(msg)))) {
|
||||
|
||||
ast_test_status_update(test, "AOC-S msg event did not match expected results\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_event_test;
|
||||
}
|
||||
decoded = ast_aoc_destroy_decoded(decoded);
|
||||
ast_str_reset(msg);
|
||||
|
||||
/* ---- TEST 3, AOC-E event generation with various charging association information*/
|
||||
if (!(decoded = ast_aoc_create(AST_AOC_E, AST_AOC_CHARGE_UNIT, 0))) {
|
||||
ast_test_status_update(test, "failed to create AOC-E message for event generation.\n");
|
||||
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_event_test;
|
||||
}
|
||||
if ((ast_aoc_add_unit_entry(decoded, 1, 111, 1, 1)) ||
|
||||
(!ast_aoc_add_unit_entry(decoded, 0, 2222, 0, 2)) || /* we expect this to fail, and it should not be added to entry list */
|
||||
(ast_aoc_add_unit_entry(decoded, 1, 3333, 0, 3)) ||
|
||||
(ast_aoc_add_unit_entry(decoded, 0, 44444, 1, 4))) {
|
||||
|
||||
ast_test_status_update(test, "failed to set unit info for AOC-E message\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_event_test;
|
||||
}
|
||||
|
||||
if (ast_aoc_decoded2str(decoded, &msg)) {
|
||||
ast_test_status_update(test, "failed to generate AOC-E msg string.\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_event_test;
|
||||
}
|
||||
|
||||
if (strncmp(ast_str_buffer(msg),
|
||||
"AOC-E\r\n"
|
||||
"Type: Units\r\n"
|
||||
"BillingID: NotAvailable\r\n"
|
||||
"Units/NumberItems: 3\r\n"
|
||||
"Units/Item(0)/NumberOf: 111\r\n"
|
||||
"Units/Item(0)/TypeOf: 1\r\n"
|
||||
"Units/Item(1)/NumberOf: 3333\r\n"
|
||||
"Units/Item(2)/TypeOf: 4\r\n",
|
||||
strlen(ast_str_buffer(msg)))) {
|
||||
|
||||
ast_test_status_update(test, "AOC-E msg event did not match expected results, with no charging association info\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_event_test;
|
||||
}
|
||||
|
||||
/* add AOC-E charging association number info */
|
||||
if (ast_aoc_set_association_number(decoded, "555-555-5555", 16)) {
|
||||
ast_test_status_update(test, "failed to set the charging association number info correctly, 3\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_event_test;
|
||||
}
|
||||
|
||||
ast_str_reset(msg);
|
||||
if (ast_aoc_decoded2str(decoded, &msg)) {
|
||||
ast_test_status_update(test, "failed to generate AOC-E msg string.\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_event_test;
|
||||
}
|
||||
|
||||
if (strncmp(ast_str_buffer(msg),
|
||||
"AOC-E\r\n"
|
||||
"ChargingAssociation/Number: 555-555-5555\r\n"
|
||||
"ChargingAssociation/Number/Plan: 16\r\n"
|
||||
"Type: Units\r\n"
|
||||
"BillingID: NotAvailable\r\n"
|
||||
"Units/NumberItems: 3\r\n"
|
||||
"Units/Item(0)/NumberOf: 111\r\n"
|
||||
"Units/Item(0)/TypeOf: 1\r\n"
|
||||
"Units/Item(1)/NumberOf: 3333\r\n"
|
||||
"Units/Item(2)/TypeOf: 4\r\n",
|
||||
strlen(ast_str_buffer(msg)))) {
|
||||
|
||||
ast_test_status_update(test, "AOC-E msg event did not match expected results, with charging association number\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_event_test;
|
||||
}
|
||||
|
||||
/* add AOC-E charging association id info */
|
||||
if (ast_aoc_set_association_id(decoded, 2222)) {
|
||||
ast_test_status_update(test, "failed to set the charging association number info correctly, 3\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_event_test;
|
||||
}
|
||||
|
||||
ast_str_reset(msg);
|
||||
if (ast_aoc_decoded2str(decoded, &msg)) {
|
||||
ast_test_status_update(test, "failed to generate AOC-E msg string.\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_event_test;
|
||||
}
|
||||
|
||||
if (strncmp(ast_str_buffer(msg),
|
||||
"AOC-E\r\n"
|
||||
"ChargingAssociation/ID: 2222\r\n"
|
||||
"Type: Units\r\n"
|
||||
"BillingID: NotAvailable\r\n"
|
||||
"Units/NumberItems: 3\r\n"
|
||||
"Units/Item(0)/NumberOf: 111\r\n"
|
||||
"Units/Item(0)/TypeOf: 1\r\n"
|
||||
"Units/Item(1)/NumberOf: 3333\r\n"
|
||||
"Units/Item(2)/TypeOf: 4\r\n",
|
||||
strlen(ast_str_buffer(msg)))) {
|
||||
|
||||
ast_test_status_update(test, "AOC-E msg event did not match expected results with charging association id.\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_event_test;
|
||||
}
|
||||
|
||||
|
||||
cleanup_aoc_event_test:
|
||||
|
||||
decoded = ast_aoc_destroy_decoded(decoded);
|
||||
ast_free(msg);
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_TEST_DEFINE(aoc_encode_decode_test)
|
||||
{
|
||||
int res = AST_TEST_PASS;
|
||||
struct ast_aoc_decoded *decoded;
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = "aoc_encode_decode_test";
|
||||
info->category = "main/aoc/";
|
||||
info->summary = "Advice of Charge encode and decode test";
|
||||
info->description =
|
||||
"This tests the Advice of Charge encode and decode routines.";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
/* ---- Test 1 ---- create AOC-D message, encode message, and decode message once again. */
|
||||
/* create AOC-D message */
|
||||
if (!(decoded = ast_aoc_create(AST_AOC_D, AST_AOC_CHARGE_CURRENCY, 0)) ||
|
||||
(ast_aoc_get_msg_type(decoded) != AST_AOC_D) ||
|
||||
(ast_aoc_get_charge_type(decoded) != AST_AOC_CHARGE_CURRENCY)) {
|
||||
|
||||
ast_test_status_update(test, "Test 1: failed to create AOC-D message\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_test;
|
||||
}
|
||||
|
||||
/* Add billing id information */
|
||||
if ((ast_aoc_set_billing_id(decoded, AST_AOC_BILLING_NORMAL) ||
|
||||
(ast_aoc_get_billing_id(decoded) != AST_AOC_BILLING_NORMAL))) {
|
||||
|
||||
ast_test_status_update(test, "TEST 1, could not set billing id correctly\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_test;
|
||||
|
||||
}
|
||||
|
||||
/* Set currency information, verify results*/
|
||||
if ((ast_aoc_set_currency_info(decoded, 100, AST_AOC_MULT_ONE, "usd")) ||
|
||||
(ast_aoc_set_total_type(decoded, AST_AOC_SUBTOTAL)) ||
|
||||
(ast_aoc_get_total_type(decoded) != AST_AOC_SUBTOTAL) ||
|
||||
(ast_aoc_get_currency_amount(decoded) != 100) ||
|
||||
(ast_aoc_get_currency_multiplier(decoded) != AST_AOC_MULT_ONE) ||
|
||||
(strcmp(ast_aoc_get_currency_name(decoded), "usd"))) {
|
||||
|
||||
ast_test_status_update(test, "Test 1: failed to set currency info\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_test;
|
||||
}
|
||||
|
||||
/* Set a currency name larger than 10 characters which is the the maximum
|
||||
* length allowed by the ETSI aoc standard. The name is expected to truncate
|
||||
* to 10 characters. */
|
||||
if ((ast_aoc_set_currency_info(decoded, 100, AST_AOC_MULT_ONE, "12345678901234567890")) ||
|
||||
(ast_aoc_get_currency_amount(decoded) != 100) ||
|
||||
(ast_aoc_get_currency_multiplier(decoded) != AST_AOC_MULT_ONE) ||
|
||||
(strcmp(ast_aoc_get_currency_name(decoded), "1234567890"))) {
|
||||
|
||||
ast_test_status_update(test, "Test 1: failed to set currency info currency name exceeding limit\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_test;
|
||||
}
|
||||
|
||||
/* Encode the message */
|
||||
if (ast_aoc_test_encode_decode_match(decoded)) {
|
||||
ast_test_status_update(test, "Test1: encode decode routine did not match expected results \n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_test;
|
||||
}
|
||||
/* cleanup decoded msg */
|
||||
decoded = ast_aoc_destroy_decoded(decoded);
|
||||
|
||||
/* ---- Test 2 ---- create AOC-E message with charge type == unit */
|
||||
/* create AOC-E message */
|
||||
if (!(decoded = ast_aoc_create(AST_AOC_E, AST_AOC_CHARGE_UNIT, 0)) ||
|
||||
(ast_aoc_get_msg_type(decoded) != AST_AOC_E) ||
|
||||
(ast_aoc_get_charge_type(decoded) != AST_AOC_CHARGE_UNIT)) {
|
||||
|
||||
ast_test_status_update(test, "Test 2: failed to create AOC-E message\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_test;
|
||||
}
|
||||
|
||||
/* Set unit information, verify results*/
|
||||
if ((ast_aoc_add_unit_entry(decoded, 1, 1, 1, 2)) ||
|
||||
(!ast_aoc_add_unit_entry(decoded, 0, 123, 0, 123)) || /* this entry should fail since either number nor type are present */
|
||||
(ast_aoc_add_unit_entry(decoded, 0, 2, 1, 3)) ||
|
||||
(ast_aoc_add_unit_entry(decoded, 1, 3, 0, 4))) {
|
||||
|
||||
ast_test_status_update(test, "Test 2: failed to set unit info\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_test;
|
||||
}
|
||||
|
||||
/* verify unit list is correct */
|
||||
if (ast_aoc_get_unit_count(decoded) == 3) {
|
||||
int i;
|
||||
const struct ast_aoc_unit_entry *unit;
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (!(unit = ast_aoc_get_unit_info(decoded, i)) ||
|
||||
((unit->valid_amount) && (unit->amount != (i+1))) ||
|
||||
((unit->valid_type) && (unit->type != (i+2)))) {
|
||||
ast_test_status_update(test, "TEST 2, invalid unit entry result, got %d,%d, expected %d,%d\n",
|
||||
unit->amount,
|
||||
unit->type,
|
||||
i+1,
|
||||
i+2);
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_test;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ast_test_status_update(test, "TEST 2, invalid unit list entry count \n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_test;
|
||||
}
|
||||
|
||||
|
||||
/* Test charging association information */
|
||||
{
|
||||
const struct ast_aoc_charging_association *ca;
|
||||
if ((ast_aoc_set_association_id(decoded, 1234)) ||
|
||||
(!(ca = ast_aoc_get_association_info(decoded)))) {
|
||||
ast_test_status_update(test, "TEST 2, could not set charging association id info correctly\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_test;
|
||||
}
|
||||
|
||||
if ((ca->charging_type != AST_AOC_CHARGING_ASSOCIATION_ID) || (ca->charge.id != 1234)) {
|
||||
ast_test_status_update(test, "TEST 2, could not get charging association id info correctly, 2\n");
|
||||
}
|
||||
|
||||
if ((ast_aoc_set_association_number(decoded, "1234", 16)) ||
|
||||
(!(ca = ast_aoc_get_association_info(decoded)))) {
|
||||
ast_test_status_update(test, "TEST 2, could not set charging association number info correctly, 3\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_test;
|
||||
}
|
||||
|
||||
if ((ca->charging_type != AST_AOC_CHARGING_ASSOCIATION_NUMBER) ||
|
||||
(ca->charge.number.plan != 16) ||
|
||||
(strcmp(ca->charge.number.number, "1234"))) {
|
||||
ast_test_status_update(test, "TEST 2, could not get charging association number info correctly\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Test every billing id possiblity */
|
||||
{
|
||||
int billid[9] = {
|
||||
AST_AOC_BILLING_NA,
|
||||
AST_AOC_BILLING_NORMAL,
|
||||
AST_AOC_BILLING_REVERSE_CHARGE,
|
||||
AST_AOC_BILLING_CREDIT_CARD,
|
||||
AST_AOC_BILLING_CALL_FWD_UNCONDITIONAL,
|
||||
AST_AOC_BILLING_CALL_FWD_BUSY,
|
||||
AST_AOC_BILLING_CALL_FWD_NO_REPLY,
|
||||
AST_AOC_BILLING_CALL_DEFLECTION,
|
||||
AST_AOC_BILLING_CALL_TRANSFER,
|
||||
};
|
||||
int i;
|
||||
|
||||
/* these should fail */
|
||||
if (!(ast_aoc_set_billing_id(decoded, (AST_AOC_BILLING_NA - 1))) ||
|
||||
!(ast_aoc_set_billing_id(decoded, (AST_AOC_BILLING_CALL_TRANSFER + 1)))) {
|
||||
|
||||
ast_test_status_update(test, "TEST 2, setting invalid billing id should fail\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_test;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_LEN(billid); i++) {
|
||||
if ((ast_aoc_set_billing_id(decoded, billid[i]) ||
|
||||
(ast_aoc_get_billing_id(decoded) != billid[i]))) {
|
||||
|
||||
ast_test_status_update(test, "TEST 2, could not set billing id correctly, iteration #%d\n", i);
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_test;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Encode the message */
|
||||
if (ast_aoc_test_encode_decode_match(decoded)) {
|
||||
ast_test_status_update(test, "Test2: encode decode routine did not match expected results \n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_test;
|
||||
}
|
||||
/* cleanup decoded msg */
|
||||
decoded = ast_aoc_destroy_decoded(decoded);
|
||||
|
||||
/* ---- Test 3 ---- create AOC-Request. test all possible combinations */
|
||||
{
|
||||
int request[7] = { /* all possible request combinations */
|
||||
AST_AOC_REQUEST_S,
|
||||
AST_AOC_REQUEST_D,
|
||||
AST_AOC_REQUEST_E,
|
||||
(AST_AOC_REQUEST_S | AST_AOC_REQUEST_D),
|
||||
(AST_AOC_REQUEST_S | AST_AOC_REQUEST_E),
|
||||
(AST_AOC_REQUEST_D | AST_AOC_REQUEST_E),
|
||||
(AST_AOC_REQUEST_D | AST_AOC_REQUEST_E | AST_AOC_REQUEST_S)
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_LEN(request); i++) {
|
||||
if (!(decoded = ast_aoc_create(AST_AOC_REQUEST, 0, request[i])) ||
|
||||
(ast_aoc_get_msg_type(decoded) != AST_AOC_REQUEST) ||
|
||||
(ast_aoc_get_termination_request(decoded)) ||
|
||||
(ast_aoc_get_request(decoded) != request[i])) {
|
||||
|
||||
ast_test_status_update(test, "Test 3: failed to create AOC-Request message, iteration #%d\n", i);
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_test;
|
||||
}
|
||||
|
||||
/* Encode the message */
|
||||
if (ast_aoc_test_encode_decode_match(decoded)) {
|
||||
ast_test_status_update(test, "Test3: encode decode routine did not match expected results, iteration #%d\n", i);
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_test;
|
||||
}
|
||||
/* cleanup decoded msg */
|
||||
decoded = ast_aoc_destroy_decoded(decoded);
|
||||
}
|
||||
|
||||
|
||||
/* Test termination Request Type */
|
||||
if (!(decoded = ast_aoc_create(AST_AOC_REQUEST, 0, AST_AOC_REQUEST_E)) ||
|
||||
(ast_aoc_set_termination_request(decoded)) ||
|
||||
(!ast_aoc_get_termination_request(decoded)) ||
|
||||
(ast_aoc_get_msg_type(decoded) != AST_AOC_REQUEST) ||
|
||||
(ast_aoc_get_request(decoded) != AST_AOC_REQUEST_E)) {
|
||||
|
||||
ast_test_status_update(test, "Test 3: failed to create AOC-Request message with Termination Request set\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_test;
|
||||
}
|
||||
|
||||
/* Encode the message */
|
||||
if (ast_aoc_test_encode_decode_match(decoded)) {
|
||||
ast_test_status_update(test, "Test3: encode decode routine did not match expected results with termination request set\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_test;
|
||||
}
|
||||
/* cleanup decoded msg */
|
||||
decoded = ast_aoc_destroy_decoded(decoded);
|
||||
}
|
||||
|
||||
/* ---- Test 4 ---- Make stuff blow up */
|
||||
if ((decoded = ast_aoc_create(AST_AOC_D, 1234567, 0))) {
|
||||
|
||||
ast_test_status_update(test, "Test 4: aoc-d creation with no valid charge type should fail\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_test;
|
||||
}
|
||||
if ((decoded = ast_aoc_create(AST_AOC_REQUEST, 0, 0))) {
|
||||
|
||||
ast_test_status_update(test, "Test 4: aoc request creation with no data should have failed\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_test;
|
||||
}
|
||||
if ((decoded = ast_aoc_create(AST_AOC_REQUEST, -12345678, -23456789))) {
|
||||
|
||||
ast_test_status_update(test, "Test 4: aoc request creation with random data should have failed\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_test;
|
||||
}
|
||||
|
||||
/* ---- Test 5 ---- create AOC-E message with charge type == FREE and charge type == NA */
|
||||
/* create AOC-E message */
|
||||
if (!(decoded = ast_aoc_create(AST_AOC_E, AST_AOC_CHARGE_FREE, 0)) ||
|
||||
(ast_aoc_get_msg_type(decoded) != AST_AOC_E) ||
|
||||
(ast_aoc_get_charge_type(decoded) != AST_AOC_CHARGE_FREE)) {
|
||||
|
||||
ast_test_status_update(test, "Test 5: failed to create AOC-E message, charge type Free\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_test;
|
||||
}
|
||||
if (ast_aoc_test_encode_decode_match(decoded)) {
|
||||
ast_test_status_update(test, "Test5: encode decode routine did not match expected results, charge type Free\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_test;
|
||||
}
|
||||
/* cleanup decoded msg */
|
||||
decoded = ast_aoc_destroy_decoded(decoded);
|
||||
|
||||
/* create AOC-E message */
|
||||
if (!(decoded = ast_aoc_create(AST_AOC_E, AST_AOC_CHARGE_NA, 0)) ||
|
||||
(ast_aoc_get_msg_type(decoded) != AST_AOC_E) ||
|
||||
(ast_aoc_get_charge_type(decoded) != AST_AOC_CHARGE_NA)) {
|
||||
|
||||
ast_test_status_update(test, "Test 5: failed to create AOC-E message, charge type NA\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_test;
|
||||
}
|
||||
if (ast_aoc_test_encode_decode_match(decoded)) {
|
||||
ast_test_status_update(test, "Test5: encode decode routine did not match expected results, charge type NA.\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_test;
|
||||
}
|
||||
/* cleanup decoded msg */
|
||||
decoded = ast_aoc_destroy_decoded(decoded);
|
||||
|
||||
|
||||
/* ---- TEST 6, AOC-S encode decode */
|
||||
if (!(decoded = ast_aoc_create(AST_AOC_S, 0, 0))) {
|
||||
ast_test_status_update(test, "failed to create AOC-S message for encode decode testing.\n");
|
||||
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_test;
|
||||
}
|
||||
|
||||
ast_aoc_s_add_rate_duration(decoded,
|
||||
AST_AOC_CHARGED_ITEM_SUPPLEMENTARY_SERVICE,
|
||||
937,
|
||||
AST_AOC_MULT_THOUSAND,
|
||||
"jkasdf",
|
||||
235328,
|
||||
AST_AOC_TIME_SCALE_SECOND,
|
||||
905423,
|
||||
AST_AOC_TIME_SCALE_DAY,
|
||||
1);
|
||||
|
||||
ast_aoc_s_add_rate_flat(decoded,
|
||||
AST_AOC_CHARGED_ITEM_CALL_SETUP,
|
||||
1337,
|
||||
AST_AOC_MULT_ONEHUNDREDTH,
|
||||
"MONEYS");
|
||||
|
||||
ast_aoc_s_add_rate_volume(decoded,
|
||||
AST_AOC_CHARGED_ITEM_CALL_ATTEMPT,
|
||||
AST_AOC_VOLUME_UNIT_SEGMENT,
|
||||
5555,
|
||||
AST_AOC_MULT_ONEHUNDREDTH,
|
||||
"pounds");
|
||||
|
||||
ast_aoc_s_add_rate_duration(decoded,
|
||||
AST_AOC_CHARGED_ITEM_CALL_ATTEMPT,
|
||||
78923,
|
||||
AST_AOC_MULT_ONETHOUSANDTH,
|
||||
"SNAP",
|
||||
9354,
|
||||
AST_AOC_TIME_SCALE_HUNDREDTH_SECOND,
|
||||
234933,
|
||||
AST_AOC_TIME_SCALE_SECOND,
|
||||
0);
|
||||
|
||||
ast_aoc_s_add_rate_free(decoded, AST_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT, 1);
|
||||
ast_aoc_s_add_rate_free(decoded, AST_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT, 0);
|
||||
ast_aoc_s_add_rate_na(decoded, AST_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT);
|
||||
|
||||
if (ast_aoc_test_encode_decode_match(decoded)) {
|
||||
ast_test_status_update(test, "Test6: encode decode routine for AOC-S did not match expected results\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto cleanup_aoc_test;
|
||||
}
|
||||
/* cleanup decoded msg */
|
||||
decoded = ast_aoc_destroy_decoded(decoded);
|
||||
|
||||
|
||||
|
||||
cleanup_aoc_test:
|
||||
|
||||
decoded = ast_aoc_destroy_decoded(decoded);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
AST_TEST_UNREGISTER(aoc_encode_decode_test);
|
||||
AST_TEST_UNREGISTER(aoc_event_generation_test);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
AST_TEST_REGISTER(aoc_encode_decode_test);
|
||||
AST_TEST_REGISTER(aoc_event_generation_test);
|
||||
return AST_MODULE_LOAD_SUCCESS;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "AOC unit tests");
|
Reference in New Issue