[asterisk-commits] rmudgett: trunk r267096 - in /trunk: apps/ channels/ channels/sip/include/ co...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed Jun 2 13:10:27 CDT 2010


Author: rmudgett
Date: Wed Jun  2 13:10:15 2010
New Revision: 267096

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=267096
Log:
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/

Added:
    trunk/doc/advice_of_charge.txt
      - copied unchanged from r267092, team/dvossel/generic_aoc/doc/advice_of_charge.txt
    trunk/include/asterisk/aoc.h
      - copied unchanged from r267092, team/dvossel/generic_aoc/include/asterisk/aoc.h
    trunk/main/aoc.c
      - copied unchanged from r267092, team/dvossel/generic_aoc/main/aoc.c
    trunk/tests/test_aoc.c
      - copied unchanged from r267092, team/dvossel/generic_aoc/tests/test_aoc.c
Modified:
    trunk/apps/app_dial.c
    trunk/apps/app_queue.c
    trunk/channels/chan_dahdi.c
    trunk/channels/chan_sip.c
    trunk/channels/sig_pri.c
    trunk/channels/sig_pri.h
    trunk/channels/sip/include/sip.h
    trunk/configs/chan_dahdi.conf.sample
    trunk/configs/manager.conf.sample
    trunk/configs/sip.conf.sample
    trunk/include/asterisk/frame.h
    trunk/main/asterisk.c
    trunk/main/channel.c
    trunk/main/features.c
    trunk/main/manager.c

Modified: trunk/apps/app_dial.c
URL: http://svnview.digium.com/svn/asterisk/trunk/apps/app_dial.c?view=diff&rev=267096&r1=267095&r2=267096
==============================================================================
--- trunk/apps/app_dial.c (original)
+++ trunk/apps/app_dial.c Wed Jun  2 13:10:15 2010
@@ -62,6 +62,7 @@
 #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 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 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 @@
 							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 |
@@ -1113,6 +1124,14 @@
 								connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
 								ast_channel_update_connected_line(in, &connected_caller);
 								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;
@@ -1230,6 +1249,17 @@
 						}
 					}
 					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);

Modified: trunk/apps/app_queue.c
URL: http://svnview.digium.com/svn/asterisk/trunk/apps/app_queue.c?view=diff&rev=267096&r1=267095&r2=267096
==============================================================================
--- trunk/apps/app_queue.c (original)
+++ trunk/apps/app_queue.c Wed Jun  2 13:10:15 2010
@@ -94,6 +94,7 @@
 #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 @@
 	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 @@
 		}
 		oo = outgoing;
 		outgoing = outgoing->q_next;
+		ast_aoc_destroy_decoded(oo->aoc_s_rate_list);
 		callattempt_free(oo);
 	}
 }
@@ -3333,6 +3336,14 @@
 							connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
 							ast_channel_update_connected_line(in, &connected_caller);
 							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;
@@ -3452,6 +3463,14 @@
 										connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
 										ast_channel_update_connected_line(in, &connected_caller);
 										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;
@@ -3520,6 +3539,17 @@
 							} else {
 								if (ast_channel_connected_line_macro(o->chan, in, f, 1, 1)) {
 									ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen);
+								}
+							}
+							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;

Modified: trunk/channels/chan_dahdi.c
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/chan_dahdi.c?view=diff&rev=267096&r1=267095&r2=267096
==============================================================================
--- trunk/channels/chan_dahdi.c (original)
+++ trunk/channels/chan_dahdi.c Wed Jun  2 13:10:15 2010
@@ -11779,6 +11779,10 @@
 #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 @@
 #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);

Modified: trunk/channels/chan_sip.c
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/chan_sip.c?view=diff&rev=267096&r1=267095&r2=267096
==============================================================================
--- trunk/channels/chan_sip.c (original)
+++ trunk/channels/chan_sip.c Wed Jun  2 13:10:15 2010
@@ -261,6 +261,7 @@
 #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 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_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 @@
 
 	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 @@
 	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 @@
 	/* 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 @@
 	*/
 }
 
