Reset automerge property.
[asterisk/asterisk.git] / main / aoc.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2010, Digium, Inc.
5  *
6  * David Vossel <dvossel@digium.com>
7  *
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.
13  *
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.
17  */
18
19 /*!
20  * \file
21  * \brief generic AOC payload generation encoding and decoding
22  *
23  * \author David Vossel <dvossel@digium.com>
24  */
25
26 /*** MODULEINFO
27         <support_level>core</support_level>
28  ***/
29
30 #include "asterisk.h"
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
32
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
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)
45
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)
49
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)
54
55 #define AST_AOC_ENCODED_CHARGE_SUBTOTAL (1 << 7)
56 #define AST_AOC_ENCODED_CHARGE_TOTAL    (0 << 7)
57
58 #define AST_AOC_ENCODE_VERSION 1
59
60
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);
64
65 /* AOC Payload Header. Holds all the encoded AOC data to pass on the wire */
66 struct ast_aoc_encoded {
67         uint8_t  version;
68         uint8_t  flags;
69         uint16_t datalen;
70         unsigned char data[0];
71 };
72
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;
79
80         /* currency information */
81         enum ast_aoc_currency_multiplier multiplier;
82         unsigned int currency_amount;
83         char currency_name[AOC_CURRENCY_NAME_SIZE];
84
85         /* unit information */
86         int unit_count;
87         struct ast_aoc_unit_entry unit_list[32];
88
89         /* Billing Id */
90         enum ast_aoc_billing_id billing_id;
91
92         /* Charging Association information */
93         struct ast_aoc_charging_association charging_association;
94
95         /* AOC-S charge information */
96         int aoc_s_count;
97         struct ast_aoc_s_entry aoc_s_entries[10];
98
99         /* Is this an AOC Termination Request */
100         char termination_request;
101 };
102
103 /*! \brief AOC Payload Information Elements */
104 enum AOC_IE {
105         AOC_IE_CURRENCY = 1,
106         AOC_IE_UNIT = 2,
107         AOC_IE_BILLING = 3,
108         AOC_IE_CHARGING_ASSOCIATION = 4,
109         AOC_IE_RATE = 5,
110         AOC_IE_TERMINATION_REQUEST = 6,
111 };
112
113 /*! \brief AOC IE payload header */
114 struct aoc_pl_ie_hdr {
115         uint8_t ie_id;
116         uint8_t datalen;
117         char data[0];
118 } __attribute__((packed));
119
120 struct aoc_ie_currency {
121         uint32_t amount;
122         uint8_t  multiplier;
123         char name[AOC_CURRENCY_NAME_SIZE];
124 } __attribute__((packed));
125
126 struct aoc_ie_unit {
127         uint32_t amount;
128         uint8_t valid_type;
129         uint8_t valid_amount;
130         uint8_t type;
131 } __attribute__((packed));
132
133 struct aoc_ie_billing {
134         uint8_t id;
135 } __attribute__((packed));
136
137 struct aoc_ie_charging_association {
138         struct ast_aoc_charging_association ca;
139 } __attribute__((packed));
140
141 struct aoc_ie_charging_rate {
142         struct ast_aoc_s_entry entry;
143 } __attribute__((packed));
144
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)
148 {
149         struct ast_aoc_decoded *decoded = NULL;
150
151         /* verify input */
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)) {
155
156                 ast_log(LOG_WARNING, "Failed to create ast_aoc_decoded object, invalid input\n");
157                 return NULL;
158         }
159
160         if (!(decoded = ast_calloc(1, sizeof(struct ast_aoc_decoded)))) {
161                 ast_log(LOG_WARNING, "Failed to create ast_aoc_decoded object \n");
162                 return NULL;
163         }
164
165         decoded->msg_type = msg_type;
166
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;
171         }
172
173         return decoded;
174 }
175
176 void *ast_aoc_destroy_decoded(struct ast_aoc_decoded *decoded)
177 {
178         ast_free(decoded);
179         return NULL;
180 }
181
182 void *ast_aoc_destroy_encoded(struct ast_aoc_encoded *encoded)
183 {
184         ast_free(encoded);
185         return NULL;
186 }
187
188 static void aoc_parse_ie_charging_rate(struct ast_aoc_decoded *decoded, const struct aoc_ie_charging_rate *ie)
189 {
190         struct ast_aoc_s_entry entry = { 0, };
191
192         entry.charged_item = ntohs(ie->entry.charged_item);
193         entry.rate_type = ntohs(ie->entry.rate_type);
194
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 */
204
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));
209                 }
210                 break;
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));
218                 }
219                 break;
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));
228                 }
229                 break;
230         case AST_AOC_RATE_TYPE_SPECIAL_CODE:
231                 entry.rate.special_code = ntohs(ie->entry.rate.special_code);
232                 break;
233         }
234
235         aoc_s_add_entry(decoded, &entry);
236 }
237
238 static int aoc_parse_ie(struct ast_aoc_decoded *decoded, unsigned char *data, unsigned int datalen)
239 {
240         enum AOC_IE ie_id;
241         unsigned int len;
242
243         while (datalen >= 2) {
244                 ie_id = data[0];
245                 len = data[1];
246                 if (len > datalen -2) {
247                         ast_log(LOG_ERROR, "AOC information element length exceeds the total message size\n");
248                         return -1;
249                 }
250
251                 switch(ie_id) {
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));
259                         } else {
260                                 ast_log(LOG_WARNING, "Received invalid currency ie\n");
261                         }
262                         break;
263                 case AOC_IE_UNIT:
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);
268                         } else {
269                                 ast_log(LOG_WARNING, "Received invalid unit ie\n");
270                         }
271                         break;
272                 case AOC_IE_BILLING:
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 */
277                         } else {
278                                 ast_log(LOG_WARNING, "Received invalid billing ie\n");
279                         }
280                         break;
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);
287                                 }
288                         } else {
289                                 ast_log(LOG_WARNING, "Received invalid charging association ie\n");
290                         }
291                         break;
292                 case AOC_IE_RATE:
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);
297                         } else {
298                                 ast_log(LOG_WARNING, "Received invalid charging rate ie\n");
299                         }
300                         break;
301                 case AOC_IE_TERMINATION_REQUEST:
302                         if (len == 0) {
303                                 decoded->termination_request = 1;
304                         } else {
305                                 ast_log(LOG_WARNING, "Received invalid termination request ie\n");
306                         }
307                         break;
308                 default:
309                         ast_log(LOG_WARNING, "Unknown AOC Information Element, ignoring.\n");
310                 }
311
312                 datalen -= (len + 2);
313                 data += (len + 2);
314         }
315         return 0;
316 }
317
318 struct ast_aoc_decoded *ast_aoc_decode(struct ast_aoc_encoded *encoded, size_t size, struct ast_channel *chan)
319 {
320         struct ast_aoc_decoded *decoded;
321
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");
325                 return NULL;
326         }
327
328         if (!(decoded = ast_calloc(1, sizeof(struct ast_aoc_decoded)))) {
329                 ast_log(LOG_WARNING, "Failed to create ast_aoc_decoded object \n");
330                 return NULL;
331         }
332
333         /* decode flags */
334
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;
341         } else {
342                 decoded->msg_type = AST_AOC_REQUEST;
343         }
344
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;
348                 }
349                 if (encoded->flags & AST_AOC_ENCODED_REQUEST_D) {
350                         decoded->request_flag |= AST_AOC_REQUEST_D;
351                 }
352                 if (encoded->flags & AST_AOC_ENCODED_REQUEST_E) {
353                         decoded->request_flag |= AST_AOC_REQUEST_E;
354                 }
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;
362                 } else {
363                         decoded->charge_type = AST_AOC_CHARGE_NA;
364                 }
365
366                 if (encoded->flags & AST_AOC_ENCODED_CHARGE_SUBTOTAL) {
367                         decoded->total_type = AST_AOC_SUBTOTAL;
368                 }
369         }
370
371         /* decode information elements */
372         aoc_parse_ie(decoded, encoded->data, ntohs(encoded->datalen));
373
374         if (aoc_debug_enabled) {
375                 aoc_display_decoded_debug(decoded, 1, chan);
376         }
377
378         return decoded;
379 }
380
381 struct aoc_ie_data {
382         unsigned char buf[1024];
383         int pos;
384 };
385
386 /*!
387  * \internal
388  * \brief append an AOC information element
389  * \note data is expected to already be in network byte order at this point
390  */
391 static int aoc_append_ie(struct aoc_ie_data *ied, unsigned short ie_id, const void *data, unsigned short datalen)
392 {
393         if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
394                 ast_log(LOG_WARNING, "Failure to append AOC information element, out of space \n");
395                 return -1;
396         }
397         ied->buf[ied->pos++] = ie_id;
398         ied->buf[ied->pos++] = datalen;
399         if (datalen) {
400                 memcpy(ied->buf + ied->pos, data, datalen);
401                 ied->pos += datalen;
402         }
403         return 0;
404 }
405
406 static void aoc_create_ie_data_charging_rate(const struct ast_aoc_s_entry *entry, struct aoc_ie_charging_rate *ie)
407 {
408         ie->entry.charged_item = htons(entry->charged_item);
409         ie->entry.rate_type = htons(entry->rate_type);
410
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 */
420
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));
425                 }
426                 break;
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));
434                 }
435                 break;
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));
444                 }
445                 break;
446         case AST_AOC_RATE_TYPE_SPECIAL_CODE:
447                 ie->entry.rate.special_code = htons(entry->rate.special_code);
448                 break;
449         }
450
451 }
452 static void aoc_create_ie_data(struct ast_aoc_decoded *decoded, struct aoc_ie_data *ied)
453 {
454         ied->pos = 0;
455
456         if (decoded->currency_amount) {
457                 struct aoc_ie_currency ie = {
458                         .amount = htonl(decoded->currency_amount),
459                         .multiplier = decoded->multiplier, /* only one byte */
460                         .name = { 0, },
461                 };
462
463                 if (!ast_strlen_zero(decoded->currency_name)) {
464                         ast_copy_string(ie.name, decoded->currency_name, sizeof(ie.name));
465                 }
466
467                 aoc_append_ie(ied, AOC_IE_CURRENCY, (const void *) &ie, sizeof(ie));
468         }
469
470         if (decoded->unit_count) {
471                 struct aoc_ie_unit ie = { 0 };
472                 int i;
473
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));
480                 }
481         }
482
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));
487         }
488
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);
500                 }
501                 aoc_append_ie(ied, AOC_IE_CHARGING_ASSOCIATION, (const void *) &ie, sizeof(ie));
502         }
503
504         if (decoded->aoc_s_count) {
505                 struct aoc_ie_charging_rate ie;
506                 int i;
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));
511                 }
512         }
513
514         if (decoded->termination_request) {
515                 aoc_append_ie(ied, AOC_IE_TERMINATION_REQUEST, NULL, 0);
516         }
517 }
518
519 struct ast_aoc_encoded *ast_aoc_encode(struct ast_aoc_decoded *decoded, size_t *out_size, struct ast_channel *chan)
520 {
521         struct aoc_ie_data ied;
522         struct ast_aoc_encoded *encoded = NULL;
523         size_t size = 0;
524
525         if (!decoded || !out_size) {
526                 return NULL;
527         }
528
529         *out_size = 0;
530
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);
535
536         size = sizeof(struct ast_aoc_encoded) + ied.pos;
537
538         if (!(encoded = ast_calloc(1, size))) {
539                 ast_log(LOG_WARNING, "Failed to create ast_aoc_encoded object during decode routine. \n");
540                 return NULL;
541         }
542
543         /* -- Set ie data buffer */
544         if (ied.pos) {
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);
548         }
549
550         /* --- Set Flags --- */
551         switch (decoded->msg_type) {
552         case AST_AOC_S:
553                 encoded->flags = AST_AOC_ENCODED_TYPE_S;
554                 break;
555         case AST_AOC_D:
556                 encoded->flags = AST_AOC_ENCODED_TYPE_D;
557                 break;
558         case AST_AOC_E:
559                 encoded->flags = AST_AOC_ENCODED_TYPE_E;
560                 break;
561         case AST_AOC_REQUEST:
562                 encoded->flags = AST_AOC_ENCODED_TYPE_REQUEST;
563         default:
564                 break;
565         }
566
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;
571                 }
572                 if (decoded->request_flag & AST_AOC_REQUEST_D) {
573                         encoded->flags |= AST_AOC_ENCODED_REQUEST_D;
574                 }
575                 if (decoded->request_flag & AST_AOC_REQUEST_E) {
576                         encoded->flags |= AST_AOC_ENCODED_REQUEST_E;
577                 }
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;
582                         break;
583                 case AST_AOC_CHARGE_CURRENCY:
584                         encoded->flags |= AST_AOC_ENCODED_CHARGE_CURRENCY;
585                         break;
586                 case AST_AOC_CHARGE_FREE:
587                         encoded->flags |= AST_AOC_ENCODED_CHARGE_FREE;
588                 case AST_AOC_CHARGE_NA:
589                 default:
590                         encoded->flags |= AST_AOC_ENCODED_CHARGE_NA;
591                         break;
592                 }
593
594                 if (decoded->total_type == AST_AOC_SUBTOTAL) {
595                         encoded->flags |= AST_AOC_ENCODED_CHARGE_SUBTOTAL;
596                 }
597         }
598
599         /* --- Set Version Number --- */
600         encoded->version = AST_AOC_ENCODE_VERSION;
601
602         /* set the output size  */
603         *out_size = size;
604
605         if (aoc_debug_enabled) {
606                 aoc_display_decoded_debug(decoded, 0, chan);
607         }
608
609         return encoded;
610 }
611
612 static int aoc_s_add_entry(struct ast_aoc_decoded *decoded, struct ast_aoc_s_entry *entry)
613 {
614         if (decoded->aoc_s_count >= ARRAY_LEN(decoded->aoc_s_entries)) {
615                 return -1;
616         }
617
618         decoded->aoc_s_entries[decoded->aoc_s_count] = *entry;
619         decoded->aoc_s_count++;
620
621         return 0;
622 }
623
624
625 unsigned int ast_aoc_s_get_count(struct ast_aoc_decoded *decoded)
626 {
627         return decoded->aoc_s_count;
628 }
629
630 const struct ast_aoc_s_entry *ast_aoc_s_get_rate_info(struct ast_aoc_decoded *decoded, unsigned int entry_number)
631 {
632         if (entry_number >= decoded->aoc_s_count) {
633                 return NULL;
634         }
635
636         return (const struct ast_aoc_s_entry *) &decoded->aoc_s_entries[entry_number];
637 }
638
639 int ast_aoc_s_add_rate_duration(struct ast_aoc_decoded *decoded,
640         enum ast_aoc_s_charged_item charged_item,
641         unsigned int amount,
642         enum ast_aoc_currency_multiplier multiplier,
643         const char *currency_name,
644         unsigned long time,
645         enum ast_aoc_time_scale time_scale,
646         unsigned long granularity_time,
647         enum ast_aoc_time_scale granularity_time_scale,
648         int step_function)
649 {
650
651         struct ast_aoc_s_entry entry = { 0, };
652
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;
662
663         if (!ast_strlen_zero(currency_name)) {
664                 ast_copy_string(entry.rate.duration.currency_name, currency_name, sizeof(entry.rate.duration.currency_name));
665         }
666
667         return aoc_s_add_entry(decoded, &entry);
668 }
669
670 int ast_aoc_s_add_rate_flat(struct ast_aoc_decoded *decoded,
671         enum ast_aoc_s_charged_item charged_item,
672         unsigned int amount,
673         enum ast_aoc_currency_multiplier multiplier,
674         const char *currency_name)
675 {
676         struct ast_aoc_s_entry entry = { 0, };
677
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;
682
683         if (!ast_strlen_zero(currency_name)) {
684                 ast_copy_string(entry.rate.flat.currency_name, currency_name, sizeof(entry.rate.flat.currency_name));
685         }
686
687         return aoc_s_add_entry(decoded, &entry);
688 }
689
690
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,
694         unsigned int amount,
695         enum ast_aoc_currency_multiplier multiplier,
696         const char *currency_name)
697 {
698         struct ast_aoc_s_entry entry = { 0, };
699
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;
705
706         if (!ast_strlen_zero(currency_name)) {
707                 ast_copy_string(entry.rate.volume.currency_name, currency_name, sizeof(entry.rate.volume.currency_name));
708         }
709
710         return aoc_s_add_entry(decoded, &entry);
711 }
712
713 int ast_aoc_s_add_rate_special_charge_code(struct ast_aoc_decoded *decoded,
714         enum ast_aoc_s_charged_item charged_item,
715         unsigned int code)
716 {
717         struct ast_aoc_s_entry entry = { 0, };
718
719         entry.charged_item = charged_item;
720         entry.rate_type = AST_AOC_RATE_TYPE_SPECIAL_CODE;
721         entry.rate.special_code = code;
722
723         return aoc_s_add_entry(decoded, &entry);
724 }
725
726 int ast_aoc_s_add_rate_free(struct ast_aoc_decoded *decoded,
727         enum ast_aoc_s_charged_item charged_item,
728         int from_beginning)
729 {
730         struct ast_aoc_s_entry entry = { 0, };
731
732         entry.charged_item = charged_item;
733         entry.rate_type = from_beginning ? AST_AOC_RATE_TYPE_FREE_FROM_BEGINNING : AST_AOC_RATE_TYPE_FREE;
734
735         return aoc_s_add_entry(decoded, &entry);
736 }
737
738 int ast_aoc_s_add_rate_na(struct ast_aoc_decoded *decoded,
739         enum ast_aoc_s_charged_item charged_item)
740 {
741         struct ast_aoc_s_entry entry = { 0, };
742
743         entry.charged_item = charged_item;
744         entry.rate_type = AST_AOC_RATE_TYPE_NA;
745
746         return aoc_s_add_entry(decoded, &entry);
747 }
748
749 int ast_aoc_s_add_special_arrangement(struct ast_aoc_decoded *decoded,
750         unsigned int code)
751 {
752         struct ast_aoc_s_entry entry = { 0, };
753
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;
757
758         return aoc_s_add_entry(decoded, &entry);
759 }
760
761 enum ast_aoc_type ast_aoc_get_msg_type(struct ast_aoc_decoded *decoded)
762 {
763         return decoded->msg_type;
764 }
765
766 enum ast_aoc_charge_type ast_aoc_get_charge_type(struct ast_aoc_decoded *decoded)
767 {
768         return decoded->charge_type;
769 }
770
771 enum ast_aoc_request ast_aoc_get_request(struct ast_aoc_decoded *decoded)
772 {
773         return decoded->request_flag;
774 }
775
776 int ast_aoc_set_total_type(struct ast_aoc_decoded *decoded,
777         const enum ast_aoc_total_type type)
778 {
779         decoded->total_type = type;
780         return 0;
781 }
782
783 enum ast_aoc_total_type ast_aoc_get_total_type(struct ast_aoc_decoded *decoded)
784 {
785         return decoded->total_type;
786 }
787
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,
791                 const char *name)
792 {
793
794         if (!ast_strlen_zero(name)) {
795                 ast_copy_string(decoded->currency_name, name, sizeof(decoded->currency_name));
796         }
797
798         decoded->currency_amount = amount;
799
800         if (multiplier && (multiplier < AST_AOC_MULT_NUM_ENTRIES)) {
801                 decoded->multiplier = multiplier;
802         } else {
803                 decoded->multiplier = AST_AOC_MULT_ONE;
804         }
805
806         return 0;
807 }
808
809 unsigned int ast_aoc_get_currency_amount(struct ast_aoc_decoded *decoded)
810 {
811         return decoded->currency_amount;
812 }
813
814 enum ast_aoc_currency_multiplier ast_aoc_get_currency_multiplier(struct ast_aoc_decoded *decoded)
815 {
816         return decoded->multiplier;
817 }
818
819 const char *ast_aoc_get_currency_multiplier_decimal(struct ast_aoc_decoded *decoded)
820 {
821         switch (decoded->multiplier) {
822         case AST_AOC_MULT_ONETHOUSANDTH:
823                 return "0.001";
824         case AST_AOC_MULT_ONEHUNDREDTH:
825                 return "0.01";
826         case AST_AOC_MULT_ONETENTH:
827                 return "0.1";
828         case AST_AOC_MULT_ONE:
829                 return "1.0";
830         case AST_AOC_MULT_TEN:
831                 return "10.0";
832         case AST_AOC_MULT_HUNDRED:
833                 return "100.0";
834         case AST_AOC_MULT_THOUSAND:
835                 return "1000.0";
836         default:
837                 return "1.0";
838         }
839 }
840
841 const char *ast_aoc_get_currency_name(struct ast_aoc_decoded *decoded)
842 {
843         return decoded->currency_name;
844 }
845
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)
851 {
852         if ((decoded->msg_type == AST_AOC_REQUEST) ||
853                 (decoded->unit_count >= ARRAY_LEN(decoded->unit_list))) {
854                 return -1;
855         }
856
857         if (!amount_is_present && !type_is_present) {
858                 return -1;
859         }
860
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;
864         } else {
865                 decoded->unit_list[decoded->unit_count].amount = 0;
866         }
867
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;
871         } else {
872                 decoded->unit_list[decoded->unit_count].type = 0;
873         }
874         decoded->unit_count++;
875
876         return 0;
877 }
878
879 const struct ast_aoc_unit_entry *ast_aoc_get_unit_info(struct ast_aoc_decoded *decoded, unsigned int entry_number)
880 {
881         if (entry_number >= decoded->unit_count) {
882                 return NULL;
883         }
884
885         return (const struct ast_aoc_unit_entry *) &decoded->unit_list[entry_number];
886 }
887
888 unsigned int ast_aoc_get_unit_count(struct ast_aoc_decoded *decoded)
889 {
890         return decoded->unit_count;
891 }
892
893 int ast_aoc_set_billing_id(struct ast_aoc_decoded *decoded, const enum ast_aoc_billing_id id)
894 {
895         if ((id >= AST_AOC_BILLING_NUM_ENTRIES) || (id < AST_AOC_BILLING_NA)) {
896                 return -1;
897         }
898
899         decoded->billing_id = id;
900
901         return 0;
902 }
903
904 enum ast_aoc_billing_id ast_aoc_get_billing_id(struct ast_aoc_decoded *decoded)
905 {
906         return decoded->billing_id;
907 }
908
909 int ast_aoc_set_association_id(struct ast_aoc_decoded *decoded, const int id)
910 {
911         if (decoded->msg_type != AST_AOC_E) {
912                 return -1;
913         }
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;
917         return 0;
918 }
919
920 const struct ast_aoc_charging_association *ast_aoc_get_association_info(struct ast_aoc_decoded *decoded)
921 {
922         return &decoded->charging_association;
923 }
924
925 int ast_aoc_set_association_number(struct ast_aoc_decoded *decoded, const char *num, uint8_t plan)
926 {
927         if ((decoded->msg_type != AST_AOC_E) || ast_strlen_zero(num)) {
928                 return -1;
929         }
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));
934
935         return 0;
936 }
937
938 int ast_aoc_set_termination_request(struct ast_aoc_decoded *decoded)
939 {
940         if (decoded->msg_type != AST_AOC_REQUEST) {
941                 return -1;
942         }
943         decoded->termination_request = 1;
944
945         return 0;
946 }
947
948 int ast_aoc_get_termination_request(struct ast_aoc_decoded *decoded)
949 {
950         return decoded->termination_request;
951 }
952
953 /*!
954  * \internal
955  * \brief Convert AST_AOC_VOLUME_UNIT to string.
956  * \since 1.8
957  *
958  * \param value Value to convert to string.
959  *
960  * \return String equivalent.
961  */
962 static const char *aoc_volume_unit_str(enum ast_aoc_volume_unit value)
963 {
964         const char *str;
965
966         switch (value) {
967         default:
968         case AST_AOC_VOLUME_UNIT_OCTET:
969                 str = "Octet";
970                 break;
971         case AST_AOC_VOLUME_UNIT_SEGMENT:
972                 str = "Segment";
973                 break;
974         case AST_AOC_VOLUME_UNIT_MESSAGE:
975                 str = "Message";
976                 break;
977         }
978         return str;
979 }
980
981 /*!
982  * \internal
983  * \brief Convert ast_aoc_charged_item to string.
984  * \since 1.8
985  *
986  * \param value Value to convert to string.
987  *
988  * \return String equivalent.
989  */
990 static const char *aoc_charged_item_str(enum ast_aoc_s_charged_item value)
991 {
992         const char *str;
993
994         switch (value) {
995         default:
996         case AST_AOC_CHARGED_ITEM_NA:
997                 str = "NotAvailable";
998                 break;
999         case AST_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT:
1000                 str = "SpecialArrangement";
1001                 break;
1002         case AST_AOC_CHARGED_ITEM_BASIC_COMMUNICATION:
1003                 str = "BasicCommunication";
1004                 break;
1005         case AST_AOC_CHARGED_ITEM_CALL_ATTEMPT:
1006                 str = "CallAttempt";
1007                 break;
1008         case AST_AOC_CHARGED_ITEM_CALL_SETUP:
1009                 str = "CallSetup";
1010                 break;
1011         case AST_AOC_CHARGED_ITEM_USER_USER_INFO:
1012                 str = "UserUserInfo";
1013                 break;
1014         case AST_AOC_CHARGED_ITEM_SUPPLEMENTARY_SERVICE:
1015                 str = "SupplementaryService";
1016                 break;
1017         }
1018         return str;
1019 }
1020
1021 /*!
1022  * \internal
1023  * \brief Convert ast_aoc_total_type to string.
1024  * \since 1.8
1025  *
1026  * \param value Value to convert to string.
1027  *
1028  * \return String equivalent.
1029  */
1030 static const char *aoc_type_of_totaling_str(enum ast_aoc_total_type value)
1031 {
1032         const char *str;
1033
1034         switch (value) {
1035         default:
1036         case AST_AOC_SUBTOTAL:
1037                 str = "SubTotal";
1038                 break;
1039         case AST_AOC_TOTAL:
1040                 str = "Total";
1041                 break;
1042         }
1043         return str;
1044 }
1045
1046 /*!
1047  * \internal
1048  * \brief Convert ast_aoc_rate_type to string.
1049  * \since 1.8
1050  *
1051  * \param value Value to convert to string.
1052  *
1053  * \return String equivalent.
1054  */
1055 static const char *aoc_rate_type_str(enum ast_aoc_s_rate_type value)
1056 {
1057         const char *str;
1058
1059         switch (value) {
1060         default:
1061         case AST_AOC_RATE_TYPE_NA:
1062                 str = "NotAvailable";
1063                 break;
1064         case AST_AOC_RATE_TYPE_FREE:
1065                 str = "Free";
1066                 break;
1067         case AST_AOC_RATE_TYPE_FREE_FROM_BEGINNING:
1068                 str = "FreeFromBeginning";
1069                 break;
1070         case AST_AOC_RATE_TYPE_DURATION:
1071                 str = "Duration";
1072                 break;
1073         case AST_AOC_RATE_TYPE_FLAT:
1074                 str = "Flat";
1075                 break;
1076         case AST_AOC_RATE_TYPE_VOLUME:
1077                 str = "Volume";
1078                 break;
1079         case AST_AOC_RATE_TYPE_SPECIAL_CODE:
1080                 str = "SpecialCode";
1081                 break;
1082         }
1083         return str;
1084 }
1085
1086 /*!
1087  * \internal
1088  * \brief Convert AST_AOC_TIME_SCALE to string.
1089  * \since 1.8
1090  *
1091  * \param value Value to convert to string.
1092  *
1093  * \return String equivalent.
1094  */
1095 static const char *aoc_scale_str(enum ast_aoc_time_scale value)
1096 {
1097         const char *str;
1098
1099         switch (value) {
1100         default:
1101         case AST_AOC_TIME_SCALE_HUNDREDTH_SECOND:
1102                 str = "OneHundredthSecond";
1103                 break;
1104         case AST_AOC_TIME_SCALE_TENTH_SECOND:
1105                 str = "OneTenthSecond";
1106                 break;
1107         case AST_AOC_TIME_SCALE_SECOND:
1108                 str = "Second";
1109                 break;
1110         case AST_AOC_TIME_SCALE_TEN_SECOND:
1111                 str = "TenSeconds";
1112                 break;
1113         case AST_AOC_TIME_SCALE_MINUTE:
1114                 str = "Minute";
1115                 break;
1116         case AST_AOC_TIME_SCALE_HOUR:
1117                 str = "Hour";
1118                 break;
1119         case AST_AOC_TIME_SCALE_DAY:
1120                 str = "Day";
1121                 break;
1122         }
1123         return str;
1124 }
1125
1126 static const char *aoc_charge_type_str(enum ast_aoc_charge_type value)
1127 {
1128         const char *str;
1129
1130         switch (value) {
1131         default:
1132         case AST_AOC_CHARGE_NA:
1133                 str = "NotAvailable";
1134                 break;
1135         case AST_AOC_CHARGE_FREE:
1136                 str = "Free";
1137                 break;
1138         case AST_AOC_CHARGE_CURRENCY:
1139                 str = "Currency";
1140                 break;
1141         case AST_AOC_CHARGE_UNIT:
1142                 str = "Units";
1143                 break;
1144         }
1145
1146         return str;
1147 }
1148
1149 static const char *aoc_multiplier_str(enum ast_aoc_currency_multiplier mult)
1150 {
1151         switch (mult) {
1152         case AST_AOC_MULT_ONETHOUSANDTH:
1153                 return "1/1000";
1154         case AST_AOC_MULT_ONEHUNDREDTH:
1155                 return "1/100";
1156         case AST_AOC_MULT_ONETENTH:
1157                 return "1/10";
1158         case AST_AOC_MULT_ONE:
1159                 return "1";
1160         case AST_AOC_MULT_TEN:
1161                 return "10";
1162         case AST_AOC_MULT_HUNDRED:
1163                 return "100";
1164         case AST_AOC_MULT_THOUSAND:
1165                 return "1000";
1166         case AST_AOC_MULT_NUM_ENTRIES:
1167                 break;
1168         }
1169         return "1";
1170 }
1171
1172 static const char *aoc_billingid_str(enum ast_aoc_billing_id billing_id)
1173 {
1174         switch (billing_id) {
1175         case AST_AOC_BILLING_NORMAL:
1176                 return "Normal";
1177         case AST_AOC_BILLING_REVERSE_CHARGE:
1178                 return "Reverse";
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:
1194                 break;
1195         }
1196         return "NotAvailable";
1197 }
1198
1199 int ast_aoc_test_encode_decode_match(struct ast_aoc_decoded *decoded)
1200 {
1201         struct ast_aoc_decoded *new_decoded = NULL;
1202         struct ast_aoc_encoded *encoded = NULL;
1203         size_t size;
1204         int res = 0;
1205
1206         if (!(encoded = ast_aoc_encode(decoded, &size, NULL))) {
1207                 return -1;
1208         }
1209
1210         if (!(new_decoded = ast_aoc_decode(encoded, size, NULL))) {
1211                 ast_free(encoded);
1212                 return -1;
1213         }
1214
1215         if (memcmp(new_decoded, decoded, sizeof(struct ast_aoc_decoded))) {
1216                 res = -1;
1217         }
1218
1219         ast_aoc_destroy_decoded(new_decoded);
1220         ast_aoc_destroy_encoded(encoded);
1221         return res;
1222 }
1223
1224 static char *aoc_cli_debug_enable(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1225 {
1226         switch (cmd) {
1227         case CLI_INIT:
1228                 e->command = "aoc set debug";
1229                 e->usage =
1230                         "Usage: 'aoc set debug on' to enable aoc debug, 'aoc set debug off' to disable debug.\n";
1231                 return NULL;
1232         case CLI_GENERATE:
1233                 return NULL;
1234         case CLI_HANDLER:
1235                 if (a->argc != 4) {
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;
1243                 } else {
1244                         return CLI_SHOWUSAGE;
1245                 }
1246         }
1247
1248         return CLI_SUCCESS;
1249 }
1250
1251 /*!
1252  * \internal
1253  * \brief Append the time structure to the event message string.
1254  * \since 1.8
1255  *
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.
1261  *
1262  * \return Nothing
1263  */
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)
1265 {
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));
1269 }
1270
1271 /*!
1272  * \internal
1273  * \brief Append the amount structure to the event message string.
1274  * \since 1.8
1275  *
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
1280  *
1281  * \return Nothing
1282  */
1283 static void aoc_amount_str(struct ast_str **msg, const char *prefix, unsigned int amount, enum ast_aoc_currency_multiplier mult)
1284 {
1285         static const char name[] = "Amount";
1286
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));
1290 }
1291
1292 static void aoc_request_event(const struct ast_aoc_decoded *decoded, struct ast_channel *chan, struct ast_str **msg)
1293 {
1294         if (chan) {
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));
1297         }
1298
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");
1303                 }
1304                 if (decoded->request_flag & AST_AOC_REQUEST_D) {
1305                         ast_str_append(msg, 0, "D");
1306                 }
1307                 if (decoded->request_flag & AST_AOC_REQUEST_E) {
1308                         ast_str_append(msg, 0, "E");
1309                 }
1310                 ast_str_append(msg, 0, "\r\n");
1311
1312         } else {
1313                 ast_str_append(msg, 0, "AOCRequest: NONE\r\n");
1314         }
1315 }
1316
1317 static void aoc_s_event(const struct ast_aoc_decoded *decoded, struct ast_channel *owner, struct ast_str **msg)
1318 {
1319         const char *rate_str;
1320         char prefix[32];
1321         int idx;
1322
1323         if (owner) {
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));
1326         }
1327
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);
1331
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) {
1335                         continue;
1336                 }
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);
1358                         }
1359                         break;
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);
1368                         break;
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));
1379                         break;
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);
1383                         break;
1384                 default:
1385                         break;
1386                 }
1387         }
1388 }
1389
1390 static void aoc_d_event(const struct ast_aoc_decoded *decoded, struct ast_channel *chan, struct ast_str **msg)
1391 {
1392         const char *charge_str;
1393         int idx;
1394         char prefix[32];
1395
1396         if (chan) {
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));
1399         }
1400
1401         charge_str = aoc_charge_type_str(decoded->charge_type);
1402         ast_str_append(msg, 0, "Type: %s\r\n", charge_str);
1403
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));
1411                 break;
1412         default:
1413                 break;
1414         }
1415
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);
1423                 break;
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);
1432                         }
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);
1436                         }
1437                 }
1438                 break;
1439         default:
1440                 break;
1441         }
1442 }
1443
1444 static void aoc_e_event(const struct ast_aoc_decoded *decoded, struct ast_channel *chan, struct ast_str **msg)
1445 {
1446         const char *charge_str;
1447         int idx;
1448         char prefix[32];
1449
1450         if (chan) {
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));
1453         }
1454
1455         charge_str = "ChargingAssociation";
1456
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);
1464                 break;
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);
1467                 break;
1468         case AST_AOC_CHARGING_ASSOCIATION_NA:
1469         default:
1470                 break;
1471         }
1472
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));
1480                 break;
1481         default:
1482                 break;
1483         }
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);
1491                 break;
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);
1500                         }
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);
1504                         }
1505                 }
1506                 break;
1507         default:
1508                 break;
1509         }
1510 }
1511
1512 int ast_aoc_manager_event(const struct ast_aoc_decoded *decoded, struct ast_channel *chan)
1513 {
1514         struct ast_str *msg;
1515
1516         if (!decoded || !(msg = ast_str_create(1024))) {
1517                 return -1;
1518         }
1519
1520         switch (decoded->msg_type) {
1521         case AST_AOC_S:
1522                 if (chan) {
1523                         aoc_s_event(decoded, chan, &msg);
1524                         ast_manager_event(chan, EVENT_FLAG_AOC, "AOC-S", "%s", ast_str_buffer(msg));
1525                 }
1526                 break;
1527         case AST_AOC_D:
1528                 if (chan) {
1529                         aoc_d_event(decoded, chan, &msg);
1530                         ast_manager_event(chan, EVENT_FLAG_AOC, "AOC-D", "%s", ast_str_buffer(msg));
1531                 }
1532                 break;
1533         case AST_AOC_E:
1534                 {
1535                         struct ast_channel *chans[1];
1536                         aoc_e_event(decoded, chan, &msg);
1537                         chans[0] = chan;
1538                         ast_manager_event_multichan(EVENT_FLAG_AOC, "AOC-E", chan ? 1 : 0, chans, "%s", ast_str_buffer(msg));
1539                 }
1540                 break;
1541         default:
1542                 /* events for AST_AOC_REQUEST are not generated here */
1543                 break;
1544         }
1545
1546         ast_free(msg);
1547         return 0;
1548 }
1549
1550 int ast_aoc_decoded2str(const struct ast_aoc_decoded *decoded, struct ast_str **msg)
1551 {
1552         if (!decoded || !msg) {
1553                 return -1;
1554         }
1555
1556         switch (decoded->msg_type) {
1557         case AST_AOC_S:
1558                 ast_str_append(msg, 0, "AOC-S\r\n");
1559                 aoc_s_event(decoded, NULL, msg);
1560                 break;
1561         case AST_AOC_D:
1562                 ast_str_append(msg, 0, "AOC-D\r\n");
1563                 aoc_d_event(decoded, NULL, msg);
1564                 break;
1565         case AST_AOC_E:
1566                 ast_str_append(msg, 0, "AOC-E\r\n");
1567                 aoc_e_event(decoded, NULL, msg);
1568                 break;
1569         case AST_AOC_REQUEST:
1570                 ast_str_append(msg, 0, "AOC-Request\r\n");
1571                 aoc_request_event(decoded, NULL, msg);
1572                 break;
1573         }
1574
1575         return 0;
1576 }
1577
1578 static void aoc_display_decoded_debug(const struct ast_aoc_decoded *decoded, int decoding, struct ast_channel *chan)
1579 {
1580         struct ast_str *msg;
1581
1582         if (!decoded || !(msg = ast_str_create(1024))) {
1583                 return;
1584         }
1585
1586         if (decoding) {
1587                 ast_str_append(&msg, 0, "---- DECODED AOC MSG ----\r\n");
1588         } else {
1589                 ast_str_append(&msg, 0, "---- ENCODED AOC MSG ----\r\n");
1590         }
1591         if (chan) {
1592                 ast_str_append(&msg, 0, "CHANNEL: %s\r\n", ast_channel_name(chan));
1593         }
1594
1595         if (ast_aoc_decoded2str(decoded, &msg)) {
1596                 ast_free(msg);
1597                 return;
1598         }
1599
1600         ast_verb(1, "%s\r\n", ast_str_buffer(msg));
1601         ast_free(msg);
1602 }
1603
1604 static struct ast_cli_entry aoc_cli[] = {
1605         AST_CLI_DEFINE(aoc_cli_debug_enable, "enable cli debugging of AOC messages"),
1606 };
1607
1608 static void aoc_shutdown(void)
1609 {
1610         ast_cli_unregister_multiple(aoc_cli, ARRAY_LEN(aoc_cli));
1611 }
1612 int ast_aoc_cli_init(void)
1613 {
1614         ast_register_atexit(aoc_shutdown);
1615         return ast_cli_register_multiple(aoc_cli, ARRAY_LEN(aoc_cli));
1616 }