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"
40 /* Encoded Payload Flags */
41 #define AST_AOC_ENCODED_TYPE_REQUEST (0 << 0)
42 #define AST_AOC_ENCODED_TYPE_D (1 << 0)
43 #define AST_AOC_ENCODED_TYPE_E (2 << 0)
44 #define AST_AOC_ENCODED_TYPE_S (3 << 0)
46 #define AST_AOC_ENCODED_REQUEST_S (1 << 2)
47 #define AST_AOC_ENCODED_REQUEST_D (1 << 3)
48 #define AST_AOC_ENCODED_REQUEST_E (1 << 4)
50 #define AST_AOC_ENCODED_CHARGE_NA (0 << 5)
51 #define AST_AOC_ENCODED_CHARGE_FREE (1 << 5)
52 #define AST_AOC_ENCODED_CHARGE_CURRENCY (2 << 5)
53 #define AST_AOC_ENCODED_CHARGE_UNIT (3 << 5)
55 #define AST_AOC_ENCODED_CHARGE_SUBTOTAL (1 << 7)
56 #define AST_AOC_ENCODED_CHARGE_TOTAL (0 << 7)
58 #define AST_AOC_ENCODE_VERSION 1
61 static char aoc_debug_enabled = 0;
62 static void aoc_display_decoded_debug(const struct ast_aoc_decoded *decoded, int decoding, struct ast_channel *chan);
63 static int aoc_s_add_entry(struct ast_aoc_decoded *decoded, struct ast_aoc_s_entry *entry);
65 /* AOC Payload Header. Holds all the encoded AOC data to pass on the wire */
66 struct ast_aoc_encoded {
70 unsigned char data[0];
73 /* Decoded AOC data */
74 struct ast_aoc_decoded {
75 enum ast_aoc_type msg_type;
76 enum ast_aoc_charge_type charge_type;
77 enum ast_aoc_request request_flag;
78 enum ast_aoc_total_type total_type;
80 /* currency information */
81 enum ast_aoc_currency_multiplier multiplier;
82 unsigned int currency_amount;
83 char currency_name[AOC_CURRENCY_NAME_SIZE];
85 /* unit information */
87 struct ast_aoc_unit_entry unit_list[32];
90 enum ast_aoc_billing_id billing_id;
92 /* Charging Association information */
93 struct ast_aoc_charging_association charging_association;
95 /* AOC-S charge information */
97 struct ast_aoc_s_entry aoc_s_entries[10];
99 /* Is this an AOC Termination Request */
100 char termination_request;
103 /*! \brief AOC Payload Information Elements */
108 AOC_IE_CHARGING_ASSOCIATION = 4,
110 AOC_IE_TERMINATION_REQUEST = 6,
113 /*! \brief AOC IE payload header */
114 struct aoc_pl_ie_hdr {
118 } __attribute__((packed));
120 struct aoc_ie_currency {
123 char name[AOC_CURRENCY_NAME_SIZE];
124 } __attribute__((packed));
129 uint8_t valid_amount;
131 } __attribute__((packed));
133 struct aoc_ie_billing {
135 } __attribute__((packed));
137 struct aoc_ie_charging_association {
138 struct ast_aoc_charging_association ca;
139 } __attribute__((packed));
141 struct aoc_ie_charging_rate {
142 struct ast_aoc_s_entry entry;
143 } __attribute__((packed));
145 struct ast_aoc_decoded *ast_aoc_create(const enum ast_aoc_type msg_type,
146 const enum ast_aoc_charge_type charge_type,
147 const enum ast_aoc_request requests)
149 struct ast_aoc_decoded *decoded = NULL;
152 if (((unsigned int) charge_type > AST_AOC_CHARGE_UNIT) ||
153 ((unsigned int) msg_type > AST_AOC_E) ||
154 ((msg_type == AST_AOC_REQUEST) && !requests)) {
156 ast_log(LOG_WARNING, "Failed to create ast_aoc_decoded object, invalid input\n");
160 if (!(decoded = ast_calloc(1, sizeof(struct ast_aoc_decoded)))) {
161 ast_log(LOG_WARNING, "Failed to create ast_aoc_decoded object \n");
165 decoded->msg_type = msg_type;
167 if (msg_type == AST_AOC_REQUEST) {
168 decoded->request_flag = requests;
169 } else if ((msg_type == AST_AOC_D) || (msg_type == AST_AOC_E)) {
170 decoded->charge_type = charge_type;
176 void *ast_aoc_destroy_decoded(struct ast_aoc_decoded *decoded)
182 void *ast_aoc_destroy_encoded(struct ast_aoc_encoded *encoded)
188 static void aoc_parse_ie_charging_rate(struct ast_aoc_decoded *decoded, const struct aoc_ie_charging_rate *ie)
190 struct ast_aoc_s_entry entry = { 0, };
192 entry.charged_item = ntohs(ie->entry.charged_item);
193 entry.rate_type = ntohs(ie->entry.rate_type);
195 switch (entry.rate_type) {
196 case AST_AOC_RATE_TYPE_DURATION:
197 entry.rate.duration.multiplier = ntohs(ie->entry.rate.duration.multiplier);
198 entry.rate.duration.amount = ntohl(ie->entry.rate.duration.amount);
199 entry.rate.duration.time = ntohl(ie->entry.rate.duration.time);
200 entry.rate.duration.time_scale = ntohs(ie->entry.rate.duration.time_scale);
201 entry.rate.duration.granularity_time = ntohl(ie->entry.rate.duration.granularity_time);
202 entry.rate.duration.granularity_time_scale = ntohs(ie->entry.rate.duration.granularity_time_scale);
203 entry.rate.duration.charging_type = ie->entry.rate.duration.charging_type; /* only one byte */
205 if (!ast_strlen_zero(ie->entry.rate.duration.currency_name)) {
206 ast_copy_string(entry.rate.duration.currency_name,
207 ie->entry.rate.duration.currency_name,
208 sizeof(entry.rate.duration.currency_name));
211 case AST_AOC_RATE_TYPE_FLAT:
212 entry.rate.flat.multiplier = ntohs(ie->entry.rate.flat.multiplier);
213 entry.rate.flat.amount = ntohl(ie->entry.rate.flat.amount);
214 if (!ast_strlen_zero(ie->entry.rate.flat.currency_name)) {
215 ast_copy_string(entry.rate.flat.currency_name,
216 ie->entry.rate.flat.currency_name,
217 sizeof(entry.rate.flat.currency_name));
220 case AST_AOC_RATE_TYPE_VOLUME:
221 entry.rate.volume.multiplier = ntohs(ie->entry.rate.volume.multiplier);
222 entry.rate.volume.amount = ntohl(ie->entry.rate.volume.amount);
223 entry.rate.volume.volume_unit = ntohs(ie->entry.rate.volume.volume_unit);
224 if (!ast_strlen_zero(ie->entry.rate.volume.currency_name)) {
225 ast_copy_string(entry.rate.volume.currency_name,
226 ie->entry.rate.volume.currency_name,
227 sizeof(entry.rate.volume.currency_name));
230 case AST_AOC_RATE_TYPE_SPECIAL_CODE:
231 entry.rate.special_code = ntohs(ie->entry.rate.special_code);
235 aoc_s_add_entry(decoded, &entry);
238 static int aoc_parse_ie(struct ast_aoc_decoded *decoded, unsigned char *data, unsigned int datalen)
243 while (datalen >= 2) {
246 if (len > datalen -2) {
247 ast_log(LOG_ERROR, "AOC information element length exceeds the total message size\n");
252 case AOC_IE_CURRENCY:
253 if (len == sizeof(struct aoc_ie_currency)) {
254 struct aoc_ie_currency ie;
255 memcpy(&ie, data + 2, len);
256 decoded->currency_amount = ntohl(ie.amount);
257 decoded->multiplier = ie.multiplier; /* only one byte */
258 memcpy(decoded->currency_name, ie.name, sizeof(decoded->currency_name));
260 ast_log(LOG_WARNING, "Received invalid currency ie\n");
264 if (len == sizeof(struct aoc_ie_unit)) {
265 struct aoc_ie_unit ie;
266 memcpy(&ie, data + 2, len);
267 ast_aoc_add_unit_entry(decoded, ie.valid_amount, ntohl(ie.amount), ie.valid_type, ie.type);
269 ast_log(LOG_WARNING, "Received invalid unit ie\n");
273 if (len == sizeof(struct aoc_ie_billing)) {
274 struct aoc_ie_billing ie;
275 memcpy(&ie, data + 2, len);
276 decoded->billing_id = ie.id; /* only one byte */
278 ast_log(LOG_WARNING, "Received invalid billing ie\n");
281 case AOC_IE_CHARGING_ASSOCIATION:
282 if (len == sizeof(struct aoc_ie_charging_association)) {
283 memcpy(&decoded->charging_association, data + 2, sizeof(decoded->charging_association));
284 /* everything in the charging_association struct is a single byte except for the id */
285 if (decoded->charging_association.charging_type == AST_AOC_CHARGING_ASSOCIATION_ID) {
286 decoded->charging_association.charge.id = ntohl(decoded->charging_association.charge.id);
289 ast_log(LOG_WARNING, "Received invalid charging association ie\n");
293 if (len == sizeof(struct aoc_ie_charging_rate)) {
294 struct aoc_ie_charging_rate ie;
295 memcpy(&ie, data + 2, len);
296 aoc_parse_ie_charging_rate(decoded, &ie);
298 ast_log(LOG_WARNING, "Received invalid charging rate ie\n");
301 case AOC_IE_TERMINATION_REQUEST:
303 decoded->termination_request = 1;
305 ast_log(LOG_WARNING, "Received invalid termination request ie\n");
309 ast_log(LOG_WARNING, "Unknown AOC Information Element, ignoring.\n");
312 datalen -= (len + 2);
318 struct ast_aoc_decoded *ast_aoc_decode(struct ast_aoc_encoded *encoded, size_t size, struct ast_channel *chan)
320 struct ast_aoc_decoded *decoded;
322 /* verify our encoded payload is actually large enough to hold all the ies */
323 if ((size - (sizeof(struct ast_aoc_encoded)) != ntohs(encoded->datalen))) {
324 ast_log(LOG_WARNING, "Corrupted aoc encoded object, can not decode\n");
328 if (!(decoded = ast_calloc(1, sizeof(struct ast_aoc_decoded)))) {
329 ast_log(LOG_WARNING, "Failed to create ast_aoc_decoded object \n");
335 if ((encoded->flags & AST_AOC_ENCODED_TYPE_S) == AST_AOC_ENCODED_TYPE_S) {
336 decoded->msg_type = AST_AOC_S;
337 } else if (encoded->flags & AST_AOC_ENCODED_TYPE_E) {
338 decoded->msg_type = AST_AOC_E;
339 } else if (encoded->flags & AST_AOC_ENCODED_TYPE_D) {
340 decoded->msg_type = AST_AOC_D;
342 decoded->msg_type = AST_AOC_REQUEST;
345 if (decoded->msg_type == AST_AOC_REQUEST) {
346 if (encoded->flags & AST_AOC_ENCODED_REQUEST_S) {
347 decoded->request_flag |= AST_AOC_REQUEST_S;
349 if (encoded->flags & AST_AOC_ENCODED_REQUEST_D) {
350 decoded->request_flag |= AST_AOC_REQUEST_D;
352 if (encoded->flags & AST_AOC_ENCODED_REQUEST_E) {
353 decoded->request_flag |= AST_AOC_REQUEST_E;
355 } else if ((decoded->msg_type == AST_AOC_D) || (decoded->msg_type == AST_AOC_E)) {
356 if ((encoded->flags & AST_AOC_ENCODED_CHARGE_UNIT) == AST_AOC_ENCODED_CHARGE_UNIT) {
357 decoded->charge_type = AST_AOC_CHARGE_UNIT;
358 } else if ((encoded->flags & AST_AOC_ENCODED_CHARGE_CURRENCY) == AST_AOC_ENCODED_CHARGE_CURRENCY) {
359 decoded->charge_type = AST_AOC_CHARGE_CURRENCY;
360 } else if ((encoded->flags & AST_AOC_ENCODED_CHARGE_FREE) == AST_AOC_ENCODED_CHARGE_FREE) {
361 decoded->charge_type = AST_AOC_CHARGE_FREE;
363 decoded->charge_type = AST_AOC_CHARGE_NA;
366 if (encoded->flags & AST_AOC_ENCODED_CHARGE_SUBTOTAL) {
367 decoded->total_type = AST_AOC_SUBTOTAL;
371 /* decode information elements */
372 aoc_parse_ie(decoded, encoded->data, ntohs(encoded->datalen));
374 if (aoc_debug_enabled) {
375 aoc_display_decoded_debug(decoded, 1, chan);
382 unsigned char buf[1024];
388 * \brief append an AOC information element
389 * \note data is expected to already be in network byte order at this point
391 static int aoc_append_ie(struct aoc_ie_data *ied, unsigned short ie_id, const void *data, unsigned short datalen)
393 if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
394 ast_log(LOG_WARNING, "Failure to append AOC information element, out of space \n");
397 ied->buf[ied->pos++] = ie_id;
398 ied->buf[ied->pos++] = datalen;
400 memcpy(ied->buf + ied->pos, data, datalen);
406 static void aoc_create_ie_data_charging_rate(const struct ast_aoc_s_entry *entry, struct aoc_ie_charging_rate *ie)
408 ie->entry.charged_item = htons(entry->charged_item);
409 ie->entry.rate_type = htons(entry->rate_type);
411 switch (entry->rate_type) {
412 case AST_AOC_RATE_TYPE_DURATION:
413 ie->entry.rate.duration.multiplier = htons(entry->rate.duration.multiplier);
414 ie->entry.rate.duration.amount = htonl(entry->rate.duration.amount);
415 ie->entry.rate.duration.time = htonl(entry->rate.duration.time);
416 ie->entry.rate.duration.time_scale = htons(entry->rate.duration.time_scale);
417 ie->entry.rate.duration.granularity_time = htonl(entry->rate.duration.granularity_time);
418 ie->entry.rate.duration.granularity_time_scale = htons(entry->rate.duration.granularity_time_scale);
419 ie->entry.rate.duration.charging_type = entry->rate.duration.charging_type; /* only one byte */
421 if (!ast_strlen_zero(entry->rate.duration.currency_name)) {
422 ast_copy_string(ie->entry.rate.duration.currency_name,
423 entry->rate.duration.currency_name,
424 sizeof(ie->entry.rate.duration.currency_name));
427 case AST_AOC_RATE_TYPE_FLAT:
428 ie->entry.rate.flat.multiplier = htons(entry->rate.flat.multiplier);
429 ie->entry.rate.flat.amount = htonl(entry->rate.flat.amount);
430 if (!ast_strlen_zero(entry->rate.flat.currency_name)) {
431 ast_copy_string(ie->entry.rate.flat.currency_name,
432 entry->rate.flat.currency_name,
433 sizeof(ie->entry.rate.flat.currency_name));
436 case AST_AOC_RATE_TYPE_VOLUME:
437 ie->entry.rate.volume.multiplier = htons(entry->rate.volume.multiplier);
438 ie->entry.rate.volume.amount = htonl(entry->rate.volume.amount);
439 ie->entry.rate.volume.volume_unit = htons(entry->rate.volume.volume_unit);
440 if (!ast_strlen_zero(entry->rate.volume.currency_name)) {
441 ast_copy_string(ie->entry.rate.volume.currency_name,
442 entry->rate.volume.currency_name,
443 sizeof(ie->entry.rate.volume.currency_name));
446 case AST_AOC_RATE_TYPE_SPECIAL_CODE:
447 ie->entry.rate.special_code = htons(entry->rate.special_code);
452 static void aoc_create_ie_data(struct ast_aoc_decoded *decoded, struct aoc_ie_data *ied)
456 if (decoded->currency_amount) {
457 struct aoc_ie_currency ie = {
458 .amount = htonl(decoded->currency_amount),
459 .multiplier = decoded->multiplier, /* only one byte */
463 if (!ast_strlen_zero(decoded->currency_name)) {
464 ast_copy_string(ie.name, decoded->currency_name, sizeof(ie.name));
467 aoc_append_ie(ied, AOC_IE_CURRENCY, (const void *) &ie, sizeof(ie));
470 if (decoded->unit_count) {
471 struct aoc_ie_unit ie = { 0 };
474 for (i = 0; i < decoded->unit_count; i++) {
475 ie.valid_amount = decoded->unit_list[i].valid_amount; /* only one byte */
476 ie.amount = htonl(decoded->unit_list[i].amount);
477 ie.valid_type = decoded->unit_list[i].valid_type; /* only one byte */
478 ie.type = decoded->unit_list[i].type; /* only one byte */
479 aoc_append_ie(ied, AOC_IE_UNIT, (const void *) &ie, sizeof(ie));
483 if (decoded->billing_id) {
484 struct aoc_ie_billing ie;
485 ie.id = decoded->billing_id; /* only one byte */
486 aoc_append_ie(ied, AOC_IE_BILLING, (const void *) &ie, sizeof(ie));
489 if (decoded->charging_association.charging_type != AST_AOC_CHARGING_ASSOCIATION_NA) {
490 struct aoc_ie_charging_association ie;
491 memset(&ie, 0, sizeof(ie));
492 ie.ca.charging_type = decoded->charging_association.charging_type; /* only one byte */
493 if (decoded->charging_association.charging_type == AST_AOC_CHARGING_ASSOCIATION_NUMBER) {
494 ie.ca.charge.number.plan = decoded->charging_association.charge.number.plan; /* only one byte */
495 ast_copy_string(ie.ca.charge.number.number,
496 decoded->charging_association.charge.number.number,
497 sizeof(ie.ca.charge.number.number));
498 } else if (decoded->charging_association.charging_type == AST_AOC_CHARGING_ASSOCIATION_ID) {
499 ie.ca.charge.id = htonl(decoded->charging_association.charge.id);
501 aoc_append_ie(ied, AOC_IE_CHARGING_ASSOCIATION, (const void *) &ie, sizeof(ie));
504 if (decoded->aoc_s_count) {
505 struct aoc_ie_charging_rate ie;
507 for (i = 0; i < decoded->aoc_s_count; i++) {
508 memset(&ie, 0, sizeof(ie));
509 aoc_create_ie_data_charging_rate(&decoded->aoc_s_entries[i], &ie);
510 aoc_append_ie(ied, AOC_IE_RATE, (const void *) &ie, sizeof(ie));
514 if (decoded->termination_request) {
515 aoc_append_ie(ied, AOC_IE_TERMINATION_REQUEST, NULL, 0);
519 struct ast_aoc_encoded *ast_aoc_encode(struct ast_aoc_decoded *decoded, size_t *out_size, struct ast_channel *chan)
521 struct aoc_ie_data ied;
522 struct ast_aoc_encoded *encoded = NULL;
525 if (!decoded || !out_size) {
531 /* create information element buffer before allocating the payload,
532 * by doing this the exact size of the payload + the id data can be
533 * allocated all at once. */
534 aoc_create_ie_data(decoded, &ied);
536 size = sizeof(struct ast_aoc_encoded) + ied.pos;
538 if (!(encoded = ast_calloc(1, size))) {
539 ast_log(LOG_WARNING, "Failed to create ast_aoc_encoded object during decode routine. \n");
543 /* -- Set ie data buffer */
545 /* this is safe because encoded was allocated to fit this perfectly */
546 memcpy(encoded->data, ied.buf, ied.pos);
547 encoded->datalen = htons(ied.pos);
550 /* --- Set Flags --- */
551 switch (decoded->msg_type) {
553 encoded->flags = AST_AOC_ENCODED_TYPE_S;
556 encoded->flags = AST_AOC_ENCODED_TYPE_D;
559 encoded->flags = AST_AOC_ENCODED_TYPE_E;
561 case AST_AOC_REQUEST:
562 encoded->flags = AST_AOC_ENCODED_TYPE_REQUEST;
567 /* if it is type request, set the types requested, else set charge type */
568 if (decoded->msg_type == AST_AOC_REQUEST) {
569 if (decoded->request_flag & AST_AOC_REQUEST_S) {
570 encoded->flags |= AST_AOC_ENCODED_REQUEST_S;
572 if (decoded->request_flag & AST_AOC_REQUEST_D) {
573 encoded->flags |= AST_AOC_ENCODED_REQUEST_D;
575 if (decoded->request_flag & AST_AOC_REQUEST_E) {
576 encoded->flags |= AST_AOC_ENCODED_REQUEST_E;
578 } else if ((decoded->msg_type == AST_AOC_D) || (decoded->msg_type == AST_AOC_E)) {
579 switch (decoded->charge_type) {
580 case AST_AOC_CHARGE_UNIT:
581 encoded->flags |= AST_AOC_ENCODED_CHARGE_UNIT;
583 case AST_AOC_CHARGE_CURRENCY:
584 encoded->flags |= AST_AOC_ENCODED_CHARGE_CURRENCY;
586 case AST_AOC_CHARGE_FREE:
587 encoded->flags |= AST_AOC_ENCODED_CHARGE_FREE;
588 case AST_AOC_CHARGE_NA:
590 encoded->flags |= AST_AOC_ENCODED_CHARGE_NA;
594 if (decoded->total_type == AST_AOC_SUBTOTAL) {
595 encoded->flags |= AST_AOC_ENCODED_CHARGE_SUBTOTAL;
599 /* --- Set Version Number --- */
600 encoded->version = AST_AOC_ENCODE_VERSION;
602 /* set the output size */
605 if (aoc_debug_enabled) {
606 aoc_display_decoded_debug(decoded, 0, chan);
612 static int aoc_s_add_entry(struct ast_aoc_decoded *decoded, struct ast_aoc_s_entry *entry)
614 if (decoded->aoc_s_count >= ARRAY_LEN(decoded->aoc_s_entries)) {
618 decoded->aoc_s_entries[decoded->aoc_s_count] = *entry;
619 decoded->aoc_s_count++;
625 unsigned int ast_aoc_s_get_count(struct ast_aoc_decoded *decoded)
627 return decoded->aoc_s_count;
630 const struct ast_aoc_s_entry *ast_aoc_s_get_rate_info(struct ast_aoc_decoded *decoded, unsigned int entry_number)
632 if (entry_number >= decoded->aoc_s_count) {
636 return (const struct ast_aoc_s_entry *) &decoded->aoc_s_entries[entry_number];
639 int ast_aoc_s_add_rate_duration(struct ast_aoc_decoded *decoded,
640 enum ast_aoc_s_charged_item charged_item,
642 enum ast_aoc_currency_multiplier multiplier,
643 const char *currency_name,
645 enum ast_aoc_time_scale time_scale,
646 unsigned long granularity_time,
647 enum ast_aoc_time_scale granularity_time_scale,
651 struct ast_aoc_s_entry entry = { 0, };
653 entry.charged_item = charged_item;
654 entry.rate_type = AST_AOC_RATE_TYPE_DURATION;
655 entry.rate.duration.amount = amount;
656 entry.rate.duration.multiplier = multiplier;
657 entry.rate.duration.time = time;
658 entry.rate.duration.time_scale = time_scale;
659 entry.rate.duration.granularity_time = granularity_time;
660 entry.rate.duration.granularity_time_scale = granularity_time_scale;
661 entry.rate.duration.charging_type = step_function ? 1 : 0;
663 if (!ast_strlen_zero(currency_name)) {
664 ast_copy_string(entry.rate.duration.currency_name, currency_name, sizeof(entry.rate.duration.currency_name));
667 return aoc_s_add_entry(decoded, &entry);
670 int ast_aoc_s_add_rate_flat(struct ast_aoc_decoded *decoded,
671 enum ast_aoc_s_charged_item charged_item,
673 enum ast_aoc_currency_multiplier multiplier,
674 const char *currency_name)
676 struct ast_aoc_s_entry entry = { 0, };
678 entry.charged_item = charged_item;
679 entry.rate_type = AST_AOC_RATE_TYPE_FLAT;
680 entry.rate.flat.amount = amount;
681 entry.rate.flat.multiplier = multiplier;
683 if (!ast_strlen_zero(currency_name)) {
684 ast_copy_string(entry.rate.flat.currency_name, currency_name, sizeof(entry.rate.flat.currency_name));
687 return aoc_s_add_entry(decoded, &entry);
691 int ast_aoc_s_add_rate_volume(struct ast_aoc_decoded *decoded,
692 enum ast_aoc_s_charged_item charged_item,
693 enum ast_aoc_volume_unit volume_unit,
695 enum ast_aoc_currency_multiplier multiplier,
696 const char *currency_name)
698 struct ast_aoc_s_entry entry = { 0, };
700 entry.charged_item = charged_item;
701 entry.rate_type = AST_AOC_RATE_TYPE_VOLUME;
702 entry.rate.volume.multiplier = multiplier;
703 entry.rate.volume.amount = amount;
704 entry.rate.volume.volume_unit = volume_unit;
706 if (!ast_strlen_zero(currency_name)) {
707 ast_copy_string(entry.rate.volume.currency_name, currency_name, sizeof(entry.rate.volume.currency_name));
710 return aoc_s_add_entry(decoded, &entry);
713 int ast_aoc_s_add_rate_special_charge_code(struct ast_aoc_decoded *decoded,
714 enum ast_aoc_s_charged_item charged_item,
717 struct ast_aoc_s_entry entry = { 0, };
719 entry.charged_item = charged_item;
720 entry.rate_type = AST_AOC_RATE_TYPE_SPECIAL_CODE;
721 entry.rate.special_code = code;
723 return aoc_s_add_entry(decoded, &entry);
726 int ast_aoc_s_add_rate_free(struct ast_aoc_decoded *decoded,
727 enum ast_aoc_s_charged_item charged_item,
730 struct ast_aoc_s_entry entry = { 0, };
732 entry.charged_item = charged_item;
733 entry.rate_type = from_beginning ? AST_AOC_RATE_TYPE_FREE_FROM_BEGINNING : AST_AOC_RATE_TYPE_FREE;
735 return aoc_s_add_entry(decoded, &entry);
738 int ast_aoc_s_add_rate_na(struct ast_aoc_decoded *decoded,
739 enum ast_aoc_s_charged_item charged_item)
741 struct ast_aoc_s_entry entry = { 0, };
743 entry.charged_item = charged_item;
744 entry.rate_type = AST_AOC_RATE_TYPE_NA;
746 return aoc_s_add_entry(decoded, &entry);
749 int ast_aoc_s_add_special_arrangement(struct ast_aoc_decoded *decoded,
752 struct ast_aoc_s_entry entry = { 0, };
754 entry.charged_item = AST_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT;
755 entry.rate_type = AST_AOC_RATE_TYPE_SPECIAL_CODE;
756 entry.rate.special_code = code;
758 return aoc_s_add_entry(decoded, &entry);
761 enum ast_aoc_type ast_aoc_get_msg_type(struct ast_aoc_decoded *decoded)
763 return decoded->msg_type;
766 enum ast_aoc_charge_type ast_aoc_get_charge_type(struct ast_aoc_decoded *decoded)
768 return decoded->charge_type;
771 enum ast_aoc_request ast_aoc_get_request(struct ast_aoc_decoded *decoded)
773 return decoded->request_flag;
776 int ast_aoc_set_total_type(struct ast_aoc_decoded *decoded,
777 const enum ast_aoc_total_type type)
779 decoded->total_type = type;
783 enum ast_aoc_total_type ast_aoc_get_total_type(struct ast_aoc_decoded *decoded)
785 return decoded->total_type;
788 int ast_aoc_set_currency_info(struct ast_aoc_decoded *decoded,
789 const unsigned int amount,
790 const enum ast_aoc_currency_multiplier multiplier,
794 if (!ast_strlen_zero(name)) {
795 ast_copy_string(decoded->currency_name, name, sizeof(decoded->currency_name));
798 decoded->currency_amount = amount;
800 if (multiplier && (multiplier < AST_AOC_MULT_NUM_ENTRIES)) {
801 decoded->multiplier = multiplier;
803 decoded->multiplier = AST_AOC_MULT_ONE;
809 unsigned int ast_aoc_get_currency_amount(struct ast_aoc_decoded *decoded)
811 return decoded->currency_amount;
814 enum ast_aoc_currency_multiplier ast_aoc_get_currency_multiplier(struct ast_aoc_decoded *decoded)
816 return decoded->multiplier;
819 const char *ast_aoc_get_currency_multiplier_decimal(struct ast_aoc_decoded *decoded)
821 switch (decoded->multiplier) {
822 case AST_AOC_MULT_ONETHOUSANDTH:
824 case AST_AOC_MULT_ONEHUNDREDTH:
826 case AST_AOC_MULT_ONETENTH:
828 case AST_AOC_MULT_ONE:
830 case AST_AOC_MULT_TEN:
832 case AST_AOC_MULT_HUNDRED:
834 case AST_AOC_MULT_THOUSAND:
841 const char *ast_aoc_get_currency_name(struct ast_aoc_decoded *decoded)
843 return decoded->currency_name;
846 int ast_aoc_add_unit_entry(struct ast_aoc_decoded *decoded,
847 const unsigned int amount_is_present,
848 const unsigned int amount,
849 const unsigned int type_is_present,
850 const unsigned int type)
852 if ((decoded->msg_type == AST_AOC_REQUEST) ||
853 (decoded->unit_count >= ARRAY_LEN(decoded->unit_list))) {
857 if (!amount_is_present && !type_is_present) {
861 decoded->unit_list[decoded->unit_count].valid_amount = amount_is_present;
862 if (amount_is_present) {
863 decoded->unit_list[decoded->unit_count].amount = amount;
865 decoded->unit_list[decoded->unit_count].amount = 0;
868 decoded->unit_list[decoded->unit_count].valid_type = type_is_present;
869 if (type_is_present) {
870 decoded->unit_list[decoded->unit_count].type = type;
872 decoded->unit_list[decoded->unit_count].type = 0;
874 decoded->unit_count++;
879 const struct ast_aoc_unit_entry *ast_aoc_get_unit_info(struct ast_aoc_decoded *decoded, unsigned int entry_number)
881 if (entry_number >= decoded->unit_count) {
885 return (const struct ast_aoc_unit_entry *) &decoded->unit_list[entry_number];
888 unsigned int ast_aoc_get_unit_count(struct ast_aoc_decoded *decoded)
890 return decoded->unit_count;
893 int ast_aoc_set_billing_id(struct ast_aoc_decoded *decoded, const enum ast_aoc_billing_id id)
895 if ((id >= AST_AOC_BILLING_NUM_ENTRIES) || (id < AST_AOC_BILLING_NA)) {
899 decoded->billing_id = id;
904 enum ast_aoc_billing_id ast_aoc_get_billing_id(struct ast_aoc_decoded *decoded)
906 return decoded->billing_id;
909 int ast_aoc_set_association_id(struct ast_aoc_decoded *decoded, const int id)
911 if (decoded->msg_type != AST_AOC_E) {
914 memset(&decoded->charging_association, 0, sizeof(decoded->charging_association));
915 decoded->charging_association.charging_type = AST_AOC_CHARGING_ASSOCIATION_ID;
916 decoded->charging_association.charge.id = id;
920 const struct ast_aoc_charging_association *ast_aoc_get_association_info(struct ast_aoc_decoded *decoded)
922 return &decoded->charging_association;
925 int ast_aoc_set_association_number(struct ast_aoc_decoded *decoded, const char *num, uint8_t plan)
927 if ((decoded->msg_type != AST_AOC_E) || ast_strlen_zero(num)) {
930 memset(&decoded->charging_association, 0, sizeof(decoded->charging_association));
931 decoded->charging_association.charging_type = AST_AOC_CHARGING_ASSOCIATION_NUMBER;
932 decoded->charging_association.charge.number.plan = plan;
933 ast_copy_string(decoded->charging_association.charge.number.number, num, sizeof(decoded->charging_association.charge.number.number));
938 int ast_aoc_set_termination_request(struct ast_aoc_decoded *decoded)
940 if (decoded->msg_type != AST_AOC_REQUEST) {
943 decoded->termination_request = 1;
948 int ast_aoc_get_termination_request(struct ast_aoc_decoded *decoded)
950 return decoded->termination_request;
955 * \brief Convert AST_AOC_VOLUME_UNIT to string.
958 * \param value Value to convert to string.
960 * \return String equivalent.
962 static const char *aoc_volume_unit_str(enum ast_aoc_volume_unit value)
968 case AST_AOC_VOLUME_UNIT_OCTET:
971 case AST_AOC_VOLUME_UNIT_SEGMENT:
974 case AST_AOC_VOLUME_UNIT_MESSAGE:
983 * \brief Convert ast_aoc_charged_item to string.
986 * \param value Value to convert to string.
988 * \return String equivalent.
990 static const char *aoc_charged_item_str(enum ast_aoc_s_charged_item value)
996 case AST_AOC_CHARGED_ITEM_NA:
997 str = "NotAvailable";
999 case AST_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT:
1000 str = "SpecialArrangement";
1002 case AST_AOC_CHARGED_ITEM_BASIC_COMMUNICATION:
1003 str = "BasicCommunication";
1005 case AST_AOC_CHARGED_ITEM_CALL_ATTEMPT:
1006 str = "CallAttempt";
1008 case AST_AOC_CHARGED_ITEM_CALL_SETUP:
1011 case AST_AOC_CHARGED_ITEM_USER_USER_INFO:
1012 str = "UserUserInfo";
1014 case AST_AOC_CHARGED_ITEM_SUPPLEMENTARY_SERVICE:
1015 str = "SupplementaryService";
1023 * \brief Convert ast_aoc_total_type to string.
1026 * \param value Value to convert to string.
1028 * \return String equivalent.
1030 static const char *aoc_type_of_totaling_str(enum ast_aoc_total_type value)
1036 case AST_AOC_SUBTOTAL:
1048 * \brief Convert ast_aoc_rate_type to string.
1051 * \param value Value to convert to string.
1053 * \return String equivalent.
1055 static const char *aoc_rate_type_str(enum ast_aoc_s_rate_type value)
1061 case AST_AOC_RATE_TYPE_NA:
1062 str = "NotAvailable";
1064 case AST_AOC_RATE_TYPE_FREE:
1067 case AST_AOC_RATE_TYPE_FREE_FROM_BEGINNING:
1068 str = "FreeFromBeginning";
1070 case AST_AOC_RATE_TYPE_DURATION:
1073 case AST_AOC_RATE_TYPE_FLAT:
1076 case AST_AOC_RATE_TYPE_VOLUME:
1079 case AST_AOC_RATE_TYPE_SPECIAL_CODE:
1080 str = "SpecialCode";
1088 * \brief Convert AST_AOC_TIME_SCALE to string.
1091 * \param value Value to convert to string.
1093 * \return String equivalent.
1095 static const char *aoc_scale_str(enum ast_aoc_time_scale value)
1101 case AST_AOC_TIME_SCALE_HUNDREDTH_SECOND:
1102 str = "OneHundredthSecond";
1104 case AST_AOC_TIME_SCALE_TENTH_SECOND:
1105 str = "OneTenthSecond";
1107 case AST_AOC_TIME_SCALE_SECOND:
1110 case AST_AOC_TIME_SCALE_TEN_SECOND:
1113 case AST_AOC_TIME_SCALE_MINUTE:
1116 case AST_AOC_TIME_SCALE_HOUR:
1119 case AST_AOC_TIME_SCALE_DAY:
1126 static const char *aoc_charge_type_str(enum ast_aoc_charge_type value)
1132 case AST_AOC_CHARGE_NA:
1133 str = "NotAvailable";
1135 case AST_AOC_CHARGE_FREE:
1138 case AST_AOC_CHARGE_CURRENCY:
1141 case AST_AOC_CHARGE_UNIT:
1149 static const char *aoc_multiplier_str(enum ast_aoc_currency_multiplier mult)
1152 case AST_AOC_MULT_ONETHOUSANDTH:
1154 case AST_AOC_MULT_ONEHUNDREDTH:
1156 case AST_AOC_MULT_ONETENTH:
1158 case AST_AOC_MULT_ONE:
1160 case AST_AOC_MULT_TEN:
1162 case AST_AOC_MULT_HUNDRED:
1164 case AST_AOC_MULT_THOUSAND:
1166 case AST_AOC_MULT_NUM_ENTRIES:
1172 static const char *aoc_billingid_str(enum ast_aoc_billing_id billing_id)
1174 switch (billing_id) {
1175 case AST_AOC_BILLING_NORMAL:
1177 case AST_AOC_BILLING_REVERSE_CHARGE:
1179 case AST_AOC_BILLING_CREDIT_CARD:
1180 return "CreditCard";
1181 case AST_AOC_BILLING_CALL_FWD_UNCONDITIONAL:
1182 return "CallForwardingUnconditional";
1183 case AST_AOC_BILLING_CALL_FWD_BUSY:
1184 return "CallForwardingBusy";
1185 case AST_AOC_BILLING_CALL_FWD_NO_REPLY:
1186 return "CallForwardingNoReply";
1187 case AST_AOC_BILLING_CALL_DEFLECTION:
1188 return "CallDeflection";
1189 case AST_AOC_BILLING_CALL_TRANSFER:
1190 return "CallTransfer";
1191 case AST_AOC_BILLING_NA:
1192 return "NotAvailable";
1193 case AST_AOC_BILLING_NUM_ENTRIES:
1196 return "NotAvailable";
1199 int ast_aoc_test_encode_decode_match(struct ast_aoc_decoded *decoded)
1201 struct ast_aoc_decoded *new_decoded = NULL;
1202 struct ast_aoc_encoded *encoded = NULL;
1206 if (!(encoded = ast_aoc_encode(decoded, &size, NULL))) {
1210 if (!(new_decoded = ast_aoc_decode(encoded, size, NULL))) {
1215 if (memcmp(new_decoded, decoded, sizeof(struct ast_aoc_decoded))) {
1219 ast_aoc_destroy_decoded(new_decoded);
1220 ast_aoc_destroy_encoded(encoded);
1224 static char *aoc_cli_debug_enable(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1228 e->command = "aoc set debug";
1230 "Usage: 'aoc set debug on' to enable aoc debug, 'aoc set debug off' to disable debug.\n";
1236 return CLI_SHOWUSAGE;
1237 } else if(ast_true(a->argv[3])) {
1238 ast_cli(a->fd, "aoc debug enabled\n");
1239 aoc_debug_enabled = 1;
1240 } else if (ast_false(a->argv[3])) {
1241 ast_cli(a->fd, "aoc debug disabled\n");
1242 aoc_debug_enabled = 0;
1244 return CLI_SHOWUSAGE;
1253 * \brief Append the time structure to the event message string.
1256 * \param msg Event message string being built.
1257 * \param prefix Prefix to add to the amount lines.
1258 * \param name Name of the time structure to convert.
1259 * \param time Data to convert.
1260 * \param scale Data to convert.
1264 static void aoc_time_str(struct ast_str **msg, const char *prefix, const char *name, unsigned long time, enum ast_aoc_time_scale scale)
1266 ast_str_append(msg, 0, "%s/%s/Length: %lu\r\n", prefix, name, time);
1267 ast_str_append(msg, 0, "%s/%s/Scale: %s\r\n", prefix, name,
1268 aoc_scale_str(scale));
1273 * \brief Append the amount structure to the event message string.
1276 * \param msg Event message string being built.
1277 * \param prefix Prefix to add to the amount lines.
1278 * \param amount Data to convert.
1279 * \param multipler to convert
1283 static void aoc_amount_str(struct ast_str **msg, const char *prefix, unsigned int amount, enum ast_aoc_currency_multiplier mult)
1285 static const char name[] = "Amount";
1287 ast_str_append(msg, 0, "%s/%s/Cost: %u\r\n", prefix, name, amount);
1288 ast_str_append(msg, 0, "%s/%s/Multiplier: %s\r\n", prefix, name,
1289 aoc_multiplier_str(mult));
1292 static void aoc_request_event(const struct ast_aoc_decoded *decoded, struct ast_channel *chan, struct ast_str **msg)
1295 ast_str_append(msg, 0, "Channel: %s\r\n", ast_channel_name(chan));
1296 ast_str_append(msg, 0, "UniqueID: %s\r\n", ast_channel_uniqueid(chan));
1299 if (decoded->request_flag) {
1300 ast_str_append(msg, 0, "AOCRequest:");
1301 if (decoded->request_flag & AST_AOC_REQUEST_S) {
1302 ast_str_append(msg, 0, "S");
1304 if (decoded->request_flag & AST_AOC_REQUEST_D) {
1305 ast_str_append(msg, 0, "D");
1307 if (decoded->request_flag & AST_AOC_REQUEST_E) {
1308 ast_str_append(msg, 0, "E");
1310 ast_str_append(msg, 0, "\r\n");
1313 ast_str_append(msg, 0, "AOCRequest: NONE\r\n");
1317 static void aoc_s_event(const struct ast_aoc_decoded *decoded, struct ast_channel *owner, struct ast_str **msg)
1319 const char *rate_str;
1324 ast_str_append(msg, 0, "Channel: %s\r\n", ast_channel_name(owner));
1325 ast_str_append(msg, 0, "UniqueID: %s\r\n", ast_channel_uniqueid(owner));
1328 ast_str_append(msg, 0, "NumberRates: %d\r\n", decoded->aoc_s_count);
1329 for (idx = 0; idx < decoded->aoc_s_count; ++idx) {
1330 snprintf(prefix, sizeof(prefix), "Rate(%d)", idx);
1332 ast_str_append(msg, 0, "%s/Chargeable: %s\r\n", prefix,
1333 aoc_charged_item_str(decoded->aoc_s_entries[idx].charged_item));
1334 if (decoded->aoc_s_entries[idx].charged_item == AST_AOC_CHARGED_ITEM_NA) {
1337 rate_str = aoc_rate_type_str(decoded->aoc_s_entries[idx].rate_type);
1338 ast_str_append(msg, 0, "%s/Type: %s\r\n", prefix, rate_str);
1339 switch (decoded->aoc_s_entries[idx].rate_type) {
1340 case AST_AOC_RATE_TYPE_DURATION:
1341 strcat(prefix, "/");
1342 strcat(prefix, rate_str);
1343 ast_str_append(msg, 0, "%s/Currency: %s\r\n", prefix,
1344 decoded->aoc_s_entries[idx].rate.duration.currency_name);
1345 aoc_amount_str(msg, prefix,
1346 decoded->aoc_s_entries[idx].rate.duration.amount,
1347 decoded->aoc_s_entries[idx].rate.duration.multiplier);
1348 ast_str_append(msg, 0, "%s/ChargingType: %s\r\n", prefix,
1349 decoded->aoc_s_entries[idx].rate.duration.charging_type ?
1350 "StepFunction" : "ContinuousCharging");
1351 aoc_time_str(msg, prefix, "Time",
1352 decoded->aoc_s_entries[idx].rate.duration.time,
1353 decoded->aoc_s_entries[idx].rate.duration.time_scale);
1354 if (decoded->aoc_s_entries[idx].rate.duration.granularity_time) {
1355 aoc_time_str(msg, prefix, "Granularity",
1356 decoded->aoc_s_entries[idx].rate.duration.granularity_time,
1357 decoded->aoc_s_entries[idx].rate.duration.granularity_time_scale);
1360 case AST_AOC_RATE_TYPE_FLAT:
1361 strcat(prefix, "/");
1362 strcat(prefix, rate_str);
1363 ast_str_append(msg, 0, "%s/Currency: %s\r\n", prefix,
1364 decoded->aoc_s_entries[idx].rate.flat.currency_name);
1365 aoc_amount_str(msg, prefix,
1366 decoded->aoc_s_entries[idx].rate.flat.amount,
1367 decoded->aoc_s_entries[idx].rate.flat.multiplier);
1369 case AST_AOC_RATE_TYPE_VOLUME:
1370 strcat(prefix, "/");
1371 strcat(prefix, rate_str);
1372 ast_str_append(msg, 0, "%s/Currency: %s\r\n", prefix,
1373 decoded->aoc_s_entries[idx].rate.volume.currency_name);
1374 aoc_amount_str(msg, prefix,
1375 decoded->aoc_s_entries[idx].rate.volume.amount,
1376 decoded->aoc_s_entries[idx].rate.volume.multiplier);
1377 ast_str_append(msg, 0, "%s/Unit: %s\r\n", prefix,
1378 aoc_volume_unit_str(decoded->aoc_s_entries[idx].rate.volume.volume_unit));
1380 case AST_AOC_RATE_TYPE_SPECIAL_CODE:
1381 ast_str_append(msg, 0, "%s/%s: %d\r\n", prefix, rate_str,
1382 decoded->aoc_s_entries[idx].rate.special_code);
1390 static void aoc_d_event(const struct ast_aoc_decoded *decoded, struct ast_channel *chan, struct ast_str **msg)
1392 const char *charge_str;
1397 ast_str_append(msg, 0, "Channel: %s\r\n", ast_channel_name(chan));
1398 ast_str_append(msg, 0, "UniqueID: %s\r\n", ast_channel_uniqueid(chan));
1401 charge_str = aoc_charge_type_str(decoded->charge_type);
1402 ast_str_append(msg, 0, "Type: %s\r\n", charge_str);
1404 switch (decoded->charge_type) {
1405 case AST_AOC_CHARGE_CURRENCY:
1406 case AST_AOC_CHARGE_UNIT:
1407 ast_str_append(msg, 0, "BillingID: %s\r\n",
1408 aoc_billingid_str(decoded->billing_id));
1409 ast_str_append(msg, 0, "TypeOfCharging: %s\r\n",
1410 aoc_type_of_totaling_str(decoded->total_type));
1416 switch (decoded->charge_type) {
1417 case AST_AOC_CHARGE_CURRENCY:
1418 ast_str_append(msg, 0, "%s: %s\r\n", charge_str,
1419 decoded->currency_name);
1420 aoc_amount_str(msg, charge_str,
1421 decoded->currency_amount,
1422 decoded->multiplier);
1424 case AST_AOC_CHARGE_UNIT:
1425 ast_str_append(msg, 0, "%s/NumberItems: %d\r\n", charge_str,
1426 decoded->unit_count);
1427 for (idx = 0; idx < decoded->unit_count; ++idx) {
1428 snprintf(prefix, sizeof(prefix), "%s/Item(%d)", charge_str, idx);
1429 if (decoded->unit_list[idx].valid_amount) {
1430 ast_str_append(msg, 0, "%s/NumberOf: %u\r\n", prefix,
1431 decoded->unit_list[idx].amount);
1433 if (decoded->unit_list[idx].valid_type) {
1434 ast_str_append(msg, 0, "%s/TypeOf: %d\r\n", prefix,
1435 decoded->unit_list[idx].type);
1444 static void aoc_e_event(const struct ast_aoc_decoded *decoded, struct ast_channel *chan, struct ast_str **msg)
1446 const char *charge_str;
1451 ast_str_append(msg, 0, "Channel: %s\r\n", ast_channel_name(chan));
1452 ast_str_append(msg, 0, "UniqueID: %s\r\n", ast_channel_uniqueid(chan));
1455 charge_str = "ChargingAssociation";
1457 switch (decoded->charging_association.charging_type) {
1458 case AST_AOC_CHARGING_ASSOCIATION_NUMBER:
1459 snprintf(prefix, sizeof(prefix), "%s/Number", charge_str);
1460 ast_str_append(msg, 0, "%s: %s\r\n", prefix,
1461 decoded->charging_association.charge.number.number);
1462 ast_str_append(msg, 0, "%s/Plan: %d\r\n", prefix,
1463 decoded->charging_association.charge.number.plan);
1465 case AST_AOC_CHARGING_ASSOCIATION_ID:
1466 ast_str_append(msg, 0, "%s/ID: %d\r\n", charge_str, decoded->charging_association.charge.id);
1468 case AST_AOC_CHARGING_ASSOCIATION_NA:
1473 charge_str = aoc_charge_type_str(decoded->charge_type);
1474 ast_str_append(msg, 0, "Type: %s\r\n", charge_str);
1475 switch (decoded->charge_type) {
1476 case AST_AOC_CHARGE_CURRENCY:
1477 case AST_AOC_CHARGE_UNIT:
1478 ast_str_append(msg, 0, "BillingID: %s\r\n",
1479 aoc_billingid_str(decoded->billing_id));
1484 switch (decoded->charge_type) {
1485 case AST_AOC_CHARGE_CURRENCY:
1486 ast_str_append(msg, 0, "%s: %s\r\n", charge_str,
1487 decoded->currency_name);
1488 aoc_amount_str(msg, charge_str,
1489 decoded->currency_amount,
1490 decoded->multiplier);
1492 case AST_AOC_CHARGE_UNIT:
1493 ast_str_append(msg, 0, "%s/NumberItems: %d\r\n", charge_str,
1494 decoded->unit_count);
1495 for (idx = 0; idx < decoded->unit_count; ++idx) {
1496 snprintf(prefix, sizeof(prefix), "%s/Item(%d)", charge_str, idx);
1497 if (decoded->unit_list[idx].valid_amount) {
1498 ast_str_append(msg, 0, "%s/NumberOf: %u\r\n", prefix,
1499 decoded->unit_list[idx].amount);
1501 if (decoded->unit_list[idx].valid_type) {
1502 ast_str_append(msg, 0, "%s/TypeOf: %d\r\n", prefix,
1503 decoded->unit_list[idx].type);
1512 int ast_aoc_manager_event(const struct ast_aoc_decoded *decoded, struct ast_channel *chan)
1514 struct ast_str *msg;
1516 if (!decoded || !(msg = ast_str_create(1024))) {
1520 switch (decoded->msg_type) {
1523 aoc_s_event(decoded, chan, &msg);
1524 ast_manager_event(chan, EVENT_FLAG_AOC, "AOC-S", "%s", ast_str_buffer(msg));
1529 aoc_d_event(decoded, chan, &msg);
1530 ast_manager_event(chan, EVENT_FLAG_AOC, "AOC-D", "%s", ast_str_buffer(msg));
1535 struct ast_channel *chans[1];
1536 aoc_e_event(decoded, chan, &msg);
1538 ast_manager_event_multichan(EVENT_FLAG_AOC, "AOC-E", chan ? 1 : 0, chans, "%s", ast_str_buffer(msg));
1542 /* events for AST_AOC_REQUEST are not generated here */
1550 int ast_aoc_decoded2str(const struct ast_aoc_decoded *decoded, struct ast_str **msg)
1552 if (!decoded || !msg) {
1556 switch (decoded->msg_type) {
1558 ast_str_append(msg, 0, "AOC-S\r\n");
1559 aoc_s_event(decoded, NULL, msg);
1562 ast_str_append(msg, 0, "AOC-D\r\n");
1563 aoc_d_event(decoded, NULL, msg);
1566 ast_str_append(msg, 0, "AOC-E\r\n");
1567 aoc_e_event(decoded, NULL, msg);
1569 case AST_AOC_REQUEST:
1570 ast_str_append(msg, 0, "AOC-Request\r\n");
1571 aoc_request_event(decoded, NULL, msg);
1578 static void aoc_display_decoded_debug(const struct ast_aoc_decoded *decoded, int decoding, struct ast_channel *chan)
1580 struct ast_str *msg;
1582 if (!decoded || !(msg = ast_str_create(1024))) {
1587 ast_str_append(&msg, 0, "---- DECODED AOC MSG ----\r\n");
1589 ast_str_append(&msg, 0, "---- ENCODED AOC MSG ----\r\n");
1592 ast_str_append(&msg, 0, "CHANNEL: %s\r\n", ast_channel_name(chan));
1595 if (ast_aoc_decoded2str(decoded, &msg)) {
1600 ast_verb(1, "%s\r\n", ast_str_buffer(msg));
1604 static struct ast_cli_entry aoc_cli[] = {
1605 AST_CLI_DEFINE(aoc_cli_debug_enable, "enable cli debugging of AOC messages"),
1608 static void aoc_shutdown(void)
1610 ast_cli_unregister_multiple(aoc_cli, ARRAY_LEN(aoc_cli));
1612 int ast_aoc_cli_init(void)
1614 ast_register_atexit(aoc_shutdown);
1615 return ast_cli_register_multiple(aoc_cli, ARRAY_LEN(aoc_cli));