+/*! \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 @@
 	/* 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 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 				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 @@
 
 	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 @@
 			}
 		} 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);
 		}
 	}
 

Modified: trunk/channels/sig_pri.c
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/sig_pri.c?view=diff&rev=267096&r1=267095&r2=267096
==============================================================================
--- trunk/channels/sig_pri.c (original)
+++ trunk/channels/sig_pri.c Wed Jun  2 13:10:15 2010
@@ -46,6 +46,7 @@
 #include "asterisk/cli.h"
 #include "asterisk/transcap.h"
 #include "asterisk/features.h"
+#include "asterisk/aoc.h"
 
 #include "sig_pri.h"
 #ifndef PRI_EVENT_FACILITY
@@ -1086,6 +1087,18 @@
 		new_chan->setup_ack = old_chan->setup_ack;
 		new_chan->outgoing = old_chan->outgoing;
 		new_chan->digital = old_chan->digital;
+#if defined(HAVE_PRI_AOC_EVENTS)
+		new_chan->aoc_s_request_invoke_id = old_chan->aoc_s_request_invoke_id;
+		new_chan->aoc_s_request_invoke_id_valid = old_chan->aoc_s_request_invoke_id_valid;
+		new_chan->holding_aoce = old_chan->holding_aoce;
+		new_chan->waiting_for_aoce = old_chan->waiting_for_aoce;
+		new_chan->aoc_e = old_chan->aoc_e;
+
+		old_chan->holding_aoce = 0;
+		old_chan->aoc_s_request_invoke_id_valid = 0;
+		old_chan->waiting_for_aoce = 0;
+		memset(&old_chan->aoc_e, 0, sizeof(&old_chan->aoc_e));
+#endif	/* defined(HAVE_PRI_AOC_EVENTS) */
 		old_chan->alerting = 0;
 		old_chan->alreadyhungup = 0;
 		old_chan->isidlecall = 0;
