2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2010, Digium, Inc.
6 * David Vossel <dvossel@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief generic AOC payload generation encoding and decoding
23 * \author David Vossel <dvossel@digium.com>
27 <support_level>core</support_level>
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
33 #include "asterisk/aoc.h"
34 #include "asterisk/utils.h"
35 #include "asterisk/strings.h"
36 #include "asterisk/_private.h"
37 #include "asterisk/cli.h"
38 #include "asterisk/manager.h"
39 #include "asterisk/stasis_channels.h"
40 #include "asterisk/stasis_message_router.h"
43 <managerEvent language="en_US" name="AOC-S">
44 <managerEventInstance class="EVENT_FLAG_AOC">
45 <synopsis>Raised when an Advice of Charge message is sent at the beginning of a call.</synopsis>
47 <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
48 <parameter name="Chargeable" />
49 <parameter name="RateType">
51 <enum name="NotAvailable" />
53 <enum name="FreeFromBeginning" />
54 <enum name="Duration" />
56 <enum name="Volume" />
57 <enum name="SpecialCode" />
60 <parameter name="Currency" />
61 <parameter name="Name" />
62 <parameter name="Cost" />
63 <parameter name="Multiplier">
65 <enum name="1/1000" />
74 <parameter name="ChargingType" />
75 <parameter name="StepFunction" />
76 <parameter name="Granularity" />
77 <parameter name="Length" />
78 <parameter name="Scale" />
79 <parameter name="Unit">
81 <enum name="Octect" />
82 <enum name="Segment" />
83 <enum name="Message" />
86 <parameter name="SpecialCode" />
88 </managerEventInstance>
90 <managerEvent language="en_US" name="AOC-D">
91 <managerEventInstance class="EVENT_FLAG_AOC">
92 <synopsis>Raised when an Advice of Charge message is sent during a call.</synopsis>
94 <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
95 <parameter name="Charge" />
96 <parameter name="Type">
98 <enum name="NotAvailable" />
100 <enum name="Currency" />
101 <enum name="Units" />
104 <parameter name="BillingID">
106 <enum name="Normal" />
107 <enum name="Reverse" />
108 <enum name="CreditCard" />
109 <enum name="CallForwardingUnconditional" />
110 <enum name="CallForwardingBusy" />
111 <enum name="CallForwardingNoReply" />
112 <enum name="CallDeflection" />
113 <enum name="CallTransfer" />
114 <enum name="NotAvailable" />
117 <parameter name="TotalType">
119 <enum name="SubTotal" />
120 <enum name="Total" />
123 <parameter name="Currency" />
124 <parameter name="Name" />
125 <parameter name="Cost" />
126 <parameter name="Multiplier">
128 <enum name="1/1000" />
129 <enum name="1/100" />
137 <parameter name="Units" />
138 <parameter name="NumberOf" />
139 <parameter name="TypeOf" />
141 </managerEventInstance>
143 <managerEvent language="en_US" name="AOC-E">
144 <managerEventInstance class="EVENT_FLAG_AOC">
145 <synopsis>Raised when an Advice of Charge message is sent at the end of a call.</synopsis>
147 <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
148 <parameter name="ChargingAssociation" />
149 <parameter name="Number" />
150 <parameter name="Plan" />
151 <parameter name="ID" />
152 <xi:include xpointer="xpointer(/docs/managerEvent[@name='AOC-D']/managerEventInstance/syntax/parameter)" />
154 </managerEventInstance>
158 /* Encoded Payload Flags */
159 #define AST_AOC_ENCODED_TYPE_REQUEST (0 << 0)
160 #define AST_AOC_ENCODED_TYPE_D (1 << 0)
161 #define AST_AOC_ENCODED_TYPE_E (2 << 0)
162 #define AST_AOC_ENCODED_TYPE_S (3 << 0)
164 #define AST_AOC_ENCODED_REQUEST_S (1 << 2)
165 #define AST_AOC_ENCODED_REQUEST_D (1 << 3)
166 #define AST_AOC_ENCODED_REQUEST_E (1 << 4)
168 #define AST_AOC_ENCODED_CHARGE_NA (0 << 5)
169 #define AST_AOC_ENCODED_CHARGE_FREE (1 << 5)
170 #define AST_AOC_ENCODED_CHARGE_CURRENCY (2 << 5)
171 #define AST_AOC_ENCODED_CHARGE_UNIT (3 << 5)
173 #define AST_AOC_ENCODED_CHARGE_SUBTOTAL (1 << 7)
174 #define AST_AOC_ENCODED_CHARGE_TOTAL (0 << 7)
176 #define AST_AOC_ENCODE_VERSION 1
179 static char aoc_debug_enabled = 0;
180 static void aoc_display_decoded_debug(const struct ast_aoc_decoded *decoded, int decoding, struct ast_channel *chan);
181 static int aoc_s_add_entry(struct ast_aoc_decoded *decoded, struct ast_aoc_s_entry *entry);
183 /* AOC Payload Header. Holds all the encoded AOC data to pass on the wire */
184 struct ast_aoc_encoded {
188 unsigned char data[0];
191 /* Decoded AOC data */
192 struct ast_aoc_decoded {
193 enum ast_aoc_type msg_type;
194 enum ast_aoc_charge_type charge_type;
195 enum ast_aoc_request request_flag;
196 enum ast_aoc_total_type total_type;
198 /* currency information */
199 enum ast_aoc_currency_multiplier multiplier;
200 unsigned int currency_amount;
201 char currency_name[AOC_CURRENCY_NAME_SIZE];
203 /* unit information */
205 struct ast_aoc_unit_entry unit_list[32];
208 enum ast_aoc_billing_id billing_id;
210 /* Charging Association information */
211 struct ast_aoc_charging_association charging_association;
213 /* AOC-S charge information */
215 struct ast_aoc_s_entry aoc_s_entries[10];
217 /* Is this an AOC Termination Request */
218 char termination_request;
221 /*! \brief AOC Payload Information Elements */
226 AOC_IE_CHARGING_ASSOCIATION = 4,
228 AOC_IE_TERMINATION_REQUEST = 6,
231 /*! \brief AOC IE payload header */
232 struct aoc_pl_ie_hdr {
236 } __attribute__((packed));
238 struct aoc_ie_currency {
241 char name[AOC_CURRENCY_NAME_SIZE];
242 } __attribute__((packed));
247 uint8_t valid_amount;
249 } __attribute__((packed));
251 struct aoc_ie_billing {
253 } __attribute__((packed));
255 struct aoc_ie_charging_association {
256 struct ast_aoc_charging_association ca;
257 } __attribute__((packed));
259 struct aoc_ie_charging_rate {
260 struct ast_aoc_s_entry entry;
261 } __attribute__((packed));
263 struct ast_aoc_decoded *ast_aoc_create(const enum ast_aoc_type msg_type,
264 const enum ast_aoc_charge_type charge_type,
265 const enum ast_aoc_request requests)
267 struct ast_aoc_decoded *decoded = NULL;
270 if (((unsigned int) charge_type > AST_AOC_CHARGE_UNIT) ||
271 ((unsigned int) msg_type > AST_AOC_E) ||
272 ((msg_type == AST_AOC_REQUEST) && !requests)) {
274 ast_log(LOG_WARNING, "Failed to create ast_aoc_decoded object, invalid input\n");
278 if (!(decoded = ast_calloc(1, sizeof(struct ast_aoc_decoded)))) {
279 ast_log(LOG_WARNING, "Failed to create ast_aoc_decoded object \n");
283 decoded->msg_type = msg_type;
285 if (msg_type == AST_AOC_REQUEST) {
286 decoded->request_flag = requests;
287 } else if ((msg_type == AST_AOC_D) || (msg_type == AST_AOC_E)) {
288 decoded->charge_type = charge_type;
294 void *ast_aoc_destroy_decoded(struct ast_aoc_decoded *decoded)
300 void *ast_aoc_destroy_encoded(struct ast_aoc_encoded *encoded)
306 static void aoc_parse_ie_charging_rate(struct ast_aoc_decoded *decoded, const struct aoc_ie_charging_rate *ie)
308 struct ast_aoc_s_entry entry = { 0, };
310 entry.charged_item = ntohs(ie->entry.charged_item);
311 entry.rate_type = ntohs(ie->entry.rate_type);
313 switch (entry.rate_type) {
314 case AST_AOC_RATE_TYPE_DURATION:
315 entry.rate.duration.multiplier = ntohs(ie->entry.rate.duration.multiplier);
316 entry.rate.duration.amount = ntohl(ie->entry.rate.duration.amount);
317 entry.rate.duration.time = ntohl(ie->entry.rate.duration.time);
318 entry.rate.duration.time_scale = ntohs(ie->entry.rate.duration.time_scale);
319 entry.rate.duration.granularity_time = ntohl(ie->entry.rate.duration.granularity_time);
320 entry.rate.duration.granularity_time_scale = ntohs(ie->entry.rate.duration.granularity_time_scale);
321 entry.rate.duration.charging_type = ie->entry.rate.duration.charging_type; /* only one byte */
323 if (!ast_strlen_zero(ie->entry.rate.duration.currency_name)) {
324 ast_copy_string(entry.rate.duration.currency_name,
325 ie->entry.rate.duration.currency_name,
326 sizeof(entry.rate.duration.currency_name));
329 case AST_AOC_RATE_TYPE_FLAT:
330 entry.rate.flat.multiplier = ntohs(ie->entry.rate.flat.multiplier);
331 entry.rate.flat.amount = ntohl(ie->entry.rate.flat.amount);
332 if (!ast_strlen_zero(ie->entry.rate.flat.currency_name)) {
333 ast_copy_string(entry.rate.flat.currency_name,
334 ie->entry.rate.flat.currency_name,
335 sizeof(entry.rate.flat.currency_name));
338 case AST_AOC_RATE_TYPE_VOLUME:
339 entry.rate.volume.multiplier = ntohs(ie->entry.rate.volume.multiplier);
340 entry.rate.volume.amount = ntohl(ie->entry.rate.volume.amount);
341 entry.rate.volume.volume_unit = ntohs(ie->entry.rate.volume.volume_unit);
342 if (!ast_strlen_zero(ie->entry.rate.volume.currency_name)) {
343 ast_copy_string(entry.rate.volume.currency_name,
344 ie->entry.rate.volume.currency_name,
345 sizeof(entry.rate.volume.currency_name));
348 case AST_AOC_RATE_TYPE_SPECIAL_CODE:
349 entry.rate.special_code = ntohs(ie->entry.rate.special_code);
353 aoc_s_add_entry(decoded, &entry);
356 static int aoc_parse_ie(struct ast_aoc_decoded *decoded, unsigned char *data, unsigned int datalen)
361 while (datalen >= 2) {
364 if (len > datalen -2) {
365 ast_log(LOG_ERROR, "AOC information element length exceeds the total message size\n");
370 case AOC_IE_CURRENCY:
371 if (len == sizeof(struct aoc_ie_currency)) {
372 struct aoc_ie_currency ie;
373 memcpy(&ie, data + 2, len);
374 decoded->currency_amount = ntohl(ie.amount);
375 decoded->multiplier = ie.multiplier; /* only one byte */
376 memcpy(decoded->currency_name, ie.name, sizeof(decoded->currency_name));
378 ast_log(LOG_WARNING, "Received invalid currency ie\n");
382 if (len == sizeof(struct aoc_ie_unit)) {
383 struct aoc_ie_unit ie;
384 memcpy(&ie, data + 2, len);
385 ast_aoc_add_unit_entry(decoded, ie.valid_amount, ntohl(ie.amount), ie.valid_type, ie.type);
387 ast_log(LOG_WARNING, "Received invalid unit ie\n");
391 if (len == sizeof(struct aoc_ie_billing)) {
392 struct aoc_ie_billing ie;
393 memcpy(&ie, data + 2, len);
394 decoded->billing_id = ie.id; /* only one byte */
396 ast_log(LOG_WARNING, "Received invalid billing ie\n");
399 case AOC_IE_CHARGING_ASSOCIATION:
400 if (len == sizeof(struct aoc_ie_charging_association)) {
401 memcpy(&decoded->charging_association, data + 2, sizeof(decoded->charging_association));
402 /* everything in the charging_association struct is a single byte except for the id */
403 if (decoded->charging_association.charging_type == AST_AOC_CHARGING_ASSOCIATION_ID) {
404 decoded->charging_association.charge.id = ntohl(decoded->charging_association.charge.id);
407 ast_log(LOG_WARNING, "Received invalid charging association ie\n");
411 if (len == sizeof(struct aoc_ie_charging_rate)) {
412 struct aoc_ie_charging_rate ie;
413 memcpy(&ie, data + 2, len);
414 aoc_parse_ie_charging_rate(decoded, &ie);
416 ast_log(LOG_WARNING, "Received invalid charging rate ie\n");
419 case AOC_IE_TERMINATION_REQUEST:
421 decoded->termination_request = 1;
423 ast_log(LOG_WARNING, "Received invalid termination request ie\n");
427 ast_log(LOG_WARNING, "Unknown AOC Information Element, ignoring.\n");
430 datalen -= (len + 2);
436 struct ast_aoc_decoded *ast_aoc_decode(struct ast_aoc_encoded *encoded, size_t size, struct ast_channel *chan)
438 struct ast_aoc_decoded *decoded;
440 /* verify our encoded payload is actually large enough to hold all the ies */
441 if ((size - (sizeof(struct ast_aoc_encoded)) != ntohs(encoded->datalen))) {
442 ast_log(LOG_WARNING, "Corrupted aoc encoded object, can not decode\n");
446 if (!(decoded = ast_calloc(1, sizeof(struct ast_aoc_decoded)))) {
447 ast_log(LOG_WARNING, "Failed to create ast_aoc_decoded object \n");
453 if ((encoded->flags & AST_AOC_ENCODED_TYPE_S) == AST_AOC_ENCODED_TYPE_S) {
454 decoded->msg_type = AST_AOC_S;
455 } else if (encoded->flags & AST_AOC_ENCODED_TYPE_E) {
456 decoded->msg_type = AST_AOC_E;
457 } else if (encoded->flags & AST_AOC_ENCODED_TYPE_D) {
458 decoded->msg_type = AST_AOC_D;
460 decoded->msg_type = AST_AOC_REQUEST;
463 if (decoded->msg_type == AST_AOC_REQUEST) {
464 if (encoded->flags & AST_AOC_ENCODED_REQUEST_S) {
465 decoded->request_flag |= AST_AOC_REQUEST_S;
467 if (encoded->flags & AST_AOC_ENCODED_REQUEST_D) {
468 decoded->request_flag |= AST_AOC_REQUEST_D;
470 if (encoded->flags & AST_AOC_ENCODED_REQUEST_E) {
471 decoded->request_flag |= AST_AOC_REQUEST_E;
473 } else if ((decoded->msg_type == AST_AOC_D) || (decoded->msg_type == AST_AOC_E)) {
474 if ((encoded->flags & AST_AOC_ENCODED_CHARGE_UNIT) == AST_AOC_ENCODED_CHARGE_UNIT) {
475 decoded->charge_type = AST_AOC_CHARGE_UNIT;
476 } else if ((encoded->flags & AST_AOC_ENCODED_CHARGE_CURRENCY) == AST_AOC_ENCODED_CHARGE_CURRENCY) {
477 decoded->charge_type = AST_AOC_CHARGE_CURRENCY;
478 } else if ((encoded->flags & AST_AOC_ENCODED_CHARGE_FREE) == AST_AOC_ENCODED_CHARGE_FREE) {
479 decoded->charge_type = AST_AOC_CHARGE_FREE;
481 decoded->charge_type = AST_AOC_CHARGE_NA;
484 if (encoded->flags & AST_AOC_ENCODED_CHARGE_SUBTOTAL) {
485 decoded->total_type = AST_AOC_SUBTOTAL;
489 /* decode information elements */
490 aoc_parse_ie(decoded, encoded->data, ntohs(encoded->datalen));
492 if (aoc_debug_enabled) {
493 aoc_display_decoded_debug(decoded, 1, chan);
500 unsigned char buf[1024];
506 * \brief append an AOC information element
507 * \note data is expected to already be in network byte order at this point
509 static int aoc_append_ie(struct aoc_ie_data *ied, unsigned short ie_id, const void *data, unsigned short datalen)
511 if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
512 ast_log(LOG_WARNING, "Failure to append AOC information element, out of space \n");
515 ied->buf[ied->pos++] = ie_id;
516 ied->buf[ied->pos++] = datalen;
518 memcpy(ied->buf + ied->pos, data, datalen);
524 static void aoc_create_ie_data_charging_rate(const struct ast_aoc_s_entry *entry, struct aoc_ie_charging_rate *ie)
526 ie->entry.charged_item = htons(entry->charged_item);
527 ie->entry.rate_type = htons(entry->rate_type);
529 switch (entry->rate_type) {
530 case AST_AOC_RATE_TYPE_DURATION:
531 ie->entry.rate.duration.multiplier = htons(entry->rate.duration.multiplier);
532 ie->entry.rate.duration.amount = htonl(entry->rate.duration.amount);
533 ie->entry.rate.duration.time = htonl(entry->rate.duration.time);
534 ie->entry.rate.duration.time_scale = htons(entry->rate.duration.time_scale);
535 ie->entry.rate.duration.granularity_time = htonl(entry->rate.duration.granularity_time);
536 ie->entry.rate.duration.granularity_time_scale = htons(entry->rate.duration.granularity_time_scale);
537 ie->entry.rate.duration.charging_type = entry->rate.duration.charging_type; /* only one byte */
539 if (!ast_strlen_zero(entry->rate.duration.currency_name)) {
540 ast_copy_string(ie->entry.rate.duration.currency_name,
541 entry->rate.duration.currency_name,
542 sizeof(ie->entry.rate.duration.currency_name));
545 case AST_AOC_RATE_TYPE_FLAT:
546 ie->entry.rate.flat.multiplier = htons(entry->rate.flat.multiplier);
547 ie->entry.rate.flat.amount = htonl(entry->rate.flat.amount);
548 if (!ast_strlen_zero(entry->rate.flat.currency_name)) {
549 ast_copy_string(ie->entry.rate.flat.currency_name,
550 entry->rate.flat.currency_name,
551 sizeof(ie->entry.rate.flat.currency_name));
554 case AST_AOC_RATE_TYPE_VOLUME:
555 ie->entry.rate.volume.multiplier = htons(entry->rate.volume.multiplier);
556 ie->entry.rate.volume.amount = htonl(entry->rate.volume.amount);
557 ie->entry.rate.volume.volume_unit = htons(entry->rate.volume.volume_unit);
558 if (!ast_strlen_zero(entry->rate.volume.currency_name)) {
559 ast_copy_string(ie->entry.rate.volume.currency_name,
560 entry->rate.volume.currency_name,
561 sizeof(ie->entry.rate.volume.currency_name));
564 case AST_AOC_RATE_TYPE_SPECIAL_CODE:
565 ie->entry.rate.special_code = htons(entry->rate.special_code);
570 static void aoc_create_ie_data(struct ast_aoc_decoded *decoded, struct aoc_ie_data *ied)
574 if (decoded->currency_amount) {
575 struct aoc_ie_currency ie = {
576 .amount = htonl(decoded->currency_amount),
577 .multiplier = decoded->multiplier, /* only one byte */
581 if (!ast_strlen_zero(decoded->currency_name)) {
582 ast_copy_string(ie.name, decoded->currency_name, sizeof(ie.name));
585 aoc_append_ie(ied, AOC_IE_CURRENCY, (const void *) &ie, sizeof(ie));
588 if (decoded->unit_count) {
589 struct aoc_ie_unit ie = { 0 };
592 for (i = 0; i < decoded->unit_count; i++) {
593 ie.valid_amount = decoded->unit_list[i].valid_amount; /* only one byte */
594 ie.amount = htonl(decoded->unit_list[i].amount);
595 ie.valid_type = decoded->unit_list[i].valid_type; /* only one byte */
596 ie.type = decoded->unit_list[i].type; /* only one byte */
597 aoc_append_ie(ied, AOC_IE_UNIT, (const void *) &ie, sizeof(ie));
601 if (decoded->billing_id) {
602 struct aoc_ie_billing ie;
603 ie.id = decoded->billing_id; /* only one byte */
604 aoc_append_ie(ied, AOC_IE_BILLING, (const void *) &ie, sizeof(ie));
607 if (decoded->charging_association.charging_type != AST_AOC_CHARGING_ASSOCIATION_NA) {
608 struct aoc_ie_charging_association ie;
609 memset(&ie, 0, sizeof(ie));
610 ie.ca.charging_type = decoded->charging_association.charging_type; /* only one byte */
611 if (decoded->charging_association.charging_type == AST_AOC_CHARGING_ASSOCIATION_NUMBER) {
612 ie.ca.charge.number.plan = decoded->charging_association.charge.number.plan; /* only one byte */
613 ast_copy_string(ie.ca.charge.number.number,
614 decoded->charging_association.charge.number.number,
615 sizeof(ie.ca.charge.number.number));
616 } else if (decoded->charging_association.charging_type == AST_AOC_CHARGING_ASSOCIATION_ID) {
617 ie.ca.charge.id = htonl(decoded->charging_association.charge.id);
619 aoc_append_ie(ied, AOC_IE_CHARGING_ASSOCIATION, (const void *) &ie, sizeof(ie));
622 if (decoded->aoc_s_count) {
623 struct aoc_ie_charging_rate ie;
625 for (i = 0; i < decoded->aoc_s_count; i++) {
626 memset(&ie, 0, sizeof(ie));
627 aoc_create_ie_data_charging_rate(&decoded->aoc_s_entries[i], &ie);
628 aoc_append_ie(ied, AOC_IE_RATE, (const void *) &ie, sizeof(ie));
632 if (decoded->termination_request) {
633 aoc_append_ie(ied, AOC_IE_TERMINATION_REQUEST, NULL, 0);
637 struct ast_aoc_encoded *ast_aoc_encode(struct ast_aoc_decoded *decoded, size_t *out_size, struct ast_channel *chan)
639 struct aoc_ie_data ied;
640 struct ast_aoc_encoded *encoded = NULL;
643 if (!decoded || !out_size) {
649 /* create information element buffer before allocating the payload,
650 * by doing this the exact size of the payload + the id data can be
651 * allocated all at once. */
652 aoc_create_ie_data(decoded, &ied);
654 size = sizeof(struct ast_aoc_encoded) + ied.pos;
656 if (!(encoded = ast_calloc(1, size))) {
657 ast_log(LOG_WARNING, "Failed to create ast_aoc_encoded object during decode routine. \n");
661 /* -- Set ie data buffer */
663 /* this is safe because encoded was allocated to fit this perfectly */
664 memcpy(encoded->data, ied.buf, ied.pos);
665 encoded->datalen = htons(ied.pos);
668 /* --- Set Flags --- */
669 switch (decoded->msg_type) {
671 encoded->flags = AST_AOC_ENCODED_TYPE_S;
674 encoded->flags = AST_AOC_ENCODED_TYPE_D;
677 encoded->flags = AST_AOC_ENCODED_TYPE_E;
679 case AST_AOC_REQUEST:
680 encoded->flags = AST_AOC_ENCODED_TYPE_REQUEST;
685 /* if it is type request, set the types requested, else set charge type */
686 if (decoded->msg_type == AST_AOC_REQUEST) {
687 if (decoded->request_flag & AST_AOC_REQUEST_S) {
688 encoded->flags |= AST_AOC_ENCODED_REQUEST_S;
690 if (decoded->request_flag & AST_AOC_REQUEST_D) {
691 encoded->flags |= AST_AOC_ENCODED_REQUEST_D;
693 if (decoded->request_flag & AST_AOC_REQUEST_E) {
694 encoded->flags |= AST_AOC_ENCODED_REQUEST_E;
696 } else if ((decoded->msg_type == AST_AOC_D) || (decoded->msg_type == AST_AOC_E)) {
697 switch (decoded->charge_type) {
698 case AST_AOC_CHARGE_UNIT:
699 encoded->flags |= AST_AOC_ENCODED_CHARGE_UNIT;
701 case AST_AOC_CHARGE_CURRENCY:
702 encoded->flags |= AST_AOC_ENCODED_CHARGE_CURRENCY;
704 case AST_AOC_CHARGE_FREE:
705 encoded->flags |= AST_AOC_ENCODED_CHARGE_FREE;
706 case AST_AOC_CHARGE_NA:
708 encoded->flags |= AST_AOC_ENCODED_CHARGE_NA;
712 if (decoded->total_type == AST_AOC_SUBTOTAL) {
713 encoded->flags |= AST_AOC_ENCODED_CHARGE_SUBTOTAL;
717 /* --- Set Version Number --- */
718 encoded->version = AST_AOC_ENCODE_VERSION;
720 /* set the output size */
723 if (aoc_debug_enabled) {
724 aoc_display_decoded_debug(decoded, 0, chan);
730 static int aoc_s_add_entry(struct ast_aoc_decoded *decoded, struct ast_aoc_s_entry *entry)
732 if (decoded->aoc_s_count >= ARRAY_LEN(decoded->aoc_s_entries)) {
736 decoded->aoc_s_entries[decoded->aoc_s_count] = *entry;
737 decoded->aoc_s_count++;
743 unsigned int ast_aoc_s_get_count(struct ast_aoc_decoded *decoded)
745 return decoded->aoc_s_count;
748 const struct ast_aoc_s_entry *ast_aoc_s_get_rate_info(struct ast_aoc_decoded *decoded, unsigned int entry_number)
750 if (entry_number >= decoded->aoc_s_count) {
754 return (const struct ast_aoc_s_entry *) &decoded->aoc_s_entries[entry_number];
757 int ast_aoc_s_add_rate_duration(struct ast_aoc_decoded *decoded,
758 enum ast_aoc_s_charged_item charged_item,
760 enum ast_aoc_currency_multiplier multiplier,
761 const char *currency_name,
763 enum ast_aoc_time_scale time_scale,
764 unsigned long granularity_time,
765 enum ast_aoc_time_scale granularity_time_scale,
769 struct ast_aoc_s_entry entry = { 0, };
771 entry.charged_item = charged_item;
772 entry.rate_type = AST_AOC_RATE_TYPE_DURATION;
773 entry.rate.duration.amount = amount;
774 entry.rate.duration.multiplier = multiplier;
775 entry.rate.duration.time = time;
776 entry.rate.duration.time_scale = time_scale;
777 entry.rate.duration.granularity_time = granularity_time;
778 entry.rate.duration.granularity_time_scale = granularity_time_scale;
779 entry.rate.duration.charging_type = step_function ? 1 : 0;
781 if (!ast_strlen_zero(currency_name)) {
782 ast_copy_string(entry.rate.duration.currency_name, currency_name, sizeof(entry.rate.duration.currency_name));
785 return aoc_s_add_entry(decoded, &entry);
788 int ast_aoc_s_add_rate_flat(struct ast_aoc_decoded *decoded,
789 enum ast_aoc_s_charged_item charged_item,
791 enum ast_aoc_currency_multiplier multiplier,
792 const char *currency_name)
794 struct ast_aoc_s_entry entry = { 0, };
796 entry.charged_item = charged_item;
797 entry.rate_type = AST_AOC_RATE_TYPE_FLAT;
798 entry.rate.flat.amount = amount;
799 entry.rate.flat.multiplier = multiplier;
801 if (!ast_strlen_zero(currency_name)) {
802 ast_copy_string(entry.rate.flat.currency_name, currency_name, sizeof(entry.rate.flat.currency_name));
805 return aoc_s_add_entry(decoded, &entry);
809 int ast_aoc_s_add_rate_volume(struct ast_aoc_decoded *decoded,
810 enum ast_aoc_s_charged_item charged_item,
811 enum ast_aoc_volume_unit volume_unit,
813 enum ast_aoc_currency_multiplier multiplier,
814 const char *currency_name)
816 struct ast_aoc_s_entry entry = { 0, };
818 entry.charged_item = charged_item;
819 entry.rate_type = AST_AOC_RATE_TYPE_VOLUME;
820 entry.rate.volume.multiplier = multiplier;
821 entry.rate.volume.amount = amount;
822 entry.rate.volume.volume_unit = volume_unit;
824 if (!ast_strlen_zero(currency_name)) {
825 ast_copy_string(entry.rate.volume.currency_name, currency_name, sizeof(entry.rate.volume.currency_name));
828 return aoc_s_add_entry(decoded, &entry);
831 int ast_aoc_s_add_rate_special_charge_code(struct ast_aoc_decoded *decoded,
832 enum ast_aoc_s_charged_item charged_item,
835 struct ast_aoc_s_entry entry = { 0, };
837 entry.charged_item = charged_item;
838 entry.rate_type = AST_AOC_RATE_TYPE_SPECIAL_CODE;
839 entry.rate.special_code = code;
841 return aoc_s_add_entry(decoded, &entry);
844 int ast_aoc_s_add_rate_free(struct ast_aoc_decoded *decoded,
845 enum ast_aoc_s_charged_item charged_item,
848 struct ast_aoc_s_entry entry = { 0, };
850 entry.charged_item = charged_item;
851 entry.rate_type = from_beginning ? AST_AOC_RATE_TYPE_FREE_FROM_BEGINNING : AST_AOC_RATE_TYPE_FREE;
853 return aoc_s_add_entry(decoded, &entry);
856 int ast_aoc_s_add_rate_na(struct ast_aoc_decoded *decoded,
857 enum ast_aoc_s_charged_item charged_item)
859 struct ast_aoc_s_entry entry = { 0, };
861 entry.charged_item = charged_item;
862 entry.rate_type = AST_AOC_RATE_TYPE_NA;
864 return aoc_s_add_entry(decoded, &entry);
867 int ast_aoc_s_add_special_arrangement(struct ast_aoc_decoded *decoded,
870 struct ast_aoc_s_entry entry = { 0, };
872 entry.charged_item = AST_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT;
873 entry.rate_type = AST_AOC_RATE_TYPE_SPECIAL_CODE;
874 entry.rate.special_code = code;
876 return aoc_s_add_entry(decoded, &entry);
879 enum ast_aoc_type ast_aoc_get_msg_type(struct ast_aoc_decoded *decoded)
881 return decoded->msg_type;
884 enum ast_aoc_charge_type ast_aoc_get_charge_type(struct ast_aoc_decoded *decoded)
886 return decoded->charge_type;
889 enum ast_aoc_request ast_aoc_get_request(struct ast_aoc_decoded *decoded)
891 return decoded->request_flag;
894 int ast_aoc_set_total_type(struct ast_aoc_decoded *decoded,
895 const enum ast_aoc_total_type type)
897 decoded->total_type = type;
901 enum ast_aoc_total_type ast_aoc_get_total_type(struct ast_aoc_decoded *decoded)
903 return decoded->total_type;
906 int ast_aoc_set_currency_info(struct ast_aoc_decoded *decoded,
907 const unsigned int amount,
908 const enum ast_aoc_currency_multiplier multiplier,
912 if (!ast_strlen_zero(name)) {
913 ast_copy_string(decoded->currency_name, name, sizeof(decoded->currency_name));
916 decoded->currency_amount = amount;
918 if (multiplier && (multiplier < AST_AOC_MULT_NUM_ENTRIES)) {
919 decoded->multiplier = multiplier;
921 decoded->multiplier = AST_AOC_MULT_ONE;
927 unsigned int ast_aoc_get_currency_amount(struct ast_aoc_decoded *decoded)
929 return decoded->currency_amount;
932 enum ast_aoc_currency_multiplier ast_aoc_get_currency_multiplier(struct ast_aoc_decoded *decoded)
934 return decoded->multiplier;
937 const char *ast_aoc_get_currency_multiplier_decimal(struct ast_aoc_decoded *decoded)
939 switch (decoded->multiplier) {
940 case AST_AOC_MULT_ONETHOUSANDTH:
942 case AST_AOC_MULT_ONEHUNDREDTH:
944 case AST_AOC_MULT_ONETENTH:
946 case AST_AOC_MULT_ONE:
948 case AST_AOC_MULT_TEN:
950 case AST_AOC_MULT_HUNDRED:
952 case AST_AOC_MULT_THOUSAND:
959 const char *ast_aoc_get_currency_name(struct ast_aoc_decoded *decoded)
961 return decoded->currency_name;
964 int ast_aoc_add_unit_entry(struct ast_aoc_decoded *decoded,
965 const unsigned int amount_is_present,
966 const unsigned int amount,
967 const unsigned int type_is_present,
968 const unsigned int type)
970 if ((decoded->msg_type == AST_AOC_REQUEST) ||
971 (decoded->unit_count >= ARRAY_LEN(decoded->unit_list))) {
975 if (!amount_is_present && !type_is_present) {
979 decoded->unit_list[decoded->unit_count].valid_amount = amount_is_present;
980 if (amount_is_present) {
981 decoded->unit_list[decoded->unit_count].amount = amount;
983 decoded->unit_list[decoded->unit_count].amount = 0;
986 decoded->unit_list[decoded->unit_count].valid_type = type_is_present;
987 if (type_is_present) {
988 decoded->unit_list[decoded->unit_count].type = type;
990 decoded->unit_list[decoded->unit_count].type = 0;
992 decoded->unit_count++;
997 const struct ast_aoc_unit_entry *ast_aoc_get_unit_info(struct ast_aoc_decoded *decoded, unsigned int entry_number)
999 if (entry_number >= decoded->unit_count) {
1003 return (const struct ast_aoc_unit_entry *) &decoded->unit_list[entry_number];
1006 unsigned int ast_aoc_get_unit_count(struct ast_aoc_decoded *decoded)
1008 return decoded->unit_count;
1011 int ast_aoc_set_billing_id(struct ast_aoc_decoded *decoded, const enum ast_aoc_billing_id id)
1013 if ((id >= AST_AOC_BILLING_NUM_ENTRIES) || (id < AST_AOC_BILLING_NA)) {
1017 decoded->billing_id = id;
1022 enum ast_aoc_billing_id ast_aoc_get_billing_id(struct ast_aoc_decoded *decoded)
1024 return decoded->billing_id;
1027 int ast_aoc_set_association_id(struct ast_aoc_decoded *decoded, const int id)
1029 if (decoded->msg_type != AST_AOC_E) {
1032 memset(&decoded->charging_association, 0, sizeof(decoded->charging_association));
1033 decoded->charging_association.charging_type = AST_AOC_CHARGING_ASSOCIATION_ID;
1034 decoded->charging_association.charge.id = id;
1038 const struct ast_aoc_charging_association *ast_aoc_get_association_info(struct ast_aoc_decoded *decoded)
1040 return &decoded->charging_association;
1043 int ast_aoc_set_association_number(struct ast_aoc_decoded *decoded, const char *num, uint8_t plan)
1045 if ((decoded->msg_type != AST_AOC_E) || ast_strlen_zero(num)) {
1048 memset(&decoded->charging_association, 0, sizeof(decoded->charging_association));
1049 decoded->charging_association.charging_type = AST_AOC_CHARGING_ASSOCIATION_NUMBER;
1050 decoded->charging_association.charge.number.plan = plan;
1051 ast_copy_string(decoded->charging_association.charge.number.number, num, sizeof(decoded->charging_association.charge.number.number));
1056 int ast_aoc_set_termination_request(struct ast_aoc_decoded *decoded)
1058 if (decoded->msg_type != AST_AOC_REQUEST) {
1061 decoded->termination_request = 1;
1066 int ast_aoc_get_termination_request(struct ast_aoc_decoded *decoded)
1068 return decoded->termination_request;
1073 * \brief Convert AST_AOC_VOLUME_UNIT to string.
1076 * \param value Value to convert to string.
1078 * \return String equivalent.
1080 static const char *aoc_volume_unit_str(enum ast_aoc_volume_unit value)
1086 case AST_AOC_VOLUME_UNIT_OCTET:
1089 case AST_AOC_VOLUME_UNIT_SEGMENT:
1092 case AST_AOC_VOLUME_UNIT_MESSAGE:
1101 * \brief Convert ast_aoc_charged_item to string.
1104 * \param value Value to convert to string.
1106 * \return String equivalent.
1108 static const char *aoc_charged_item_str(enum ast_aoc_s_charged_item value)
1114 case AST_AOC_CHARGED_ITEM_NA:
1115 str = "NotAvailable";
1117 case AST_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT:
1118 str = "SpecialArrangement";
1120 case AST_AOC_CHARGED_ITEM_BASIC_COMMUNICATION:
1121 str = "BasicCommunication";
1123 case AST_AOC_CHARGED_ITEM_CALL_ATTEMPT:
1124 str = "CallAttempt";
1126 case AST_AOC_CHARGED_ITEM_CALL_SETUP:
1129 case AST_AOC_CHARGED_ITEM_USER_USER_INFO:
1130 str = "UserUserInfo";
1132 case AST_AOC_CHARGED_ITEM_SUPPLEMENTARY_SERVICE:
1133 str = "SupplementaryService";
1141 * \brief Convert ast_aoc_total_type to string.
1144 * \param value Value to convert to string.
1146 * \return String equivalent.
1148 static const char *aoc_type_of_totaling_str(enum ast_aoc_total_type value)
1154 case AST_AOC_SUBTOTAL:
1166 * \brief Convert ast_aoc_rate_type to string.
1169 * \param value Value to convert to string.
1171 * \return String equivalent.
1173 static const char *aoc_rate_type_str(enum ast_aoc_s_rate_type value)
1179 case AST_AOC_RATE_TYPE_NA:
1180 str = "NotAvailable";
1182 case AST_AOC_RATE_TYPE_FREE:
1185 case AST_AOC_RATE_TYPE_FREE_FROM_BEGINNING:
1186 str = "FreeFromBeginning";
1188 case AST_AOC_RATE_TYPE_DURATION:
1191 case AST_AOC_RATE_TYPE_FLAT:
1194 case AST_AOC_RATE_TYPE_VOLUME:
1197 case AST_AOC_RATE_TYPE_SPECIAL_CODE:
1198 str = "SpecialCode";
1206 * \brief Convert AST_AOC_TIME_SCALE to string.
1209 * \param value Value to convert to string.
1211 * \return String equivalent.
1213 static const char *aoc_scale_str(enum ast_aoc_time_scale value)
1219 case AST_AOC_TIME_SCALE_HUNDREDTH_SECOND:
1220 str = "OneHundredthSecond";
1222 case AST_AOC_TIME_SCALE_TENTH_SECOND:
1223 str = "OneTenthSecond";
1225 case AST_AOC_TIME_SCALE_SECOND:
1228 case AST_AOC_TIME_SCALE_TEN_SECOND:
1231 case AST_AOC_TIME_SCALE_MINUTE:
1234 case AST_AOC_TIME_SCALE_HOUR:
1237 case AST_AOC_TIME_SCALE_DAY:
1244 static const char *aoc_charge_type_str(enum ast_aoc_charge_type value)
1250 case AST_AOC_CHARGE_NA:
1251 str = "NotAvailable";
1253 case AST_AOC_CHARGE_FREE:
1256 case AST_AOC_CHARGE_CURRENCY:
1259 case AST_AOC_CHARGE_UNIT:
1267 static const char *aoc_multiplier_str(enum ast_aoc_currency_multiplier mult)
1270 case AST_AOC_MULT_ONETHOUSANDTH:
1272 case AST_AOC_MULT_ONEHUNDREDTH:
1274 case AST_AOC_MULT_ONETENTH:
1276 case AST_AOC_MULT_ONE:
1278 case AST_AOC_MULT_TEN:
1280 case AST_AOC_MULT_HUNDRED:
1282 case AST_AOC_MULT_THOUSAND:
1284 case AST_AOC_MULT_NUM_ENTRIES:
1290 static const char *aoc_billingid_str(enum ast_aoc_billing_id billing_id)
1292 switch (billing_id) {
1293 case AST_AOC_BILLING_NORMAL:
1295 case AST_AOC_BILLING_REVERSE_CHARGE:
1297 case AST_AOC_BILLING_CREDIT_CARD:
1298 return "CreditCard";
1299 case AST_AOC_BILLING_CALL_FWD_UNCONDITIONAL:
1300 return "CallForwardingUnconditional";
1301 case AST_AOC_BILLING_CALL_FWD_BUSY:
1302 return "CallForwardingBusy";
1303 case AST_AOC_BILLING_CALL_FWD_NO_REPLY:
1304 return "CallForwardingNoReply";
1305 case AST_AOC_BILLING_CALL_DEFLECTION:
1306 return "CallDeflection";
1307 case AST_AOC_BILLING_CALL_TRANSFER:
1308 return "CallTransfer";
1309 case AST_AOC_BILLING_NA:
1310 return "NotAvailable";
1311 case AST_AOC_BILLING_NUM_ENTRIES:
1314 return "NotAvailable";
1317 int ast_aoc_test_encode_decode_match(struct ast_aoc_decoded *decoded)
1319 struct ast_aoc_decoded *new_decoded = NULL;
1320 struct ast_aoc_encoded *encoded = NULL;
1324 if (!(encoded = ast_aoc_encode(decoded, &size, NULL))) {
1328 if (!(new_decoded = ast_aoc_decode(encoded, size, NULL))) {
1333 if (memcmp(new_decoded, decoded, sizeof(struct ast_aoc_decoded))) {
1337 ast_aoc_destroy_decoded(new_decoded);
1338 ast_aoc_destroy_encoded(encoded);
1342 static char *aoc_cli_debug_enable(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1346 e->command = "aoc set debug";
1348 "Usage: 'aoc set debug on' to enable aoc debug, 'aoc set debug off' to disable debug.\n";
1354 return CLI_SHOWUSAGE;
1355 } else if(ast_true(a->argv[3])) {
1356 ast_cli(a->fd, "aoc debug enabled\n");
1357 aoc_debug_enabled = 1;
1358 } else if (ast_false(a->argv[3])) {
1359 ast_cli(a->fd, "aoc debug disabled\n");
1360 aoc_debug_enabled = 0;
1362 return CLI_SHOWUSAGE;
1371 * \brief Append the time structure to the event message string.
1374 * \param msg Event message string being built.
1375 * \param prefix Prefix to add to the amount lines.
1376 * \param name Name of the time structure to convert.
1377 * \param time Data to convert.
1378 * \param scale Data to convert.
1382 static void aoc_time_str(struct ast_str **msg, const char *prefix, const char *name, unsigned long time, enum ast_aoc_time_scale scale)
1384 ast_str_append(msg, 0, "%s/%s/Length: %lu\r\n", prefix, name, time);
1385 ast_str_append(msg, 0, "%s/%s/Scale: %s\r\n", prefix, name,
1386 aoc_scale_str(scale));
1391 * \brief Append the amount structure to the event message string.
1394 * \param msg Event message string being built.
1395 * \param prefix Prefix to add to the amount lines.
1396 * \param amount Data to convert.
1397 * \param multipler to convert
1401 static void aoc_amount_str(struct ast_str **msg, const char *prefix, unsigned int amount, enum ast_aoc_currency_multiplier mult)
1403 static const char name[] = "Amount";
1405 ast_str_append(msg, 0, "%s/%s/Cost: %u\r\n", prefix, name, amount);
1406 ast_str_append(msg, 0, "%s/%s/Multiplier: %s\r\n", prefix, name,
1407 aoc_multiplier_str(mult));
1410 static void aoc_request_event(const struct ast_aoc_decoded *decoded, struct ast_str **msg)
1412 if (decoded->request_flag) {
1413 ast_str_append(msg, 0, "AOCRequest:");
1414 if (decoded->request_flag & AST_AOC_REQUEST_S) {
1415 ast_str_append(msg, 0, "S");
1417 if (decoded->request_flag & AST_AOC_REQUEST_D) {
1418 ast_str_append(msg, 0, "D");
1420 if (decoded->request_flag & AST_AOC_REQUEST_E) {
1421 ast_str_append(msg, 0, "E");
1423 ast_str_append(msg, 0, "\r\n");
1426 ast_str_append(msg, 0, "AOCRequest: NONE\r\n");
1430 static void aoc_s_event(const struct ast_aoc_decoded *decoded, struct ast_str **msg)
1432 const char *rate_str;
1436 ast_str_append(msg, 0, "NumberRates: %d\r\n", decoded->aoc_s_count);
1437 for (idx = 0; idx < decoded->aoc_s_count; ++idx) {
1438 snprintf(prefix, sizeof(prefix), "Rate(%d)", idx);
1440 ast_str_append(msg, 0, "%s/Chargeable: %s\r\n", prefix,
1441 aoc_charged_item_str(decoded->aoc_s_entries[idx].charged_item));
1442 if (decoded->aoc_s_entries[idx].charged_item == AST_AOC_CHARGED_ITEM_NA) {
1445 rate_str = aoc_rate_type_str(decoded->aoc_s_entries[idx].rate_type);
1446 ast_str_append(msg, 0, "%s/Type: %s\r\n", prefix, rate_str);
1447 switch (decoded->aoc_s_entries[idx].rate_type) {
1448 case AST_AOC_RATE_TYPE_DURATION:
1449 strcat(prefix, "/");
1450 strcat(prefix, rate_str);
1451 ast_str_append(msg, 0, "%s/Currency: %s\r\n", prefix,
1452 decoded->aoc_s_entries[idx].rate.duration.currency_name);
1453 aoc_amount_str(msg, prefix,
1454 decoded->aoc_s_entries[idx].rate.duration.amount,
1455 decoded->aoc_s_entries[idx].rate.duration.multiplier);
1456 ast_str_append(msg, 0, "%s/ChargingType: %s\r\n", prefix,
1457 decoded->aoc_s_entries[idx].rate.duration.charging_type ?
1458 "StepFunction" : "ContinuousCharging");
1459 aoc_time_str(msg, prefix, "Time",
1460 decoded->aoc_s_entries[idx].rate.duration.time,
1461 decoded->aoc_s_entries[idx].rate.duration.time_scale);
1462 if (decoded->aoc_s_entries[idx].rate.duration.granularity_time) {
1463 aoc_time_str(msg, prefix, "Granularity",
1464 decoded->aoc_s_entries[idx].rate.duration.granularity_time,
1465 decoded->aoc_s_entries[idx].rate.duration.granularity_time_scale);
1468 case AST_AOC_RATE_TYPE_FLAT:
1469 strcat(prefix, "/");
1470 strcat(prefix, rate_str);
1471 ast_str_append(msg, 0, "%s/Currency: %s\r\n", prefix,
1472 decoded->aoc_s_entries[idx].rate.flat.currency_name);
1473 aoc_amount_str(msg, prefix,
1474 decoded->aoc_s_entries[idx].rate.flat.amount,
1475 decoded->aoc_s_entries[idx].rate.flat.multiplier);
1477 case AST_AOC_RATE_TYPE_VOLUME:
1478 strcat(prefix, "/");
1479 strcat(prefix, rate_str);
1480 ast_str_append(msg, 0, "%s/Currency: %s\r\n", prefix,
1481 decoded->aoc_s_entries[idx].rate.volume.currency_name);
1482 aoc_amount_str(msg, prefix,
1483 decoded->aoc_s_entries[idx].rate.volume.amount,
1484 decoded->aoc_s_entries[idx].rate.volume.multiplier);
1485 ast_str_append(msg, 0, "%s/Unit: %s\r\n", prefix,
1486 aoc_volume_unit_str(decoded->aoc_s_entries[idx].rate.volume.volume_unit));
1488 case AST_AOC_RATE_TYPE_SPECIAL_CODE:
1489 ast_str_append(msg, 0, "%s/%s: %d\r\n", prefix, rate_str,
1490 decoded->aoc_s_entries[idx].rate.special_code);
1498 static void aoc_d_event(const struct ast_aoc_decoded *decoded, struct ast_str **msg)
1500 const char *charge_str;
1504 charge_str = aoc_charge_type_str(decoded->charge_type);
1505 ast_str_append(msg, 0, "Type: %s\r\n", charge_str);
1507 switch (decoded->charge_type) {
1508 case AST_AOC_CHARGE_CURRENCY:
1509 case AST_AOC_CHARGE_UNIT:
1510 ast_str_append(msg, 0, "BillingID: %s\r\n",
1511 aoc_billingid_str(decoded->billing_id));
1512 ast_str_append(msg, 0, "TypeOfCharging: %s\r\n",
1513 aoc_type_of_totaling_str(decoded->total_type));
1519 switch (decoded->charge_type) {
1520 case AST_AOC_CHARGE_CURRENCY:
1521 ast_str_append(msg, 0, "%s: %s\r\n", charge_str,
1522 decoded->currency_name);
1523 aoc_amount_str(msg, charge_str,
1524 decoded->currency_amount,
1525 decoded->multiplier);
1527 case AST_AOC_CHARGE_UNIT:
1528 ast_str_append(msg, 0, "%s/NumberItems: %d\r\n", charge_str,
1529 decoded->unit_count);
1530 for (idx = 0; idx < decoded->unit_count; ++idx) {
1531 snprintf(prefix, sizeof(prefix), "%s/Item(%d)", charge_str, idx);
1532 if (decoded->unit_list[idx].valid_amount) {
1533 ast_str_append(msg, 0, "%s/NumberOf: %u\r\n", prefix,
1534 decoded->unit_list[idx].amount);
1536 if (decoded->unit_list[idx].valid_type) {
1537 ast_str_append(msg, 0, "%s/TypeOf: %d\r\n", prefix,
1538 decoded->unit_list[idx].type);
1547 static void aoc_e_event(const struct ast_aoc_decoded *decoded, struct ast_str **msg)
1549 const char *charge_str;
1553 charge_str = "ChargingAssociation";
1555 switch (decoded->charging_association.charging_type) {
1556 case AST_AOC_CHARGING_ASSOCIATION_NUMBER:
1557 snprintf(prefix, sizeof(prefix), "%s/Number", charge_str);
1558 ast_str_append(msg, 0, "%s: %s\r\n", prefix,
1559 decoded->charging_association.charge.number.number);
1560 ast_str_append(msg, 0, "%s/Plan: %d\r\n", prefix,
1561 decoded->charging_association.charge.number.plan);
1563 case AST_AOC_CHARGING_ASSOCIATION_ID:
1564 ast_str_append(msg, 0, "%s/ID: %d\r\n", charge_str, decoded->charging_association.charge.id);
1566 case AST_AOC_CHARGING_ASSOCIATION_NA:
1571 charge_str = aoc_charge_type_str(decoded->charge_type);
1572 ast_str_append(msg, 0, "Type: %s\r\n", charge_str);
1573 switch (decoded->charge_type) {
1574 case AST_AOC_CHARGE_CURRENCY:
1575 case AST_AOC_CHARGE_UNIT:
1576 ast_str_append(msg, 0, "BillingID: %s\r\n",
1577 aoc_billingid_str(decoded->billing_id));
1582 switch (decoded->charge_type) {
1583 case AST_AOC_CHARGE_CURRENCY:
1584 ast_str_append(msg, 0, "%s: %s\r\n", charge_str,
1585 decoded->currency_name);
1586 aoc_amount_str(msg, charge_str,
1587 decoded->currency_amount,
1588 decoded->multiplier);
1590 case AST_AOC_CHARGE_UNIT:
1591 ast_str_append(msg, 0, "%s/NumberItems: %d\r\n", charge_str,
1592 decoded->unit_count);
1593 for (idx = 0; idx < decoded->unit_count; ++idx) {
1594 snprintf(prefix, sizeof(prefix), "%s/Item(%d)", charge_str, idx);
1595 if (decoded->unit_list[idx].valid_amount) {
1596 ast_str_append(msg, 0, "%s/NumberOf: %u\r\n", prefix,
1597 decoded->unit_list[idx].amount);
1599 if (decoded->unit_list[idx].valid_type) {
1600 ast_str_append(msg, 0, "%s/TypeOf: %d\r\n", prefix,
1601 decoded->unit_list[idx].type);
1610 static struct ast_json *units_to_json(const struct ast_aoc_decoded *decoded)
1613 struct ast_json *units = ast_json_array_create();
1616 return ast_json_null();
1619 for (i = 0; i < decoded->unit_count; ++i) {
1620 struct ast_json *unit = ast_json_object_create();
1622 if (decoded->unit_list[i].valid_amount) {
1623 ast_json_object_set(
1624 unit, "NumberOf", ast_json_stringf(
1625 "%u", decoded->unit_list[i].amount));
1628 if (decoded->unit_list[i].valid_type) {
1629 ast_json_object_set(
1630 unit, "TypeOf", ast_json_stringf(
1631 "%d", decoded->unit_list[i].type));
1634 if (ast_json_array_append(units, unit)) {
1642 static struct ast_json *currency_to_json(const char *name, int cost,
1643 enum ast_aoc_currency_multiplier mult)
1645 return ast_json_pack("{s:s, s:i, s:s}", "Name", name,
1646 "Cost", cost, "Multiplier", aoc_multiplier_str(mult));
1649 static struct ast_json *charge_to_json(const struct ast_aoc_decoded *decoded)
1651 RAII_VAR(struct ast_json *, obj, NULL, ast_json_unref);
1652 const char *obj_type;
1654 if (decoded->charge_type != AST_AOC_CHARGE_CURRENCY &&
1655 decoded->charge_type != AST_AOC_CHARGE_UNIT) {
1656 return ast_json_pack("{s:s}", "Type",
1657 aoc_charge_type_str(decoded->charge_type));
1660 if (decoded->charge_type == AST_AOC_CHARGE_CURRENCY) {
1661 obj_type = "Currency";
1662 obj = currency_to_json(decoded->currency_name, decoded->currency_amount,
1663 decoded->multiplier);
1664 } else { /* decoded->charge_type == AST_AOC_CHARGE_UNIT */
1666 obj = units_to_json(decoded);
1669 return ast_json_pack(
1670 "{s:s, s:s, s:s, s:O}",
1671 "Type", aoc_charge_type_str(decoded->charge_type),
1672 "BillingID", aoc_billingid_str(decoded->billing_id),
1673 "TotalType", aoc_type_of_totaling_str(decoded->total_type),
1677 static struct ast_json *association_to_json(const struct ast_aoc_decoded *decoded)
1679 switch (decoded->charging_association.charging_type) {
1680 case AST_AOC_CHARGING_ASSOCIATION_NUMBER:
1681 return ast_json_pack(
1683 "Number", decoded->charging_association.charge.number.number,
1684 "Plan", decoded->charging_association.charge.number.plan);
1685 case AST_AOC_CHARGING_ASSOCIATION_ID:
1686 return ast_json_pack(
1687 "{s:i}", "ID", decoded->charging_association.charge.id);
1688 case AST_AOC_CHARGING_ASSOCIATION_NA:
1690 return ast_json_null();
1694 static struct ast_json *s_to_json(const struct ast_aoc_decoded *decoded)
1697 struct ast_json *rates = ast_json_array_create();
1700 return ast_json_null();
1703 for (i = 0; i < decoded->aoc_s_count; ++i) {
1704 struct ast_json *rate = ast_json_object_create();
1705 RAII_VAR(struct ast_json *, type, NULL, ast_json_unref);
1706 RAII_VAR(struct ast_json *, currency, NULL, ast_json_unref);
1707 const char *charge_item = aoc_charged_item_str(
1708 decoded->aoc_s_entries[i].charged_item);
1710 if (decoded->aoc_s_entries[i].charged_item == AST_AOC_CHARGED_ITEM_NA) {
1711 rate = ast_json_pack("{s:s}", "Chargeable", charge_item);
1712 if (ast_json_array_append(rates, rate)) {
1718 switch (decoded->aoc_s_entries[i].rate_type) {
1719 case AST_AOC_RATE_TYPE_DURATION:
1721 RAII_VAR(struct ast_json *, time, NULL, ast_json_unref);
1722 RAII_VAR(struct ast_json *, granularity, NULL, ast_json_unref);
1724 currency = currency_to_json(
1725 decoded->aoc_s_entries[i].rate.duration.currency_name,
1726 decoded->aoc_s_entries[i].rate.duration.amount,
1727 decoded->aoc_s_entries[i].rate.duration.multiplier);
1729 time = ast_json_pack(
1731 "Length", decoded->aoc_s_entries[i].rate.duration.time,
1732 "Scale", decoded->aoc_s_entries[i].rate.duration.time_scale);
1734 if (decoded->aoc_s_entries[i].rate.duration.granularity_time) {
1735 granularity = ast_json_pack(
1737 "Length", decoded->aoc_s_entries[i].rate.duration.granularity_time,
1738 "Scale", decoded->aoc_s_entries[i].rate.duration.granularity_time_scale);
1741 type = ast_json_pack("{s:O, s:s, s:O, s:O}", "Currency", currency, "ChargingType",
1742 decoded->aoc_s_entries[i].rate.duration.charging_type ?
1743 "StepFunction" : "ContinuousCharging", "Time", time,
1744 "Granularity", granularity ? granularity : ast_json_null());
1748 case AST_AOC_RATE_TYPE_FLAT:
1749 currency = currency_to_json(
1750 decoded->aoc_s_entries[i].rate.flat.currency_name,
1751 decoded->aoc_s_entries[i].rate.flat.amount,
1752 decoded->aoc_s_entries[i].rate.flat.multiplier);
1754 type = ast_json_pack("{s:O}", "Currency", currency);
1756 case AST_AOC_RATE_TYPE_VOLUME:
1757 currency = currency_to_json(
1758 decoded->aoc_s_entries[i].rate.volume.currency_name,
1759 decoded->aoc_s_entries[i].rate.volume.amount,
1760 decoded->aoc_s_entries[i].rate.volume.multiplier);
1762 type = ast_json_pack(
1763 "{s:s, s:O}", "Unit", aoc_volume_unit_str(
1764 decoded->aoc_s_entries[i].rate.volume.volume_unit),
1765 "Currency", currency);
1767 case AST_AOC_RATE_TYPE_SPECIAL_CODE:
1768 type = ast_json_pack("{s:i}", "SpecialCode",
1769 decoded->aoc_s_entries[i].rate.special_code);
1775 rate = ast_json_pack("{s:s, s:O}", "Chargeable", charge_item,
1776 aoc_rate_type_str(decoded->aoc_s_entries[i].rate_type), type);
1777 if (ast_json_array_append(rates, rate)) {
1784 static struct ast_json *d_to_json(const struct ast_aoc_decoded *decoded)
1786 return ast_json_pack("{s:o}", "Charge", charge_to_json(decoded));
1789 static struct ast_json *e_to_json(const struct ast_aoc_decoded *decoded)
1791 return ast_json_pack("{s:o, s:o}",
1792 "ChargingAssociation", association_to_json(decoded),
1793 "Charge", charge_to_json(decoded));
1796 static struct ast_manager_event_blob *aoc_to_ami(struct stasis_message *message,
1797 const char *event_name)
1799 struct ast_channel_blob *obj = stasis_message_data(message);
1800 RAII_VAR(struct ast_str *, channel, NULL, ast_free);
1801 RAII_VAR(struct ast_str *, aoc, NULL, ast_free);
1803 if (!(channel = ast_manager_build_channel_state_string(
1808 if (!(aoc = ast_manager_str_from_json_object(obj->blob, NULL))) {
1812 return ast_manager_event_blob_create(EVENT_FLAG_AOC, event_name, "%s%s",
1813 AS_OR(channel, ""), ast_str_buffer(aoc));
1816 static struct ast_manager_event_blob *aoc_s_to_ami(struct stasis_message *message)
1818 return aoc_to_ami(message, "AOC-S");
1821 static struct ast_manager_event_blob *aoc_d_to_ami(struct stasis_message *message)
1823 return aoc_to_ami(message, "AOC-D");
1826 static struct ast_manager_event_blob *aoc_e_to_ami(struct stasis_message *message)
1828 return aoc_to_ami(message, "AOC-E");
1831 struct stasis_message_type *aoc_s_type(void);
1832 struct stasis_message_type *aoc_d_type(void);
1833 struct stasis_message_type *aoc_e_type(void);
1835 STASIS_MESSAGE_TYPE_DEFN(
1837 .to_ami = aoc_s_to_ami);
1839 STASIS_MESSAGE_TYPE_DEFN(
1841 .to_ami = aoc_d_to_ami);
1843 STASIS_MESSAGE_TYPE_DEFN(
1845 .to_ami = aoc_e_to_ami);
1847 int ast_aoc_manager_event(const struct ast_aoc_decoded *decoded, struct ast_channel *chan)
1849 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
1850 struct stasis_message_type *msg_type;
1856 switch (decoded->msg_type) {
1858 blob = s_to_json(decoded);
1859 msg_type = aoc_s_type();
1862 blob = d_to_json(decoded);
1863 msg_type = aoc_d_type();
1866 blob = e_to_json(decoded);
1867 msg_type = aoc_e_type();
1870 /* events for AST_AOC_REQUEST are not generated here */
1874 ast_channel_publish_blob(chan, msg_type, blob);
1878 int ast_aoc_decoded2str(const struct ast_aoc_decoded *decoded, struct ast_str **msg)
1880 if (!decoded || !msg) {
1884 switch (decoded->msg_type) {
1886 ast_str_append(msg, 0, "AOC-S\r\n");
1887 aoc_s_event(decoded, msg);
1890 ast_str_append(msg, 0, "AOC-D\r\n");
1891 aoc_d_event(decoded, msg);
1894 ast_str_append(msg, 0, "AOC-E\r\n");
1895 aoc_e_event(decoded, msg);
1897 case AST_AOC_REQUEST:
1898 ast_str_append(msg, 0, "AOC-Request\r\n");
1899 aoc_request_event(decoded, msg);
1906 static void aoc_display_decoded_debug(const struct ast_aoc_decoded *decoded, int decoding, struct ast_channel *chan)
1908 struct ast_str *msg;
1910 if (!decoded || !(msg = ast_str_create(1024))) {
1915 ast_str_append(&msg, 0, "---- DECODED AOC MSG ----\r\n");
1917 ast_str_append(&msg, 0, "---- ENCODED AOC MSG ----\r\n");
1920 ast_str_append(&msg, 0, "CHANNEL: %s\r\n", ast_channel_name(chan));
1923 if (ast_aoc_decoded2str(decoded, &msg)) {
1928 ast_verb(1, "%s\r\n", ast_str_buffer(msg));
1932 static struct ast_cli_entry aoc_cli[] = {
1933 AST_CLI_DEFINE(aoc_cli_debug_enable, "enable cli debugging of AOC messages"),
1936 static void aoc_shutdown(void)
1938 STASIS_MESSAGE_TYPE_CLEANUP(aoc_s_type);
1939 STASIS_MESSAGE_TYPE_CLEANUP(aoc_d_type);
1940 STASIS_MESSAGE_TYPE_CLEANUP(aoc_e_type);
1942 ast_cli_unregister_multiple(aoc_cli, ARRAY_LEN(aoc_cli));
1944 int ast_aoc_cli_init(void)
1946 STASIS_MESSAGE_TYPE_INIT(aoc_s_type);
1947 STASIS_MESSAGE_TYPE_INIT(aoc_d_type);
1948 STASIS_MESSAGE_TYPE_INIT(aoc_e_type);
1950 ast_register_atexit(aoc_shutdown);
1951 return ast_cli_register_multiple(aoc_cli, ARRAY_LEN(aoc_cli));