@@ -2057,645 +2070,934 @@
 #if defined(HAVE_PRI_AOC_EVENTS)
 /*!
  * \internal
- * \brief Convert PRI_AOC_CHARGED_ITEM to string.
+ * \brief Convert ast_aoc_charged_item to PRI_AOC_CHARGED_ITEM .
  * \since 1.8
  *
  * \param value Value to convert to string.
  *
- * \return String equivalent.
- */
-static const char *sig_pri_aoc_charged_item_str(enum PRI_AOC_CHARGED_ITEM value)
-{
-	const char *str;
-
+ * \return PRI_AOC_CHARGED_ITEM
+ */
+static enum PRI_AOC_CHARGED_ITEM sig_pri_aoc_charged_item_to_pri(enum PRI_AOC_CHARGED_ITEM value)
+{
+	switch (value) {
+	case AST_AOC_CHARGED_ITEM_NA:
+		return PRI_AOC_CHARGED_ITEM_NOT_AVAILABLE;
+	case AST_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT:
+		return PRI_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT;
+	case AST_AOC_CHARGED_ITEM_BASIC_COMMUNICATION:
+		return PRI_AOC_CHARGED_ITEM_BASIC_COMMUNICATION;
+	case AST_AOC_CHARGED_ITEM_CALL_ATTEMPT:
+		return PRI_AOC_CHARGED_ITEM_CALL_ATTEMPT;
+	case AST_AOC_CHARGED_ITEM_CALL_SETUP:
+		return PRI_AOC_CHARGED_ITEM_CALL_SETUP;
+	case AST_AOC_CHARGED_ITEM_USER_USER_INFO:
+		return PRI_AOC_CHARGED_ITEM_USER_USER_INFO;
+	case AST_AOC_CHARGED_ITEM_SUPPLEMENTARY_SERVICE:
+		return PRI_AOC_CHARGED_ITEM_SUPPLEMENTARY_SERVICE;
+	}
+	return PRI_AOC_CHARGED_ITEM_NOT_AVAILABLE;
+}
+#endif	/* defined(HAVE_PRI_AOC_EVENTS) */
+
+#if defined(HAVE_PRI_AOC_EVENTS)
+/*!
+ * \internal
+ * \brief Convert PRI_AOC_CHARGED_ITEM to ast_aoc_charged_item.
+ * \since 1.8
+ *
+ * \param value Value to convert to string.
+ *
+ * \return ast_aoc_charged_item
+ */
+static enum ast_aoc_s_charged_item sig_pri_aoc_charged_item_to_ast(enum PRI_AOC_CHARGED_ITEM value)
+{
+	switch (value) {
+	case PRI_AOC_CHARGED_ITEM_NOT_AVAILABLE:
+		return AST_AOC_CHARGED_ITEM_NA;
+	case PRI_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT:
+		return AST_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT;
+	case PRI_AOC_CHARGED_ITEM_BASIC_COMMUNICATION:
+		return AST_AOC_CHARGED_ITEM_BASIC_COMMUNICATION;
+	case PRI_AOC_CHARGED_ITEM_CALL_ATTEMPT:
+		return AST_AOC_CHARGED_ITEM_CALL_ATTEMPT;
+	case PRI_AOC_CHARGED_ITEM_CALL_SETUP:
+		return AST_AOC_CHARGED_ITEM_CALL_SETUP;
+	case PRI_AOC_CHARGED_ITEM_USER_USER_INFO:
+		return AST_AOC_CHARGED_ITEM_USER_USER_INFO;
+	case PRI_AOC_CHARGED_ITEM_SUPPLEMENTARY_SERVICE:
+		return AST_AOC_CHARGED_ITEM_SUPPLEMENTARY_SERVICE;
+	}
+	return AST_AOC_CHARGED_ITEM_NA;
+}
+#endif	/* defined(HAVE_PRI_AOC_EVENTS) */
+
+#if defined(HAVE_PRI_AOC_EVENTS)
+/*!
+ * \internal
+ * \brief Convert AST_AOC_MULTIPLER to PRI_AOC_MULTIPLIER.
+ * \since 1.8
+ *
+ * \return pri enum equivalent.
+ */
+static int sig_pri_aoc_multiplier_from_ast(enum ast_aoc_currency_multiplier mult)
+{
+	switch (mult) {
+	case AST_AOC_MULT_ONETHOUSANDTH:
+		return PRI_AOC_MULTIPLIER_THOUSANDTH;
+	case AST_AOC_MULT_ONEHUNDREDTH:
+		return PRI_AOC_MULTIPLIER_HUNDREDTH;
+	case AST_AOC_MULT_ONETENTH:
+		return PRI_AOC_MULTIPLIER_TENTH;
+	case AST_AOC_MULT_ONE:
+		return PRI_AOC_MULTIPLIER_ONE;
+	case AST_AOC_MULT_TEN:
+		return PRI_AOC_MULTIPLIER_TEN;
+	case AST_AOC_MULT_HUNDRED:
+		return PRI_AOC_MULTIPLIER_HUNDRED;
+	case AST_AOC_MULT_THOUSAND:
+		return PRI_AOC_MULTIPLIER_THOUSAND;
+	default:
+		return PRI_AOC_MULTIPLIER_ONE;
+	}
+}
+#endif	/* defined(HAVE_PRI_AOC_EVENTS) */
+
+#if defined(HAVE_PRI_AOC_EVENTS)
+/*!
+ * \internal
+ * \brief Convert PRI_AOC_MULTIPLIER to AST_AOC_MULTIPLIER
+ * \since 1.8
+ *
+ * \return ast enum equivalent.
+ */
+static int sig_pri_aoc_multiplier_from_pri(const int mult)
+{
+	switch (mult) {
+	case PRI_AOC_MULTIPLIER_THOUSANDTH:
+		return AST_AOC_MULT_ONETHOUSANDTH;
+	case PRI_AOC_MULTIPLIER_HUNDREDTH:
+		return AST_AOC_MULT_ONEHUNDREDTH;
+	case PRI_AOC_MULTIPLIER_TENTH:
+		return AST_AOC_MULT_ONETENTH;
+	case PRI_AOC_MULTIPLIER_ONE:
+		return AST_AOC_MULT_ONE;
+	case PRI_AOC_MULTIPLIER_TEN:
+		return AST_AOC_MULT_TEN;
+	case PRI_AOC_MULTIPLIER_HUNDRED:
+		return AST_AOC_MULT_HUNDRED;
+	case PRI_AOC_MULTIPLIER_THOUSAND:
+		return AST_AOC_MULT_THOUSAND;
+	default:
+		return AST_AOC_MULT_ONE;
+	}
+}
+#endif	/* defined(HAVE_PRI_AOC_EVENTS) */
+
+#if defined(HAVE_PRI_AOC_EVENTS)
+/*!
+ * \internal
+ * \brief Convert ast_aoc_time_scale representation to PRI_AOC_TIME_SCALE
+ * \since 1.8
+ *
+ * \param value Value to convert to ast representation
+ *
+ * \return PRI_AOC_TIME_SCALE
+ */
+static enum PRI_AOC_TIME_SCALE sig_pri_aoc_scale_to_pri(enum ast_aoc_time_scale value)
+{
 	switch (value) {
 	default:
-	case PRI_AOC_CHARGED_ITEM_NOT_AVAILABLE:
-		str = "NotAvailable";
-		break;
-	case PRI_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT:
-		str = "SpecialArrangement";
-		break;
-	case PRI_AOC_CHARGED_ITEM_BASIC_COMMUNICATION:
-		str = "BasicCommunication";
-		break;
-	case PRI_AOC_CHARGED_ITEM_CALL_ATTEMPT:
-		str = "CallAttempt";
-		break;
-	case PRI_AOC_CHARGED_ITEM_CALL_SETUP:
-		str = "CallSetup";
-		break;
-	case PRI_AOC_CHARGED_ITEM_USER_USER_INFO:
-		str = "UserUserInfo";
-		break;
-	case PRI_AOC_CHARGED_ITEM_SUPPLEMENTARY_SERVICE:
-		str = "SupplementaryService";
-		break;
-	}
-	return str;
+	case AST_AOC_TIME_SCALE_HUNDREDTH_SECOND:
+		return PRI_AOC_TIME_SCALE_HUNDREDTH_SECOND;
+	case AST_AOC_TIME_SCALE_TENTH_SECOND:
+		return PRI_AOC_TIME_SCALE_TENTH_SECOND;
+	case AST_AOC_TIME_SCALE_SECOND:
+		return PRI_AOC_TIME_SCALE_SECOND;
+	case AST_AOC_TIME_SCALE_TEN_SECOND:
+		return PRI_AOC_TIME_SCALE_TEN_SECOND;
+	case AST_AOC_TIME_SCALE_MINUTE:
+		return PRI_AOC_TIME_SCALE_MINUTE;
+	case AST_AOC_TIME_SCALE_HOUR:
+		return PRI_AOC_TIME_SCALE_HOUR;
+	case AST_AOC_TIME_SCALE_DAY:
+		return PRI_AOC_TIME_SCALE_DAY;
+	}
 }
 #endif	/* defined(HAVE_PRI_AOC_EVENTS) */
 
 #if defined(HAVE_PRI_AOC_EVENTS)
 /*!
  * \internal
- * \brief Convert PRI_AOC_RATE_TYPE to string.
- * \since 1.8
- *
- * \param value Value to convert to string.
- *
- * \return String equivalent.
- */
-static const char *sig_pri_aoc_rate_type_str(enum PRI_AOC_RATE_TYPE value)
-{
-	const char *str;
-
-	switch (value) {
-	default:
-	case PRI_AOC_RATE_TYPE_NOT_AVAILABLE:
-		str = "NotAvailable";
-		break;
-	case PRI_AOC_RATE_TYPE_FREE:
-		str = "Free";
-		break;
-	case PRI_AOC_RATE_TYPE_FREE_FROM_BEGINNING:
-		str = "FreeFromBeginning";
-		break;
-	case PRI_AOC_RATE_TYPE_DURATION:
-		str = "Duration";
-		break;
-	case PRI_AOC_RATE_TYPE_FLAT:
-		str = "Flat";
-		break;
-	case PRI_AOC_RATE_TYPE_VOLUME:
-		str = "Volume";
-		break;
-	case PRI_AOC_RATE_TYPE_SPECIAL_CODE:
-		str = "SpecialCode";
-		break;
-	}
-	return str;
-}
-#endif	/* defined(HAVE_PRI_AOC_EVENTS) */
-
-#if defined(HAVE_PRI_AOC_EVENTS)
-/*!
- * \internal
- * \brief Convert PRI_AOC_VOLUME_UNIT to string.
- * \since 1.8
- *
- * \param value Value to convert to string.
- *
- * \return String equivalent.
- */
-static const char *sig_pri_aoc_volume_unit_str(enum PRI_AOC_VOLUME_UNIT value)
-{
-	const char *str;
-
-	switch (value) {
-	default:
-	case PRI_AOC_VOLUME_UNIT_OCTET:
-		str = "Octet";
-		break;
-	case PRI_AOC_VOLUME_UNIT_SEGMENT:
-		str = "Segment";
-		break;
-	case PRI_AOC_VOLUME_UNIT_MESSAGE:
-		str = "Message";
-		break;
-	}
-	return str;
-}
-#endif	/* defined(HAVE_PRI_AOC_EVENTS) */
-
-#if defined(HAVE_PRI_AOC_EVENTS)
-/*!
- * \internal
- * \brief Convert PRI_AOC_MULTIPLIER to string.
- * \since 1.8
- *
- * \param value Value to convert to string.
- *
- * \return String equivalent.
- */
-static const char *sig_pri_aoc_multiplier_str(enum PRI_AOC_MULTIPLIER value)
-{
-	const char *str;
-
-	switch (value) {
-	default:
-	case PRI_AOC_MULTIPLIER_THOUSANDTH:
-		str = "1/1000";
-		break;
-	case PRI_AOC_MULTIPLIER_HUNDREDTH:
-		str = "1/100";
-		break;
-	case PRI_AOC_MULTIPLIER_TENTH:
-		str = "1/10";
-		break;
-	case PRI_AOC_MULTIPLIER_ONE:
-		str = "1";
-		break;
-	case PRI_AOC_MULTIPLIER_TEN:
-		str = "10";
-		break;
-	case PRI_AOC_MULTIPLIER_HUNDRED:
-		str = "100";
-		break;
-	case PRI_AOC_MULTIPLIER_THOUSAND:
-		str = "1000";
-		break;
-	}
-	return str;
-}
-#endif	/* defined(HAVE_PRI_AOC_EVENTS) */
-
-#if defined(HAVE_PRI_AOC_EVENTS)
-/*!
- * \internal
- * \brief Convert PRI_AOC_TIME_SCALE to string.
- * \since 1.8
- *
- * \param value Value to convert to string.
- *
- * \return String equivalent.
- */
-static const char *sig_pri_aoc_scale_str(enum PRI_AOC_TIME_SCALE value)
-{
-	const char *str;
-
+ * \brief Convert PRI_AOC_TIME_SCALE to ast aoc representation
+ * \since 1.8
+ *
+ * \param value Value to convert to ast representation
+ *
+ * \return ast aoc time scale
+ */
+static enum ast_aoc_time_scale sig_pri_aoc_scale_to_ast(enum PRI_AOC_TIME_SCALE value)
+{
 	switch (value) {
 	default:
 	case PRI_AOC_TIME_SCALE_HUNDREDTH_SECOND:
-		str = "OneHundredthSecond";
-		break;
+		return AST_AOC_TIME_SCALE_HUNDREDTH_SECOND;
 	case PRI_AOC_TIME_SCALE_TENTH_SECOND:
-		str = "OneTenthSecond";
-		break;
+		return AST_AOC_TIME_SCALE_TENTH_SECOND;
 	case PRI_AOC_TIME_SCALE_SECOND:
-		str = "Second";
-		break;
+		return AST_AOC_TIME_SCALE_SECOND;
 	case PRI_AOC_TIME_SCALE_TEN_SECOND:
-		str = "TenSeconds";
-		break;
+		return AST_AOC_TIME_SCALE_TEN_SECOND;
 	case PRI_AOC_TIME_SCALE_MINUTE:
-		str = "Minute";
-		break;
+		return AST_AOC_TIME_SCALE_MINUTE;
 	case PRI_AOC_TIME_SCALE_HOUR:
-		str = "Hour";
-		break;
+		return AST_AOC_TIME_SCALE_HOUR;
 	case PRI_AOC_TIME_SCALE_DAY:
-		str = "Day";
-		break;
-	}
-	return str;
+		return AST_AOC_TIME_SCALE_DAY;
+	}
+	return AST_AOC_TIME_SCALE_HUNDREDTH_SECOND;
 }
 #endif	/* defined(HAVE_PRI_AOC_EVENTS) */
 
 #if defined(HAVE_PRI_AOC_EVENTS)
 /*!
  * \internal
- * \brief Convert PRI_AOC_DE_CHARGE to string.
- * \since 1.8
- *
- * \param value Value to convert to string.
- *
- * \return String equivalent.
- */
-static const char *sig_pri_aoc_de_charge_str(enum PRI_AOC_DE_CHARGE value)
-{
-	const char *str;
-
-	switch (value) {
-	default:
-	case PRI_AOC_DE_CHARGE_NOT_AVAILABLE:
-		str = "NotAvailable";
-		break;
-	case PRI_AOC_DE_CHARGE_FREE:
-		str = "Free";
-		break;
-	case PRI_AOC_DE_CHARGE_CURRENCY:
-		str = "Currency";
-		break;
-	case PRI_AOC_DE_CHARGE_UNITS:
-		str = "Units";
-		break;
-	}
-	return str;
-}
-#endif	/* defined(HAVE_PRI_AOC_EVENTS) */
-
-#if defined(HAVE_PRI_AOC_EVENTS)
-/*!
- * \internal
- * \brief Convert PRI_AOC_D_BILLING_ID to string.
- * \since 1.8
- *
- * \param value Value to convert to string.
- *
- * \return String equivalent.
- */
-static const char *sig_pri_aoc_d_billing_id_str(enum PRI_AOC_D_BILLING_ID value)
-{
-	const char *str;
-
-	switch (value) {
-	default:
-	case PRI_AOC_D_BILLING_ID_NOT_AVAILABLE:
-		str = "NotAvailable";
-		break;
-	case PRI_AOC_D_BILLING_ID_NORMAL:
-		str = "Normal";
-		break;
-	case PRI_AOC_D_BILLING_ID_REVERSE:
-		str = "Reverse";
-		break;
-	case PRI_AOC_D_BILLING_ID_CREDIT_CARD:
-		str = "CreditCard";
-		break;
-	}
-	return str;
-}
-#endif	/* defined(HAVE_PRI_AOC_EVENTS) */
-
-#if defined(HAVE_PRI_AOC_EVENTS)
-/*!
- * \internal
- * \brief Convert PRI_AOC_E_BILLING_ID to string.
- * \since 1.8
- *
- * \param value Value to convert to string.
- *
- * \return String equivalent.
- */
-static const char *sig_pri_aoc_e_billing_id_str(enum PRI_AOC_E_BILLING_ID value)
-{
-	const char *str;
-
-	switch (value) {
-	default:
-	case PRI_AOC_E_BILLING_ID_NOT_AVAILABLE:
-		str = "NotAvailable";
-		break;
-	case PRI_AOC_E_BILLING_ID_NORMAL:
-		str = "Normal";
-		break;
-	case PRI_AOC_E_BILLING_ID_REVERSE:
-		str = "Reverse";
-		break;
-	case PRI_AOC_E_BILLING_ID_CREDIT_CARD:
-		str = "CreditCard";
-		break;
-	case PRI_AOC_E_BILLING_ID_CALL_FORWARDING_UNCONDITIONAL:
-		str = "CallForwardingUnconditional";
-		break;
-	case PRI_AOC_E_BILLING_ID_CALL_FORWARDING_BUSY:
-		str = "CallForwardingBusy";
-		break;
-	case PRI_AOC_E_BILLING_ID_CALL_FORWARDING_NO_REPLY:
-		str = "CallForwardingNoReply";
-		break;
-	case PRI_AOC_E_BILLING_ID_CALL_DEFLECTION:
-		str = "CallDeflection";
-		break;
-	case PRI_AOC_E_BILLING_ID_CALL_TRANSFER:
-		str = "CallTransfer";
-		break;
-	}
-	return str;
-}
-#endif	/* defined(HAVE_PRI_AOC_EVENTS) */
-
-#if defined(HAVE_PRI_AOC_EVENTS)
-/*!
- * \internal
- * \brief Append the amount structure to the event message string.
- * \since 1.8
- *
- * \param msg Event message string being built.
- * \param prefix Prefix to add to the amount lines.
- * \param amount Data to convert.
- *
- * \return Nothing
- */
-static void sig_pri_aoc_amount(struct ast_str **msg, const char *prefix, const struct pri_aoc_amount *amount)
-{
-	static const char name[] = "Amount";
-
-	ast_str_append(msg, 0, "%s/%s/Cost: %ld\r\n", prefix, name, amount->cost);
-	ast_str_append(msg, 0, "%s/%s/Multiplier: %s\r\n", prefix, name,
-		sig_pri_aoc_multiplier_str(amount->multiplier));
-}
-#endif	/* defined(HAVE_PRI_AOC_EVENTS) */
-
-#if defined(HAVE_PRI_AOC_EVENTS)
-/*!
- * \internal
- * \brief Append the time structure to the event message string.
- * \since 1.8
- *
- * \param msg Event message string being built.
- * \param prefix Prefix to add to the amount lines.
- * \param name Name of the time structure to convert.
- * \param time Data to convert.
- *
- * \return Nothing
- */
-static void sig_pri_aoc_time(struct ast_str **msg, const char *prefix, const char *name, const struct pri_aoc_time *time)
-{
-	ast_str_append(msg, 0, "%s/%s/Length: %ld\r\n", prefix, name, time->length);
-	ast_str_append(msg, 0, "%s/%s/Scale: %s\r\n", prefix, name,
-		sig_pri_aoc_scale_str(time->scale));
-}
-#endif	/* defined(HAVE_PRI_AOC_EVENTS) */
-
-#if defined(HAVE_PRI_AOC_EVENTS)
-/*!
- * \internal
- * \brief Handle the AOC-S event.
+ * \brief Handle AOC-S control frame
  * \since 1.8
  *
  * \param aoc_s AOC-S event parameters.
  * \param owner Asterisk channel associated with the call.
+ * \param passthrough indicating if this message should be queued on the ast channel
  *
  * \note Assumes the pri->lock is already obtained.
+ * \note Assumes the sig_pri private is locked
  * \note Assumes the owner channel lock is already obtained.
  *
  * \return Nothing
  */
-static void sig_pri_aoc_s_event(const struct pri_subcmd_aoc_s *aoc_s, struct ast_channel *owner)
-{
-	struct ast_str *msg;
-	const char *rate_str;
-	char prefix[32];
+static void sig_pri_aoc_s_from_pri(const struct pri_subcmd_aoc_s *aoc_s, struct ast_channel *owner, int passthrough)
+{
+	struct ast_aoc_decoded *decoded = NULL;
+	struct ast_aoc_encoded *encoded = NULL;
+	size_t encoded_size = 0;
 	int idx;
 
-	msg = ast_str_create(4096);
-	if (!msg) {
+	if (!owner || !aoc_s) {
 		return;
 	}
 
-	ast_str_append(&msg, 0, "Channel: %s\r\n", owner->name);
-	ast_str_append(&msg, 0, "UniqueID: %s\r\n", owner->uniqueid);
-
-	ast_str_append(&msg, 0, "NumberRates: %d\r\n", aoc_s->num_items);
+	if (!(decoded = ast_aoc_create(AST_AOC_S, 0, 0))) {
+		return;
+	}
+
 	for (idx = 0; idx < aoc_s->num_items; ++idx) {
-		snprintf(prefix, sizeof(prefix), "Rate(%d)", idx);
-
-		ast_str_append(&msg, 0, "%s/Chargeable: %s\r\n", prefix,
-			sig_pri_aoc_charged_item_str(aoc_s->item[idx].chargeable));
-		if (aoc_s->item[idx].chargeable == PRI_AOC_CHARGED_ITEM_NOT_AVAILABLE) {
+		enum ast_aoc_s_charged_item charged_item;
+
+		charged_item = sig_pri_aoc_charged_item_to_ast(aoc_s->item[idx].chargeable);
+		if (charged_item == AST_AOC_CHARGED_ITEM_NA) {
+			/* Delete the unknown charged item from the list. */
 			continue;
 		}
-		rate_str = sig_pri_aoc_rate_type_str(aoc_s->item[idx].rate_type);
-		ast_str_append(&msg, 0, "%s/Type: %s\r\n", prefix, rate_str);
 		switch (aoc_s->item[idx].rate_type) {
 		case PRI_AOC_RATE_TYPE_DURATION:
-			strcat(prefix, "/");
-			strcat(prefix, rate_str);
-			ast_str_append(&msg, 0, "%s/Currency: %s\r\n", prefix,
-				aoc_s->item[idx].rate.duration.currency);
-			sig_pri_aoc_amount(&msg, prefix, &aoc_s->item[idx].rate.duration.amount);
-			ast_str_append(&msg, 0, "%s/ChargingType: %s\r\n", prefix,
-				aoc_s->item[idx].rate.duration.charging_type
-				? "StepFunction" : "ContinuousCharging");
-			sig_pri_aoc_time(&msg, prefix, "Time", &aoc_s->item[idx].rate.duration.time);
-			if (aoc_s->item[idx].rate.duration.granularity.length) {
-				sig_pri_aoc_time(&msg, prefix, "Granularity",
-					&aoc_s->item[idx].rate.duration.granularity);
-			}
+			ast_aoc_s_add_rate_duration(decoded,
+				charged_item,
+				aoc_s->item[idx].rate.duration.amount.cost,
+				sig_pri_aoc_multiplier_from_pri(aoc_s->item[idx].rate.duration.amount.multiplier),
+				aoc_s->item[idx].rate.duration.currency,
+				aoc_s->item[idx].rate.duration.time.length,
+				sig_pri_aoc_scale_to_ast(aoc_s->item[idx].rate.duration.time.scale),
+				aoc_s->item[idx].rate.duration.granularity.length,
+				sig_pri_aoc_scale_to_ast(aoc_s->item[idx].rate.duration.granularity.scale),
+				aoc_s->item[idx].rate.duration.charging_type);
 			break;
 		case PRI_AOC_RATE_TYPE_FLAT:
-			strcat(prefix, "/");
-			strcat(prefix, rate_str);
-			ast_str_append(&msg, 0, "%s/Currency: %s\r\n", prefix,
+			ast_aoc_s_add_rate_flat(decoded,
+				charged_item,
+				aoc_s->item[idx].rate.flat.amount.cost,
+				sig_pri_aoc_multiplier_from_pri(aoc_s->item[idx].rate.flat.amount.multiplier),
 				aoc_s->item[idx].rate.flat.currency);
-			sig_pri_aoc_amount(&msg, prefix, &aoc_s->item[idx].rate.flat.amount);
 			break;
 		case PRI_AOC_RATE_TYPE_VOLUME:
-			strcat(prefix, "/");
-			strcat(prefix, rate_str);
-			ast_str_append(&msg, 0, "%s/Currency: %s\r\n", prefix,
+			ast_aoc_s_add_rate_volume(decoded,
+				charged_item,
+				aoc_s->item[idx].rate.volume.unit,
+				aoc_s->item[idx].rate.volume.amount.cost,
+				sig_pri_aoc_multiplier_from_pri(aoc_s->item[idx].rate.volume.amount.multiplier),
 				aoc_s->item[idx].rate.volume.currency);
-			sig_pri_aoc_amount(&msg, prefix, &aoc_s->item[idx].rate.volume.amount);
-			ast_str_append(&msg, 0, "%s/Unit: %s\r\n", prefix,
-				sig_pri_aoc_volume_unit_str(aoc_s->item[idx].rate.volume.unit));
 			break;
 		case PRI_AOC_RATE_TYPE_SPECIAL_CODE:
-			ast_str_append(&msg, 0, "%s/%s: %d\r\n", prefix, rate_str,
+			ast_aoc_s_add_rate_special_charge_code(decoded,
+				charged_item,
 				aoc_s->item[idx].rate.special);
 			break;
+		case PRI_AOC_RATE_TYPE_FREE:
+			ast_aoc_s_add_rate_free(decoded, charged_item, 0);
+			break;
+		case PRI_AOC_RATE_TYPE_FREE_FROM_BEGINNING:
+			ast_aoc_s_add_rate_free(decoded, charged_item, 1);
+			break;
 		default:
-			break;
-		}
-	}
-
-	ast_manager_event(owner, EVENT_FLAG_AOC, "AOC-S", "%s", ast_str_buffer(msg));
-	ast_free(msg);
+			ast_aoc_s_add_rate_na(decoded, charged_item);
+			break;
+		}
+	}
+
+	if (passthrough && (encoded = ast_aoc_encode(decoded, &encoded_size, owner))) {
+		ast_queue_control_data(owner, AST_CONTROL_AOC, encoded, encoded_size);
+	}
+
+	ast_aoc_manager_event(decoded, owner);
+
+	ast_aoc_destroy_decoded(decoded);
+	ast_aoc_destroy_encoded(encoded);
 }
 #endif	/* defined(HAVE_PRI_AOC_EVENTS) */
 
 #if defined(HAVE_PRI_AOC_EVENTS)
 /*!
  * \internal
- * \brief Handle the AOC-D event.
- * \since 1.8
- *
- * \param aoc_d AOC-D event parameters.
+ * \brief Generate AOC Request Response
+ * \since 1.8
+ *
+ * \param aoc_request
+ *
+ * \note Assumes the pri->lock is already obtained.
+ * \note Assumes the sig_pri private is locked
+ * \note Assumes the owner channel lock is already obtained.
+ *
+ * \return Nothing
+ */
+static void sig_pri_aoc_request_from_pri(const struct pri_subcmd_aoc_request *aoc_request, struct sig_pri_chan *pvt, q931_call *call)
+{
+	int request;
+
+	if (!aoc_request) {
+		return;
+	}
+
+	request = aoc_request->charging_request;
+
+	if (request & PRI_AOC_REQUEST_S) {
+		if (pvt->pri->aoc_passthrough_flag & SIG_PRI_AOC_GRANT_S) {
+			/* An AOC-S response must come from the other side, so save off this invoke_id
+			 * and see if an AOC-S message comes in before the call is answered. */
+			pvt->aoc_s_request_invoke_id = aoc_request->invoke_id;
+			pvt->aoc_s_request_invoke_id_valid = 1;
+
+		} else {
+			pri_aoc_s_request_response_send(pvt->pri->pri,
+				call,
+				aoc_request->invoke_id,
+				NULL);
+		}
+	}
+
+	if (request & PRI_AOC_REQUEST_D) {

[... 1701 lines stripped ...]



More information about the asterisk-commits mailing list