Replace direct access to channel name with accessor functions
[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 #include "asterisk.h"
27 ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
28
29 #include "asterisk/aoc.h"
30 #include "asterisk/utils.h"
31 #include "asterisk/strings.h"
32 #include "asterisk/_private.h"
33 #include "asterisk/cli.h"
34 #include "asterisk/manager.h"
35
36 /* Encoded Payload Flags */
37 #define AST_AOC_ENCODED_TYPE_REQUEST    (0 << 0)
38 #define AST_AOC_ENCODED_TYPE_D          (1 << 0)
39 #define AST_AOC_ENCODED_TYPE_E          (2 << 0)
40 #define AST_AOC_ENCODED_TYPE_S          (3 << 0)
41
42 #define AST_AOC_ENCODED_REQUEST_S       (1 << 2)
43 #define AST_AOC_ENCODED_REQUEST_D       (1 << 3)
44 #define AST_AOC_ENCODED_REQUEST_E       (1 << 4)
45
46 #define AST_AOC_ENCODED_CHARGE_NA       (0 << 5)
47 #define AST_AOC_ENCODED_CHARGE_FREE     (1 << 5)
48 #define AST_AOC_ENCODED_CHARGE_CURRENCY (2 << 5)
49 #define AST_AOC_ENCODED_CHARGE_UNIT     (3 << 5)
50
51 #define AST_AOC_ENCODED_CHARGE_SUBTOTAL (1 << 7)
52 #define AST_AOC_ENCODED_CHARGE_TOTAL    (0 << 7)
53
54 #define AST_AOC_ENCODE_VERSION 1
55
56
57 static char aoc_debug_enabled = 0;
58 static void aoc_display_decoded_debug(const struct ast_aoc_decoded *decoded, int decoding, struct ast_channel *chan);
59 static int aoc_s_add_entry(struct ast_aoc_decoded *decoded, struct ast_aoc_s_entry *entry);
60
61 /* AOC Payload Header. Holds all the encoded AOC data to pass on the wire */
62 struct ast_aoc_encoded {
63         uint8_t  version;
64         uint8_t  flags;
65         uint16_t datalen;
66         unsigned char data[0];
67 };
68
69 /* Decoded AOC data */
70 struct ast_aoc_decoded {
71         enum ast_aoc_type msg_type;
72         enum ast_aoc_charge_type charge_type;
73         enum ast_aoc_request request_flag;
74         enum ast_aoc_total_type total_type;
75
76         /* currency information */
77         enum ast_aoc_currency_multiplier multiplier;
78         unsigned int currency_amount;
79         char currency_name[AOC_CURRENCY_NAME_SIZE];
80
81         /* unit information */
82         int unit_count;
83         struct ast_aoc_unit_entry unit_list[32];
84
85         /* Billing Id */
86         enum ast_aoc_billing_id billing_id;
87
88         /* Charging Association information */
89         struct ast_aoc_charging_association charging_association;
90
91         /* AOC-S charge information */
92         int aoc_s_count;
93         struct ast_aoc_s_entry aoc_s_entries[10];
94
95         /* Is this an AOC Termination Request */
96         char termination_request;
97 };
98
99 /*! \brief AOC Payload Information Elements */
100 enum AOC_IE {
101         AOC_IE_CURRENCY = 1,
102         AOC_IE_UNIT = 2,
103         AOC_IE_BILLING = 3,
104         AOC_IE_CHARGING_ASSOCIATION = 4,
105         AOC_IE_RATE = 5,
106         AOC_IE_TERMINATION_REQUEST = 6,
107 };
108
109 /*! \brief AOC IE payload header */
110 struct aoc_pl_ie_hdr {
111         uint8_t ie_id;
112         uint8_t datalen;
113         char data[0];
114 } __attribute__((packed));
115
116 struct aoc_ie_currency {
117         uint32_t amount;
118         uint8_t  multiplier;
119         char name[AOC_CURRENCY_NAME_SIZE];
120 } __attribute__((packed));
121
122 struct aoc_ie_unit {
123         uint32_t amount;
124         uint8_t valid_type;
125         uint8_t valid_amount;
126         uint8_t type;
127 } __attribute__((packed));
128
129 struct aoc_ie_billing {
130         uint8_t id;
131 } __attribute__((packed));
132
133 struct aoc_ie_charging_association {
134         struct ast_aoc_charging_association ca;
135 } __attribute__((packed));
136
137 struct aoc_ie_charging_rate {
138         struct ast_aoc_s_entry entry;
139 } __attribute__((packed));
140
141 struct ast_aoc_decoded *ast_aoc_create(const enum ast_aoc_type msg_type,
142                 const enum ast_aoc_charge_type charge_type,
143                 const enum ast_aoc_request requests)
144 {
145         struct ast_aoc_decoded *decoded = NULL;
146
147         /* verify input */
148         if (((unsigned int) charge_type > AST_AOC_CHARGE_UNIT) ||
149                 ((unsigned int) msg_type > AST_AOC_E) ||
150                 ((msg_type == AST_AOC_REQUEST) && !requests)) {
151
152                 ast_log(LOG_WARNING, "Failed to create ast_aoc_decoded object, invalid input\n");
153                 return NULL;
154         }
155
156         if (!(decoded = ast_calloc(1, sizeof(struct ast_aoc_decoded)))) {
157                 ast_log(LOG_WARNING, "Failed to create ast_aoc_decoded object \n");
158                 return NULL;
159         }
160
161         decoded->msg_type = msg_type;
162
163         if (msg_type == AST_AOC_REQUEST) {
164                 decoded->request_flag = requests;
165         } else if ((msg_type == AST_AOC_D) || (msg_type == AST_AOC_E)) {
166                 decoded->charge_type = charge_type;
167         }
168
169         return decoded;
170 }
171
172 void *ast_aoc_destroy_decoded(struct ast_aoc_decoded *decoded)
173 {
174         ast_free(decoded);
175         return NULL;
176 }
177
178 void *ast_aoc_destroy_encoded(struct ast_aoc_encoded *encoded)
179 {
180         ast_free(encoded);
181         return NULL;
182 }
183
184 static void aoc_parse_ie_charging_rate(struct ast_aoc_decoded *decoded, const struct aoc_ie_charging_rate *ie)
185 {
186         struct ast_aoc_s_entry entry = { 0, };
187
188         entry.charged_item = ntohs(ie->entry.charged_item);
189         entry.rate_type = ntohs(ie->entry.rate_type);
190
191         switch (entry.rate_type) {
192         case AST_AOC_RATE_TYPE_DURATION:
193                 entry.rate.duration.multiplier = ntohs(ie->entry.rate.duration.multiplier);
194                 entry.rate.duration.amount = ntohl(ie->entry.rate.duration.amount);
195                 entry.rate.duration.time = ntohl(ie->entry.rate.duration.time);
196                 entry.rate.duration.time_scale = ntohs(ie->entry.rate.duration.time_scale);
197                 entry.rate.duration.granularity_time = ntohl(ie->entry.rate.duration.granularity_time);
198                 entry.rate.duration.granularity_time_scale = ntohs(ie->entry.rate.duration.granularity_time_scale);
199                 entry.rate.duration.charging_type = ie->entry.rate.duration.charging_type; /* only one byte */
200
201                 if (!ast_strlen_zero(ie->entry.rate.duration.currency_name)) {
202                         ast_copy_string(entry.rate.duration.currency_name,
203                                 ie->entry.rate.duration.currency_name,
204                                 sizeof(entry.rate.duration.currency_name));
205                 }
206                 break;
207         case AST_AOC_RATE_TYPE_FLAT:
208                 entry.rate.flat.multiplier = ntohs(ie->entry.rate.flat.multiplier);
209                 entry.rate.flat.amount = ntohl(ie->entry.rate.flat.amount);
210                 if (!ast_strlen_zero(ie->entry.rate.flat.currency_name)) {
211                         ast_copy_string(entry.rate.flat.currency_name,
212                                 ie->entry.rate.flat.currency_name,
213                                 sizeof(entry.rate.flat.currency_name));
214                 }
215                 break;
216         case AST_AOC_RATE_TYPE_VOLUME:
217                 entry.rate.volume.multiplier = ntohs(ie->entry.rate.volume.multiplier);
218                 entry.rate.volume.amount = ntohl(ie->entry.rate.volume.amount);
219                 entry.rate.volume.volume_unit = ntohs(ie->entry.rate.volume.volume_unit);
220                 if (!ast_strlen_zero(ie->entry.rate.volume.currency_name)) {
221                         ast_copy_string(entry.rate.volume.currency_name,
222                                 ie->entry.rate.volume.currency_name,
223                                 sizeof(entry.rate.volume.currency_name));
224                 }
225                 break;
226         case AST_AOC_RATE_TYPE_SPECIAL_CODE:
227                 entry.rate.special_code = ntohs(ie->entry.rate.special_code);
228                 break;
229         }
230
231         aoc_s_add_entry(decoded, &entry);
232 }
233
234 static int aoc_parse_ie(struct ast_aoc_decoded *decoded, unsigned char *data, unsigned int datalen)
235 {
236         enum AOC_IE ie_id;
237         unsigned int len;
238
239         while (datalen >= 2) {
240                 ie_id = data[0];
241                 len = data[1];
242                 if (len > datalen -2) {
243                         ast_log(LOG_ERROR, "AOC information element length exceeds the total message size\n");
244                         return -1;
245                 }
246
247                 switch(ie_id) {
248                 case AOC_IE_CURRENCY:
249                         if (len == sizeof(struct aoc_ie_currency)) {
250                                 struct aoc_ie_currency ie;
251                                 memcpy(&ie, data + 2, len);
252                                 decoded->currency_amount = ntohl(ie.amount);
253                                 decoded->multiplier = ie.multiplier; /* only one byte */
254                                 memcpy(decoded->currency_name, ie.name, sizeof(decoded->currency_name));
255                         } else {
256                                 ast_log(LOG_WARNING, "Received invalid currency ie\n");
257                         }
258                         break;
259                 case AOC_IE_UNIT:
260                         if (len == sizeof(struct aoc_ie_unit)) {
261                                 struct aoc_ie_unit ie;
262                                 memcpy(&ie, data + 2, len);
263                                 ast_aoc_add_unit_entry(decoded, ie.valid_amount, ntohl(ie.amount), ie.valid_type, ie.type);
264                         } else {
265                                 ast_log(LOG_WARNING, "Received invalid unit ie\n");
266                         }
267                         break;
268                 case AOC_IE_BILLING:
269                         if (len == sizeof(struct aoc_ie_billing)) {
270                                 struct aoc_ie_billing ie;
271                                 memcpy(&ie, data + 2, len);
272                                 decoded->billing_id = ie.id; /* only one byte */
273                         } else {
274                                 ast_log(LOG_WARNING, "Received invalid billing ie\n");
275                         }
276                         break;
277                 case AOC_IE_CHARGING_ASSOCIATION:
278                         if (len == sizeof(struct aoc_ie_charging_association)) {
279                                 memcpy(&decoded->charging_association, data + 2, sizeof(decoded->charging_association));
280                                 /* everything in the charging_association struct is a single byte except for the id */
281                                 if (decoded->charging_association.charging_type == AST_AOC_CHARGING_ASSOCIATION_ID) {
282                                         decoded->charging_association.charge.id = ntohl(decoded->charging_association.charge.id);
283                                 }
284                         } else {
285                                 ast_log(LOG_WARNING, "Received invalid charging association ie\n");
286                         }
287                         break;
288                 case AOC_IE_RATE:
289                         if (len == sizeof(struct aoc_ie_charging_rate)) {
290                                 struct aoc_ie_charging_rate ie;
291                                 memcpy(&ie, data + 2, len);
292                                 aoc_parse_ie_charging_rate(decoded, &ie);
293                         } else {
294                                 ast_log(LOG_WARNING, "Received invalid charging rate ie\n");
295                         }
296                         break;
297                 case AOC_IE_TERMINATION_REQUEST:
298                         if (len == 0) {
299                                 decoded->termination_request = 1;
300                         } else {
301                                 ast_log(LOG_WARNING, "Received invalid termination request ie\n");
302                         }
303                         break;
304                 default:
305                         ast_log(LOG_WARNING, "Unknown AOC Information Element, ignoring.\n");
306                 }
307
308                 datalen -= (len + 2);
309                 data += (len + 2);
310         }
311         return 0;
312 }
313
314 struct ast_aoc_decoded *ast_aoc_decode(struct ast_aoc_encoded *encoded, size_t size, struct ast_channel *chan)
315 {
316         struct ast_aoc_decoded *decoded;
317
318         /* verify our encoded payload is actually large enough to hold all the ies */
319         if ((size - (sizeof(struct ast_aoc_encoded)) != ntohs(encoded->datalen))) {
320                 ast_log(LOG_WARNING, "Corrupted aoc encoded object, can not decode\n");
321                 return NULL;
322         }
323
324         if (!(decoded = ast_calloc(1, sizeof(struct ast_aoc_decoded)))) {
325                 ast_log(LOG_WARNING, "Failed to create ast_aoc_decoded object \n");
326                 return NULL;
327         }
328
329         /* decode flags */
330
331         if ((encoded->flags & AST_AOC_ENCODED_TYPE_S) == AST_AOC_ENCODED_TYPE_S) {
332                 decoded->msg_type = AST_AOC_S;
333         } else if (encoded->flags & AST_AOC_ENCODED_TYPE_E) {
334                 decoded->msg_type = AST_AOC_E;
335         } else if (encoded->flags & AST_AOC_ENCODED_TYPE_D) {
336                 decoded->msg_type = AST_AOC_D;
337         } else {
338                 decoded->msg_type = AST_AOC_REQUEST;
339         }
340
341         if (decoded->msg_type == AST_AOC_REQUEST) {
342                 if (encoded->flags & AST_AOC_ENCODED_REQUEST_S) {
343                         decoded->request_flag |= AST_AOC_REQUEST_S;
344                 }
345                 if (encoded->flags & AST_AOC_ENCODED_REQUEST_D) {
346                         decoded->request_flag |= AST_AOC_REQUEST_D;
347                 }
348                 if (encoded->flags & AST_AOC_ENCODED_REQUEST_E) {
349                         decoded->request_flag |= AST_AOC_REQUEST_E;
350                 }
351         } else if ((decoded->msg_type == AST_AOC_D) || (decoded->msg_type == AST_AOC_E)) {
352                 if ((encoded->flags & AST_AOC_ENCODED_CHARGE_UNIT) == AST_AOC_ENCODED_CHARGE_UNIT) {
353                         decoded->charge_type = AST_AOC_CHARGE_UNIT;
354                 } else if ((encoded->flags & AST_AOC_ENCODED_CHARGE_CURRENCY) == AST_AOC_ENCODED_CHARGE_CURRENCY) {
355                         decoded->charge_type = AST_AOC_CHARGE_CURRENCY;
356                 } else if ((encoded->flags & AST_AOC_ENCODED_CHARGE_FREE) == AST_AOC_ENCODED_CHARGE_FREE) {
357                         decoded->charge_type = AST_AOC_CHARGE_FREE;
358                 } else {
359                         decoded->charge_type = AST_AOC_CHARGE_NA;
360                 }
361
362                 if (encoded->flags & AST_AOC_ENCODED_CHARGE_SUBTOTAL) {
363                         decoded->total_type = AST_AOC_SUBTOTAL;
364                 }
365         }
366
367         /* decode information elements */
368         aoc_parse_ie(decoded, encoded->data, ntohs(encoded->datalen));
369
370         if (aoc_debug_enabled) {
371                 aoc_display_decoded_debug(decoded, 1, chan);
372         }
373
374         return decoded;
375 }
376
377 struct aoc_ie_data {
378         unsigned char buf[1024];
379         int pos;
380 };
381
382 /*!
383  * \internal
384  * \brief append an AOC information element
385  * \note data is expected to already be in network byte order at this point
386  */
387 static int aoc_append_ie(struct aoc_ie_data *ied, unsigned short ie_id, const void *data, unsigned short datalen)
388 {
389         if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
390                 ast_log(LOG_WARNING, "Failure to append AOC information element, out of space \n");
391                 return -1;
392         }
393         ied->buf[ied->pos++] = ie_id;
394         ied->buf[ied->pos++] = datalen;
395         if (datalen) {
396                 memcpy(ied->buf + ied->pos, data, datalen);
397                 ied->pos += datalen;
398         }
399         return 0;
400 }
401
402 static void aoc_create_ie_data_charging_rate(const struct ast_aoc_s_entry *entry, struct aoc_ie_charging_rate *ie)
403 {
404         ie->entry.charged_item = htons(entry->charged_item);
405         ie->entry.rate_type = htons(entry->rate_type);
406
407         switch (entry->rate_type) {
408         case AST_AOC_RATE_TYPE_DURATION:
409                 ie->entry.rate.duration.multiplier = htons(entry->rate.duration.multiplier);
410                 ie->entry.rate.duration.amount = htonl(entry->rate.duration.amount);
411                 ie->entry.rate.duration.time = htonl(entry->rate.duration.time);
412                 ie->entry.rate.duration.time_scale = htons(entry->rate.duration.time_scale);
413                 ie->entry.rate.duration.granularity_time = htonl(entry->rate.duration.granularity_time);
414                 ie->entry.rate.duration.granularity_time_scale = htons(entry->rate.duration.granularity_time_scale);
415                 ie->entry.rate.duration.charging_type = entry->rate.duration.charging_type; /* only one byte */
416
417                 if (!ast_strlen_zero(entry->rate.duration.currency_name)) {
418                         ast_copy_string(ie->entry.rate.duration.currency_name,
419                                 entry->rate.duration.currency_name,
420                                 sizeof(ie->entry.rate.duration.currency_name));
421                 }
422                 break;
423         case AST_AOC_RATE_TYPE_FLAT:
424                 ie->entry.rate.flat.multiplier = htons(entry->rate.flat.multiplier);
425                 ie->entry.rate.flat.amount = htonl(entry->rate.flat.amount);
426                 if (!ast_strlen_zero(entry->rate.flat.currency_name)) {
427                         ast_copy_string(ie->entry.rate.flat.currency_name,
428                                 entry->rate.flat.currency_name,
429                                 sizeof(ie->entry.rate.flat.currency_name));
430                 }
431                 break;
432         case AST_AOC_RATE_TYPE_VOLUME:
433                 ie->entry.rate.volume.multiplier = htons(entry->rate.volume.multiplier);
434                 ie->entry.rate.volume.amount = htonl(entry->rate.volume.amount);
435                 ie->entry.rate.volume.volume_unit = htons(entry->rate.volume.volume_unit);
436                 if (!ast_strlen_zero(entry->rate.volume.currency_name)) {
437                         ast_copy_string(ie->entry.rate.volume.currency_name,
438                                 entry->rate.volume.currency_name,
439                                 sizeof(ie->entry.rate.volume.currency_name));
440                 }
441                 break;
442         case AST_AOC_RATE_TYPE_SPECIAL_CODE:
443                 ie->entry.rate.special_code = htons(entry->rate.special_code);
444                 break;
445         }
446
447 }
448 static void aoc_create_ie_data(struct ast_aoc_decoded *decoded, struct aoc_ie_data *ied)
449 {
450         ied->pos = 0;
451
452         if (decoded->currency_amount) {
453                 struct aoc_ie_currency ie = {
454                         .amount = htonl(decoded->currency_amount),
455                         .multiplier = decoded->multiplier, /* only one byte */
456                         .name = { 0, },
457                 };
458
459                 if (!ast_strlen_zero(decoded->currency_name)) {
460                         ast_copy_string(ie.name, decoded->currency_name, sizeof(ie.name));
461                 }
462
463                 aoc_append_ie(ied, AOC_IE_CURRENCY, (const void *) &ie, sizeof(ie));
464         }
465
466         if (decoded->unit_count) {
467                 struct aoc_ie_unit ie = { 0 };
468                 int i;
469
470                 for (i = 0; i < decoded->unit_count; i++) {
471                         ie.valid_amount = decoded->unit_list[i].valid_amount; /* only one byte */
472                         ie.amount = htonl(decoded->unit_list[i].amount);
473                         ie.valid_type = decoded->unit_list[i].valid_type; /* only one byte */
474                         ie.type = decoded->unit_list[i].type; /* only one byte */
475                         aoc_append_ie(ied, AOC_IE_UNIT, (const void *) &ie, sizeof(ie));
476                 }
477         }
478
479         if (decoded->billing_id) {
480                 struct aoc_ie_billing ie;
481                 ie.id = decoded->billing_id; /* only one byte */
482                 aoc_append_ie(ied, AOC_IE_BILLING, (const void *) &ie, sizeof(ie));
483         }
484
485         if (decoded->charging_association.charging_type != AST_AOC_CHARGING_ASSOCIATION_NA) {
486                 struct aoc_ie_charging_association ie;
487                 memset(&ie, 0, sizeof(ie));
488                 ie.ca.charging_type = decoded->charging_association.charging_type;   /* only one byte */
489                 if (decoded->charging_association.charging_type == AST_AOC_CHARGING_ASSOCIATION_NUMBER) {
490                         ie.ca.charge.number.plan = decoded->charging_association.charge.number.plan; /* only one byte */
491                         ast_copy_string(ie.ca.charge.number.number,
492                                 decoded->charging_association.charge.number.number,
493                                 sizeof(ie.ca.charge.number.number));
494                 } else if (decoded->charging_association.charging_type == AST_AOC_CHARGING_ASSOCIATION_ID) {
495                         ie.ca.charge.id = htonl(decoded->charging_association.charge.id);
496                 }
497                 aoc_append_ie(ied, AOC_IE_CHARGING_ASSOCIATION, (const void *) &ie, sizeof(ie));
498         }
499
500         if (decoded->aoc_s_count) {
501                 struct aoc_ie_charging_rate ie;
502                 int i;
503                 for (i = 0; i < decoded->aoc_s_count; i++) {
504                         memset(&ie, 0, sizeof(ie));
505                         aoc_create_ie_data_charging_rate(&decoded->aoc_s_entries[i], &ie);
506                         aoc_append_ie(ied, AOC_IE_RATE, (const void *) &ie, sizeof(ie));
507                 }
508         }
509
510         if (decoded->termination_request) {
511                 aoc_append_ie(ied, AOC_IE_TERMINATION_REQUEST, NULL, 0);
512         }
513 }
514
515 struct ast_aoc_encoded *ast_aoc_encode(struct ast_aoc_decoded *decoded, size_t *out_size, struct ast_channel *chan)
516 {
517         struct aoc_ie_data ied;
518         struct ast_aoc_encoded *encoded = NULL;
519         size_t size = 0;
520
521         if (!decoded || !out_size) {
522                 return NULL;
523         }
524
525         *out_size = 0;
526
527         /* create information element buffer before allocating the payload,
528          * by doing this the exact size of the payload + the id data can be
529          * allocated all at once. */
530         aoc_create_ie_data(decoded, &ied);
531
532         size = sizeof(struct ast_aoc_encoded) + ied.pos;
533
534         if (!(encoded = ast_calloc(1, size))) {
535                 ast_log(LOG_WARNING, "Failed to create ast_aoc_encoded object during decode routine. \n");
536                 return NULL;
537         }
538
539         /* -- Set ie data buffer */
540         if (ied.pos) {
541                 /* this is safe because encoded was allocated to fit this perfectly */
542                 memcpy(encoded->data, ied.buf, ied.pos);
543                 encoded->datalen = htons(ied.pos);
544         }
545
546         /* --- Set Flags --- */
547         switch (decoded->msg_type) {
548         case AST_AOC_S:
549                 encoded->flags = AST_AOC_ENCODED_TYPE_S;
550                 break;
551         case AST_AOC_D:
552                 encoded->flags = AST_AOC_ENCODED_TYPE_D;
553                 break;
554         case AST_AOC_E:
555                 encoded->flags = AST_AOC_ENCODED_TYPE_E;
556                 break;
557         case AST_AOC_REQUEST:
558                 encoded->flags = AST_AOC_ENCODED_TYPE_REQUEST;
559         default:
560                 break;
561         }
562
563         /* if it is type request, set the types requested, else set charge type */
564         if (decoded->msg_type == AST_AOC_REQUEST) {
565                 if (decoded->request_flag & AST_AOC_REQUEST_S) {
566                         encoded->flags |= AST_AOC_ENCODED_REQUEST_S;
567                 }
568                 if (decoded->request_flag & AST_AOC_REQUEST_D) {
569                         encoded->flags |= AST_AOC_ENCODED_REQUEST_D;
570                 }
571                 if (decoded->request_flag & AST_AOC_REQUEST_E) {
572                         encoded->flags |= AST_AOC_ENCODED_REQUEST_E;
573                 }
574         } else if ((decoded->msg_type == AST_AOC_D) || (decoded->msg_type == AST_AOC_E)) {
575                 switch (decoded->charge_type) {
576                 case AST_AOC_CHARGE_UNIT:
577                         encoded->flags |= AST_AOC_ENCODED_CHARGE_UNIT;
578                         break;
579                 case AST_AOC_CHARGE_CURRENCY:
580                         encoded->flags |= AST_AOC_ENCODED_CHARGE_CURRENCY;
581                         break;
582                 case AST_AOC_CHARGE_FREE:
583                         encoded->flags |= AST_AOC_ENCODED_CHARGE_FREE;
584                 case AST_AOC_CHARGE_NA:
585                 default:
586                         encoded->flags |= AST_AOC_ENCODED_CHARGE_NA;
587                         break;
588                 }
589
590                 if (decoded->total_type == AST_AOC_SUBTOTAL) {
591                         encoded->flags |= AST_AOC_ENCODED_CHARGE_SUBTOTAL;
592                 }
593         }
594
595         /* --- Set Version Number --- */
596         encoded->version = AST_AOC_ENCODE_VERSION;
597
598         /* set the output size  */
599         *out_size = size;
600
601         if (aoc_debug_enabled) {
602                 aoc_display_decoded_debug(decoded, 0, chan);
603         }
604
605         return encoded;
606 }
607
608 static int aoc_s_add_entry(struct ast_aoc_decoded *decoded, struct ast_aoc_s_entry *entry)
609 {
610         if (decoded->aoc_s_count >= ARRAY_LEN(decoded->aoc_s_entries)) {
611                 return -1;
612         }
613
614         decoded->aoc_s_entries[decoded->aoc_s_count] = *entry;
615         decoded->aoc_s_count++;
616
617         return 0;
618 }
619
620
621 unsigned int ast_aoc_s_get_count(struct ast_aoc_decoded *decoded)
622 {
623         return decoded->aoc_s_count;
624 }
625
626 const struct ast_aoc_s_entry *ast_aoc_s_get_rate_info(struct ast_aoc_decoded *decoded, unsigned int entry_number)
627 {
628         if (entry_number >= decoded->aoc_s_count) {
629                 return NULL;
630         }
631
632         return (const struct ast_aoc_s_entry *) &decoded->aoc_s_entries[entry_number];
633 }
634
635 int ast_aoc_s_add_rate_duration(struct ast_aoc_decoded *decoded,
636         enum ast_aoc_s_charged_item charged_item,
637         unsigned int amount,
638         enum ast_aoc_currency_multiplier multiplier,
639         const char *currency_name,
640         unsigned long time,
641         enum ast_aoc_time_scale time_scale,
642         unsigned long granularity_time,
643         enum ast_aoc_time_scale granularity_time_scale,
644         int step_function)
645 {
646
647         struct ast_aoc_s_entry entry = { 0, };
648
649         entry.charged_item = charged_item;
650         entry.rate_type = AST_AOC_RATE_TYPE_DURATION;
651         entry.rate.duration.amount = amount;
652         entry.rate.duration.multiplier = multiplier;
653         entry.rate.duration.time = time;
654         entry.rate.duration.time_scale = time_scale;
655         entry.rate.duration.granularity_time = granularity_time;
656         entry.rate.duration.granularity_time_scale = granularity_time_scale;
657         entry.rate.duration.charging_type = step_function ? 1 : 0;
658
659         if (!ast_strlen_zero(currency_name)) {
660                 ast_copy_string(entry.rate.duration.currency_name, currency_name, sizeof(entry.rate.duration.currency_name));
661         }
662
663         return aoc_s_add_entry(decoded, &entry);
664 }
665
666 int ast_aoc_s_add_rate_flat(struct ast_aoc_decoded *decoded,
667         enum ast_aoc_s_charged_item charged_item,
668         unsigned int amount,
669         enum ast_aoc_currency_multiplier multiplier,
670         const char *currency_name)
671 {
672         struct ast_aoc_s_entry entry = { 0, };
673
674         entry.charged_item = charged_item;
675         entry.rate_type = AST_AOC_RATE_TYPE_FLAT;
676         entry.rate.flat.amount = amount;
677         entry.rate.flat.multiplier = multiplier;
678
679         if (!ast_strlen_zero(currency_name)) {
680                 ast_copy_string(entry.rate.flat.currency_name, currency_name, sizeof(entry.rate.flat.currency_name));
681         }
682
683         return aoc_s_add_entry(decoded, &entry);
684 }
685
686
687 int ast_aoc_s_add_rate_volume(struct ast_aoc_decoded *decoded,
688         enum ast_aoc_s_charged_item charged_item,
689         enum ast_aoc_volume_unit volume_unit,
690         unsigned int amount,
691         enum ast_aoc_currency_multiplier multiplier,
692         const char *currency_name)
693 {
694         struct ast_aoc_s_entry entry = { 0, };
695
696         entry.charged_item = charged_item;
697         entry.rate_type = AST_AOC_RATE_TYPE_VOLUME;
698         entry.rate.volume.multiplier = multiplier;
699         entry.rate.volume.amount = amount;
700         entry.rate.volume.volume_unit = volume_unit;
701
702         if (!ast_strlen_zero(currency_name)) {
703                 ast_copy_string(entry.rate.volume.currency_name, currency_name, sizeof(entry.rate.volume.currency_name));
704         }
705
706         return aoc_s_add_entry(decoded, &entry);
707 }
708
709 int ast_aoc_s_add_rate_special_charge_code(struct ast_aoc_decoded *decoded,
710         enum ast_aoc_s_charged_item charged_item,
711         unsigned int code)
712 {
713         struct ast_aoc_s_entry entry = { 0, };
714
715         entry.charged_item = charged_item;
716         entry.rate_type = AST_AOC_RATE_TYPE_SPECIAL_CODE;
717         entry.rate.special_code = code;
718
719         return aoc_s_add_entry(decoded, &entry);
720 }
721
722 int ast_aoc_s_add_rate_free(struct ast_aoc_decoded *decoded,
723         enum ast_aoc_s_charged_item charged_item,
724         int from_beginning)
725 {
726         struct ast_aoc_s_entry entry = { 0, };
727
728         entry.charged_item = charged_item;
729         entry.rate_type = from_beginning ? AST_AOC_RATE_TYPE_FREE_FROM_BEGINNING : AST_AOC_RATE_TYPE_FREE;
730
731         return aoc_s_add_entry(decoded, &entry);
732 }
733
734 int ast_aoc_s_add_rate_na(struct ast_aoc_decoded *decoded,
735         enum ast_aoc_s_charged_item charged_item)
736 {
737         struct ast_aoc_s_entry entry = { 0, };
738
739         entry.charged_item = charged_item;
740         entry.rate_type = AST_AOC_RATE_TYPE_NA;
741
742         return aoc_s_add_entry(decoded, &entry);
743 }
744
745 int ast_aoc_s_add_special_arrangement(struct ast_aoc_decoded *decoded,
746         unsigned int code)
747 {
748         struct ast_aoc_s_entry entry = { 0, };
749
750         entry.charged_item = AST_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT;
751         entry.rate_type = AST_AOC_RATE_TYPE_SPECIAL_CODE;
752         entry.rate.special_code = code;
753
754         return aoc_s_add_entry(decoded, &entry);
755 }
756
757 enum ast_aoc_type ast_aoc_get_msg_type(struct ast_aoc_decoded *decoded)
758 {
759         return decoded->msg_type;
760 }
761
762 enum ast_aoc_charge_type ast_aoc_get_charge_type(struct ast_aoc_decoded *decoded)
763 {
764         return decoded->charge_type;
765 }
766
767 enum ast_aoc_request ast_aoc_get_request(struct ast_aoc_decoded *decoded)
768 {
769         return decoded->request_flag;
770 }
771
772 int ast_aoc_set_total_type(struct ast_aoc_decoded *decoded,
773         const enum ast_aoc_total_type type)
774 {
775         decoded->total_type = type;
776         return 0;
777 }
778
779 enum ast_aoc_total_type ast_aoc_get_total_type(struct ast_aoc_decoded *decoded)
780 {
781         return decoded->total_type;
782 }
783
784 int ast_aoc_set_currency_info(struct ast_aoc_decoded *decoded,
785                 const unsigned int amount,
786                 const enum ast_aoc_currency_multiplier multiplier,
787                 const char *name)
788 {
789
790         if (!ast_strlen_zero(name)) {
791                 ast_copy_string(decoded->currency_name, name, sizeof(decoded->currency_name));
792         }
793
794         decoded->currency_amount = amount;
795
796         if (multiplier && (multiplier < AST_AOC_MULT_NUM_ENTRIES)) {
797                 decoded->multiplier = multiplier;
798         } else {
799                 decoded->multiplier = AST_AOC_MULT_ONE;
800         }
801
802         return 0;
803 }
804
805 unsigned int ast_aoc_get_currency_amount(struct ast_aoc_decoded *decoded)
806 {
807         return decoded->currency_amount;
808 }
809
810 enum ast_aoc_currency_multiplier ast_aoc_get_currency_multiplier(struct ast_aoc_decoded *decoded)
811 {
812         return decoded->multiplier;
813 }
814
815 const char *ast_aoc_get_currency_multiplier_decimal(struct ast_aoc_decoded *decoded)
816 {
817         switch (decoded->multiplier) {
818         case AST_AOC_MULT_ONETHOUSANDTH:
819                 return "0.001";
820         case AST_AOC_MULT_ONEHUNDREDTH:
821                 return "0.01";
822         case AST_AOC_MULT_ONETENTH:
823                 return "0.1";
824         case AST_AOC_MULT_ONE:
825                 return "1.0";
826         case AST_AOC_MULT_TEN:
827                 return "10.0";
828         case AST_AOC_MULT_HUNDRED:
829                 return "100.0";
830         case AST_AOC_MULT_THOUSAND:
831                 return "1000.0";
832         default:
833                 return "1.0";
834         }
835 }
836
837 const char *ast_aoc_get_currency_name(struct ast_aoc_decoded *decoded)
838 {
839         return decoded->currency_name;
840 }
841
842 int ast_aoc_add_unit_entry(struct ast_aoc_decoded *decoded,
843                 const unsigned int amount_is_present,
844                 const unsigned int amount,
845                 const unsigned int type_is_present,
846                 const unsigned int type)
847 {
848         if ((decoded->msg_type == AST_AOC_REQUEST) ||
849                 (decoded->unit_count >= ARRAY_LEN(decoded->unit_list))) {
850                 return -1;
851         }
852
853         if (!amount_is_present && !type_is_present) {
854                 return -1;
855         }
856
857         decoded->unit_list[decoded->unit_count].valid_amount = amount_is_present;
858         if (amount_is_present) {
859                 decoded->unit_list[decoded->unit_count].amount = amount;
860         } else {
861                 decoded->unit_list[decoded->unit_count].amount = 0;
862         }
863
864         decoded->unit_list[decoded->unit_count].valid_type = type_is_present;
865         if (type_is_present) {
866                 decoded->unit_list[decoded->unit_count].type = type;
867         } else {
868                 decoded->unit_list[decoded->unit_count].type = 0;
869         }
870         decoded->unit_count++;
871
872         return 0;
873 }
874
875 const struct ast_aoc_unit_entry *ast_aoc_get_unit_info(struct ast_aoc_decoded *decoded, unsigned int entry_number)
876 {
877         if (entry_number >= decoded->unit_count) {
878                 return NULL;
879         }
880
881         return (const struct ast_aoc_unit_entry *) &decoded->unit_list[entry_number];
882 }
883
884 unsigned int ast_aoc_get_unit_count(struct ast_aoc_decoded *decoded)
885 {
886         return decoded->unit_count;
887 }
888
889 int ast_aoc_set_billing_id(struct ast_aoc_decoded *decoded, const enum ast_aoc_billing_id id)
890 {
891         if ((id >= AST_AOC_BILLING_NUM_ENTRIES) || (id < AST_AOC_BILLING_NA)) {
892                 return -1;
893         }
894
895         decoded->billing_id = id;
896
897         return 0;
898 }
899
900 enum ast_aoc_billing_id ast_aoc_get_billing_id(struct ast_aoc_decoded *decoded)
901 {
902         return decoded->billing_id;
903 }
904
905 int ast_aoc_set_association_id(struct ast_aoc_decoded *decoded, const int id)
906 {
907         if (decoded->msg_type != AST_AOC_E) {
908                 return -1;
909         }
910         memset(&decoded->charging_association, 0, sizeof(decoded->charging_association));
911         decoded->charging_association.charging_type = AST_AOC_CHARGING_ASSOCIATION_ID;
912         decoded->charging_association.charge.id = id;
913         return 0;
914 }
915
916 const struct ast_aoc_charging_association *ast_aoc_get_association_info(struct ast_aoc_decoded *decoded)
917 {
918         return &decoded->charging_association;
919 }
920
921 int ast_aoc_set_association_number(struct ast_aoc_decoded *decoded, const char *num, uint8_t plan)
922 {
923         if ((decoded->msg_type != AST_AOC_E) || ast_strlen_zero(num)) {
924                 return -1;
925         }
926         memset(&decoded->charging_association, 0, sizeof(decoded->charging_association));
927         decoded->charging_association.charging_type = AST_AOC_CHARGING_ASSOCIATION_NUMBER;
928         decoded->charging_association.charge.number.plan = plan;
929         ast_copy_string(decoded->charging_association.charge.number.number, num, sizeof(decoded->charging_association.charge.number.number));
930
931         return 0;
932 }
933
934 int ast_aoc_set_termination_request(struct ast_aoc_decoded *decoded)
935 {
936         if (decoded->msg_type != AST_AOC_REQUEST) {
937                 return -1;
938         }
939         decoded->termination_request = 1;
940
941         return 0;
942 }
943
944 int ast_aoc_get_termination_request(struct ast_aoc_decoded *decoded)
945 {
946         return decoded->termination_request;
947 }
948
949 /*!
950  * \internal
951  * \brief Convert AST_AOC_VOLUME_UNIT to string.
952  * \since 1.8
953  *
954  * \param value Value to convert to string.
955  *
956  * \return String equivalent.
957  */
958 static const char *aoc_volume_unit_str(enum ast_aoc_volume_unit value)
959 {
960         const char *str;
961
962         switch (value) {
963         default:
964         case AST_AOC_VOLUME_UNIT_OCTET:
965                 str = "Octet";
966                 break;
967         case AST_AOC_VOLUME_UNIT_SEGMENT:
968                 str = "Segment";
969                 break;
970         case AST_AOC_VOLUME_UNIT_MESSAGE:
971                 str = "Message";
972                 break;
973         }
974         return str;
975 }
976
977 /*!
978  * \internal
979  * \brief Convert ast_aoc_charged_item to string.
980  * \since 1.8
981  *
982  * \param value Value to convert to string.
983  *
984  * \return String equivalent.
985  */
986 static const char *aoc_charged_item_str(enum ast_aoc_s_charged_item value)
987 {
988         const char *str;
989
990         switch (value) {
991         default:
992         case AST_AOC_CHARGED_ITEM_NA:
993                 str = "NotAvailable";
994                 break;
995         case AST_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT:
996                 str = "SpecialArrangement";
997                 break;
998         case AST_AOC_CHARGED_ITEM_BASIC_COMMUNICATION:
999                 str = "BasicCommunication";
1000                 break;
1001         case AST_AOC_CHARGED_ITEM_CALL_ATTEMPT:
1002                 str = "CallAttempt";
1003                 break;
1004         case AST_AOC_CHARGED_ITEM_CALL_SETUP:
1005                 str = "CallSetup";
1006                 break;
1007         case AST_AOC_CHARGED_ITEM_USER_USER_INFO:
1008                 str = "UserUserInfo";
1009                 break;
1010         case AST_AOC_CHARGED_ITEM_SUPPLEMENTARY_SERVICE:
1011                 str = "SupplementaryService";
1012                 break;
1013         }
1014         return str;
1015 }
1016
1017 /*!
1018  * \internal
1019  * \brief Convert ast_aoc_total_type to string.
1020  * \since 1.8
1021  *
1022  * \param value Value to convert to string.
1023  *
1024  * \return String equivalent.
1025  */
1026 static const char *aoc_type_of_totaling_str(enum ast_aoc_total_type value)
1027 {
1028         const char *str;
1029
1030         switch (value) {
1031         default:
1032         case AST_AOC_SUBTOTAL:
1033                 str = "SubTotal";
1034                 break;
1035         case AST_AOC_TOTAL:
1036                 str = "Total";
1037                 break;
1038         }
1039         return str;
1040 }
1041
1042 /*!
1043  * \internal
1044  * \brief Convert ast_aoc_rate_type to string.
1045  * \since 1.8
1046  *
1047  * \param value Value to convert to string.
1048  *
1049  * \return String equivalent.
1050  */
1051 static const char *aoc_rate_type_str(enum ast_aoc_s_rate_type value)
1052 {
1053         const char *str;
1054
1055         switch (value) {
1056         default:
1057         case AST_AOC_RATE_TYPE_NA:
1058                 str = "NotAvailable";
1059                 break;
1060         case AST_AOC_RATE_TYPE_FREE:
1061                 str = "Free";
1062                 break;
1063         case AST_AOC_RATE_TYPE_FREE_FROM_BEGINNING:
1064                 str = "FreeFromBeginning";
1065                 break;
1066         case AST_AOC_RATE_TYPE_DURATION:
1067                 str = "Duration";
1068                 break;
1069         case AST_AOC_RATE_TYPE_FLAT:
1070                 str = "Flat";
1071                 break;
1072         case AST_AOC_RATE_TYPE_VOLUME:
1073                 str = "Volume";
1074                 break;
1075         case AST_AOC_RATE_TYPE_SPECIAL_CODE:
1076                 str = "SpecialCode";
1077                 break;
1078         }
1079         return str;
1080 }
1081
1082 /*!
1083  * \internal
1084  * \brief Convert AST_AOC_TIME_SCALE to string.
1085  * \since 1.8
1086  *
1087  * \param value Value to convert to string.
1088  *
1089  * \return String equivalent.
1090  */
1091 static const char *aoc_scale_str(enum ast_aoc_time_scale value)
1092 {
1093         const char *str;
1094
1095         switch (value) {
1096         default:
1097         case AST_AOC_TIME_SCALE_HUNDREDTH_SECOND:
1098                 str = "OneHundredthSecond";
1099                 break;
1100         case AST_AOC_TIME_SCALE_TENTH_SECOND:
1101                 str = "OneTenthSecond";
1102                 break;
1103         case AST_AOC_TIME_SCALE_SECOND:
1104                 str = "Second";
1105                 break;
1106         case AST_AOC_TIME_SCALE_TEN_SECOND:
1107                 str = "TenSeconds";
1108                 break;
1109         case AST_AOC_TIME_SCALE_MINUTE:
1110                 str = "Minute";
1111                 break;
1112         case AST_AOC_TIME_SCALE_HOUR:
1113                 str = "Hour";
1114                 break;
1115         case AST_AOC_TIME_SCALE_DAY:
1116                 str = "Day";
1117                 break;
1118         }
1119         return str;
1120 }
1121
1122 static const char *aoc_charge_type_str(enum ast_aoc_charge_type value)
1123 {
1124         const char *str;
1125
1126         switch (value) {
1127         default:
1128         case AST_AOC_CHARGE_NA:
1129                 str = "NotAvailable";
1130                 break;
1131         case AST_AOC_CHARGE_FREE:
1132                 str = "Free";
1133                 break;
1134         case AST_AOC_CHARGE_CURRENCY:
1135                 str = "Currency";
1136                 break;
1137         case AST_AOC_CHARGE_UNIT:
1138                 str = "Units";
1139                 break;
1140         }
1141
1142         return str;
1143 }
1144
1145 static const char *aoc_multiplier_str(enum ast_aoc_currency_multiplier mult)
1146 {
1147         switch (mult) {
1148         case AST_AOC_MULT_ONETHOUSANDTH:
1149                 return "1/1000";
1150         case AST_AOC_MULT_ONEHUNDREDTH:
1151                 return "1/100";
1152         case AST_AOC_MULT_ONETENTH:
1153                 return "1/10";
1154         case AST_AOC_MULT_ONE:
1155                 return "1";
1156         case AST_AOC_MULT_TEN:
1157                 return "10";
1158         case AST_AOC_MULT_HUNDRED:
1159                 return "100";
1160         case AST_AOC_MULT_THOUSAND:
1161                 return "1000";
1162         case AST_AOC_MULT_NUM_ENTRIES:
1163                 break;
1164         }
1165         return "1";
1166 }
1167
1168 static const char *aoc_billingid_str(enum ast_aoc_billing_id billing_id)
1169 {
1170         switch (billing_id) {
1171         case AST_AOC_BILLING_NORMAL:
1172                 return "Normal";
1173         case AST_AOC_BILLING_REVERSE_CHARGE:
1174                 return "Reverse";
1175         case AST_AOC_BILLING_CREDIT_CARD:
1176                 return "CreditCard";
1177         case AST_AOC_BILLING_CALL_FWD_UNCONDITIONAL:
1178                 return "CallForwardingUnconditional";
1179         case AST_AOC_BILLING_CALL_FWD_BUSY:
1180                 return "CallForwardingBusy";
1181         case AST_AOC_BILLING_CALL_FWD_NO_REPLY:
1182                 return "CallForwardingNoReply";
1183         case AST_AOC_BILLING_CALL_DEFLECTION:
1184                 return "CallDeflection";
1185         case AST_AOC_BILLING_CALL_TRANSFER:
1186                 return "CallTransfer";
1187         case AST_AOC_BILLING_NA:
1188                 return "NotAvailable";
1189         case AST_AOC_BILLING_NUM_ENTRIES:
1190                 break;
1191         }
1192         return "NotAvailable";
1193 }
1194
1195 int ast_aoc_test_encode_decode_match(struct ast_aoc_decoded *decoded)
1196 {
1197         struct ast_aoc_decoded *new_decoded = NULL;
1198         struct ast_aoc_encoded *encoded = NULL;
1199         size_t size;
1200         int res = 0;
1201
1202         if (!(encoded = ast_aoc_encode(decoded, &size, NULL))) {
1203                 return -1;
1204         }
1205
1206         if (!(new_decoded = ast_aoc_decode(encoded, size, NULL))) {
1207                 ast_free(encoded);
1208                 return -1;
1209         }
1210
1211         if (memcmp(new_decoded, decoded, sizeof(struct ast_aoc_decoded))) {
1212                 res = -1;
1213         }
1214
1215         ast_aoc_destroy_decoded(new_decoded);
1216         ast_aoc_destroy_encoded(encoded);
1217         return res;
1218 }
1219
1220 static char *aoc_cli_debug_enable(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1221 {
1222         switch (cmd) {
1223         case CLI_INIT:
1224                 e->command = "aoc set debug";
1225                 e->usage =
1226                         "Usage: 'aoc set debug on' to enable aoc debug, 'aoc set debug off' to disable debug.\n";
1227                 return NULL;
1228         case CLI_GENERATE:
1229                 return NULL;
1230         case CLI_HANDLER:
1231                 if (a->argc != 4) {
1232                         return CLI_SHOWUSAGE;
1233                 } else if(ast_true(a->argv[3])) {
1234                         ast_cli(a->fd, "aoc debug enabled\n");
1235                         aoc_debug_enabled = 1;
1236                 } else if (ast_false(a->argv[3])) {
1237                         ast_cli(a->fd, "aoc debug disabled\n");
1238                         aoc_debug_enabled = 0;
1239                 } else {
1240                         return CLI_SHOWUSAGE;
1241                 }
1242         }
1243
1244         return CLI_SUCCESS;
1245 }
1246
1247 /*!
1248  * \internal
1249  * \brief Append the time structure to the event message string.
1250  * \since 1.8
1251  *
1252  * \param msg Event message string being built.
1253  * \param prefix Prefix to add to the amount lines.
1254  * \param name Name of the time structure to convert.
1255  * \param time Data to convert.
1256  * \param scale Data to convert.
1257  *
1258  * \return Nothing
1259  */
1260 static void aoc_time_str(struct ast_str **msg, const char *prefix, const char *name, unsigned long time, enum ast_aoc_time_scale scale)
1261 {
1262         ast_str_append(msg, 0, "%s/%s/Length: %lu\r\n", prefix, name, time);
1263         ast_str_append(msg, 0, "%s/%s/Scale: %s\r\n", prefix, name,
1264                 aoc_scale_str(scale));
1265 }
1266
1267 /*!
1268  * \internal
1269  * \brief Append the amount structure to the event message string.
1270  * \since 1.8
1271  *
1272  * \param msg Event message string being built.
1273  * \param prefix Prefix to add to the amount lines.
1274  * \param amount Data to convert.
1275  * \param multipler to convert
1276  *
1277  * \return Nothing
1278  */
1279 static void aoc_amount_str(struct ast_str **msg, const char *prefix, unsigned int amount, enum ast_aoc_currency_multiplier mult)
1280 {
1281         static const char name[] = "Amount";
1282
1283         ast_str_append(msg, 0, "%s/%s/Cost: %u\r\n", prefix, name, amount);
1284         ast_str_append(msg, 0, "%s/%s/Multiplier: %s\r\n", prefix, name,
1285                 aoc_multiplier_str(mult));
1286 }
1287
1288 static void aoc_request_event(const struct ast_aoc_decoded *decoded, struct ast_channel *chan, struct ast_str **msg)
1289 {
1290         if (chan) {
1291                 ast_str_append(msg, 0, "Channel: %s\r\n", ast_channel_name(chan));
1292                 ast_str_append(msg, 0, "UniqueID: %s\r\n", chan->uniqueid);
1293         }
1294
1295         if (decoded->request_flag) {
1296                 ast_str_append(msg, 0, "AOCRequest:");
1297                 if (decoded->request_flag & AST_AOC_REQUEST_S) {
1298                         ast_str_append(msg, 0, "S");
1299                 }
1300                 if (decoded->request_flag & AST_AOC_REQUEST_D) {
1301                         ast_str_append(msg, 0, "D");
1302                 }
1303                 if (decoded->request_flag & AST_AOC_REQUEST_E) {
1304                         ast_str_append(msg, 0, "E");
1305                 }
1306                 ast_str_append(msg, 0, "\r\n");
1307
1308         } else {
1309                 ast_str_append(msg, 0, "AOCRequest: NONE\r\n");
1310         }
1311 }
1312
1313 static void aoc_s_event(const struct ast_aoc_decoded *decoded, struct ast_channel *owner, struct ast_str **msg)
1314 {
1315         const char *rate_str;
1316         char prefix[32];
1317         int idx;
1318
1319         if (owner) {
1320                 ast_str_append(msg, 0, "Channel: %s\r\n", ast_channel_name(owner));
1321                 ast_str_append(msg, 0, "UniqueID: %s\r\n", owner->uniqueid);
1322         }
1323
1324         ast_str_append(msg, 0, "NumberRates: %d\r\n", decoded->aoc_s_count);
1325         for (idx = 0; idx < decoded->aoc_s_count; ++idx) {
1326                 snprintf(prefix, sizeof(prefix), "Rate(%d)", idx);
1327
1328                 ast_str_append(msg, 0, "%s/Chargeable: %s\r\n", prefix,
1329                         aoc_charged_item_str(decoded->aoc_s_entries[idx].charged_item));
1330                 if (decoded->aoc_s_entries[idx].charged_item == AST_AOC_CHARGED_ITEM_NA) {
1331                         continue;
1332                 }
1333                 rate_str = aoc_rate_type_str(decoded->aoc_s_entries[idx].rate_type);
1334                 ast_str_append(msg, 0, "%s/Type: %s\r\n", prefix, rate_str);
1335                 switch (decoded->aoc_s_entries[idx].rate_type) {
1336                 case AST_AOC_RATE_TYPE_DURATION:
1337                         strcat(prefix, "/");
1338                         strcat(prefix, rate_str);
1339                         ast_str_append(msg, 0, "%s/Currency: %s\r\n", prefix,
1340                                 decoded->aoc_s_entries[idx].rate.duration.currency_name);
1341                         aoc_amount_str(msg, prefix,
1342                                 decoded->aoc_s_entries[idx].rate.duration.amount,
1343                                 decoded->aoc_s_entries[idx].rate.duration.multiplier);
1344                         ast_str_append(msg, 0, "%s/ChargingType: %s\r\n", prefix,
1345                                 decoded->aoc_s_entries[idx].rate.duration.charging_type ?
1346                                 "StepFunction" : "ContinuousCharging");
1347                         aoc_time_str(msg, prefix, "Time",
1348                                 decoded->aoc_s_entries[idx].rate.duration.time,
1349                                 decoded->aoc_s_entries[idx].rate.duration.time_scale);
1350                         if (decoded->aoc_s_entries[idx].rate.duration.granularity_time) {
1351                                 aoc_time_str(msg, prefix, "Granularity",
1352                                         decoded->aoc_s_entries[idx].rate.duration.granularity_time,
1353                                         decoded->aoc_s_entries[idx].rate.duration.granularity_time_scale);
1354                         }
1355                         break;
1356                 case AST_AOC_RATE_TYPE_FLAT:
1357                         strcat(prefix, "/");
1358                         strcat(prefix, rate_str);
1359                         ast_str_append(msg, 0, "%s/Currency: %s\r\n", prefix,
1360                                 decoded->aoc_s_entries[idx].rate.flat.currency_name);
1361                         aoc_amount_str(msg, prefix,
1362                                 decoded->aoc_s_entries[idx].rate.flat.amount,
1363                                 decoded->aoc_s_entries[idx].rate.flat.multiplier);
1364                         break;
1365                 case AST_AOC_RATE_TYPE_VOLUME:
1366                         strcat(prefix, "/");
1367                         strcat(prefix, rate_str);
1368                         ast_str_append(msg, 0, "%s/Currency: %s\r\n", prefix,
1369                                 decoded->aoc_s_entries[idx].rate.volume.currency_name);
1370                         aoc_amount_str(msg, prefix,
1371                                 decoded->aoc_s_entries[idx].rate.volume.amount,
1372                                 decoded->aoc_s_entries[idx].rate.volume.multiplier);
1373                         ast_str_append(msg, 0, "%s/Unit: %s\r\n", prefix,
1374                                 aoc_volume_unit_str(decoded->aoc_s_entries[idx].rate.volume.volume_unit));
1375                         break;
1376                 case AST_AOC_RATE_TYPE_SPECIAL_CODE:
1377                         ast_str_append(msg, 0, "%s/%s: %d\r\n", prefix, rate_str,
1378                                 decoded->aoc_s_entries[idx].rate.special_code);
1379                         break;
1380                 default:
1381                         break;
1382                 }
1383         }
1384 }
1385
1386 static void aoc_d_event(const struct ast_aoc_decoded *decoded, struct ast_channel *chan, struct ast_str **msg)
1387 {
1388         const char *charge_str;
1389         int idx;
1390         char prefix[32];
1391
1392         if (chan) {
1393                 ast_str_append(msg, 0, "Channel: %s\r\n", ast_channel_name(chan));
1394                 ast_str_append(msg, 0, "UniqueID: %s\r\n", chan->uniqueid);
1395         }
1396
1397         charge_str = aoc_charge_type_str(decoded->charge_type);
1398         ast_str_append(msg, 0, "Type: %s\r\n", charge_str);
1399
1400         switch (decoded->charge_type) {
1401         case AST_AOC_CHARGE_CURRENCY:
1402         case AST_AOC_CHARGE_UNIT:
1403                 ast_str_append(msg, 0, "BillingID: %s\r\n",
1404                         aoc_billingid_str(decoded->billing_id));
1405                 ast_str_append(msg, 0, "TypeOfCharging: %s\r\n",
1406                         aoc_type_of_totaling_str(decoded->total_type));
1407                 break;
1408         default:
1409                 break;
1410         }
1411
1412         switch (decoded->charge_type) {
1413         case AST_AOC_CHARGE_CURRENCY:
1414                 ast_str_append(msg, 0, "%s: %s\r\n", charge_str,
1415                         decoded->currency_name);
1416                 aoc_amount_str(msg, charge_str,
1417                         decoded->currency_amount,
1418                         decoded->multiplier);
1419                 break;
1420         case AST_AOC_CHARGE_UNIT:
1421                 ast_str_append(msg, 0, "%s/NumberItems: %d\r\n", charge_str,
1422                         decoded->unit_count);
1423                 for (idx = 0; idx < decoded->unit_count; ++idx) {
1424                         snprintf(prefix, sizeof(prefix), "%s/Item(%d)", charge_str, idx);
1425                         if (decoded->unit_list[idx].valid_amount) {
1426                                 ast_str_append(msg, 0, "%s/NumberOf: %u\r\n", prefix,
1427                                         decoded->unit_list[idx].amount);
1428                         }
1429                         if (decoded->unit_list[idx].valid_type) {
1430                                 ast_str_append(msg, 0, "%s/TypeOf: %d\r\n", prefix,
1431                                         decoded->unit_list[idx].type);
1432                         }
1433                 }
1434                 break;
1435         default:
1436                 break;
1437         }
1438 }
1439
1440 static void aoc_e_event(const struct ast_aoc_decoded *decoded, struct ast_channel *chan, struct ast_str **msg)
1441 {
1442         const char *charge_str;
1443         int idx;
1444         char prefix[32];
1445
1446         if (chan) {
1447                 ast_str_append(msg, 0, "Channel: %s\r\n", ast_channel_name(chan));
1448                 ast_str_append(msg, 0, "UniqueID: %s\r\n", chan->uniqueid);
1449         }
1450
1451         charge_str = "ChargingAssociation";
1452
1453         switch (decoded->charging_association.charging_type) {
1454         case AST_AOC_CHARGING_ASSOCIATION_NUMBER:
1455                 snprintf(prefix, sizeof(prefix), "%s/Number", charge_str);
1456                 ast_str_append(msg, 0, "%s: %s\r\n", prefix,
1457                         decoded->charging_association.charge.number.number);
1458                 ast_str_append(msg, 0, "%s/Plan: %d\r\n", prefix,
1459                         decoded->charging_association.charge.number.plan);
1460                 break;
1461         case AST_AOC_CHARGING_ASSOCIATION_ID:
1462                 ast_str_append(msg, 0, "%s/ID: %d\r\n", charge_str, decoded->charging_association.charge.id);
1463                 break;
1464         case AST_AOC_CHARGING_ASSOCIATION_NA:
1465         default:
1466                 break;
1467         }
1468
1469         charge_str = aoc_charge_type_str(decoded->charge_type);
1470         ast_str_append(msg, 0, "Type: %s\r\n", charge_str);
1471         switch (decoded->charge_type) {
1472         case AST_AOC_CHARGE_CURRENCY:
1473         case AST_AOC_CHARGE_UNIT:
1474                 ast_str_append(msg, 0, "BillingID: %s\r\n",
1475                         aoc_billingid_str(decoded->billing_id));
1476                 break;
1477         default:
1478                 break;
1479         }
1480         switch (decoded->charge_type) {
1481         case AST_AOC_CHARGE_CURRENCY:
1482                 ast_str_append(msg, 0, "%s: %s\r\n", charge_str,
1483                         decoded->currency_name);
1484                 aoc_amount_str(msg, charge_str,
1485                         decoded->currency_amount,
1486                         decoded->multiplier);
1487                 break;
1488         case AST_AOC_CHARGE_UNIT:
1489                 ast_str_append(msg, 0, "%s/NumberItems: %d\r\n", charge_str,
1490                         decoded->unit_count);
1491                 for (idx = 0; idx < decoded->unit_count; ++idx) {
1492                         snprintf(prefix, sizeof(prefix), "%s/Item(%d)", charge_str, idx);
1493                         if (decoded->unit_list[idx].valid_amount) {
1494                                 ast_str_append(msg, 0, "%s/NumberOf: %u\r\n", prefix,
1495                                         decoded->unit_list[idx].amount);
1496                         }
1497                         if (decoded->unit_list[idx].valid_type) {
1498                                 ast_str_append(msg, 0, "%s/TypeOf: %d\r\n", prefix,
1499                                         decoded->unit_list[idx].type);
1500                         }
1501                 }
1502                 break;
1503         default:
1504                 break;
1505         }
1506 }
1507
1508 int ast_aoc_manager_event(const struct ast_aoc_decoded *decoded, struct ast_channel *chan)
1509 {
1510         struct ast_str *msg;
1511
1512         if (!decoded || !(msg = ast_str_create(1024))) {
1513                 return -1;
1514         }
1515
1516         switch (decoded->msg_type) {
1517         case AST_AOC_S:
1518                 if (chan) {
1519                         aoc_s_event(decoded, chan, &msg);
1520                         ast_manager_event(chan, EVENT_FLAG_AOC, "AOC-S", "%s", ast_str_buffer(msg));
1521                 }
1522                 break;
1523         case AST_AOC_D:
1524                 if (chan) {
1525                         aoc_d_event(decoded, chan, &msg);
1526                         ast_manager_event(chan, EVENT_FLAG_AOC, "AOC-D", "%s", ast_str_buffer(msg));
1527                 }
1528                 break;
1529         case AST_AOC_E:
1530                 {
1531                         struct ast_channel *chans[1];
1532                         aoc_e_event(decoded, chan, &msg);
1533                         chans[0] = chan;
1534                         ast_manager_event_multichan(EVENT_FLAG_AOC, "AOC-E", chan ? 1 : 0, chans, "%s", ast_str_buffer(msg));
1535                 }
1536                 break;
1537         default:
1538                 /* events for AST_AOC_REQUEST are not generated here */
1539                 break;
1540         }
1541
1542         ast_free(msg);
1543         return 0;
1544 }
1545
1546 int ast_aoc_decoded2str(const struct ast_aoc_decoded *decoded, struct ast_str **msg)
1547 {
1548         if (!decoded || !msg) {
1549                 return -1;
1550         }
1551
1552         switch (decoded->msg_type) {
1553         case AST_AOC_S:
1554                 ast_str_append(msg, 0, "AOC-S\r\n");
1555                 aoc_s_event(decoded, NULL, msg);
1556                 break;
1557         case AST_AOC_D:
1558                 ast_str_append(msg, 0, "AOC-D\r\n");
1559                 aoc_d_event(decoded, NULL, msg);
1560                 break;
1561         case AST_AOC_E:
1562                 ast_str_append(msg, 0, "AOC-E\r\n");
1563                 aoc_e_event(decoded, NULL, msg);
1564                 break;
1565         case AST_AOC_REQUEST:
1566                 ast_str_append(msg, 0, "AOC-Request\r\n");
1567                 aoc_request_event(decoded, NULL, msg);
1568                 break;
1569         }
1570
1571         return 0;
1572 }
1573
1574 static void aoc_display_decoded_debug(const struct ast_aoc_decoded *decoded, int decoding, struct ast_channel *chan)
1575 {
1576         struct ast_str *msg;
1577
1578         if (!decoded || !(msg = ast_str_create(1024))) {
1579                 return;
1580         }
1581
1582         if (decoding) {
1583                 ast_str_append(&msg, 0, "---- DECODED AOC MSG ----\r\n");
1584         } else {
1585                 ast_str_append(&msg, 0, "---- ENCODED AOC MSG ----\r\n");
1586         }
1587         if (chan) {
1588                 ast_str_append(&msg, 0, "CHANNEL: %s\r\n", ast_channel_name(chan));
1589         }
1590
1591         if (ast_aoc_decoded2str(decoded, &msg)) {
1592                 ast_free(msg);
1593                 return;
1594         }
1595
1596         ast_verb(1, "%s\r\n", ast_str_buffer(msg));
1597         ast_free(msg);
1598 }
1599
1600 static struct ast_cli_entry aoc_cli[] = {
1601         AST_CLI_DEFINE(aoc_cli_debug_enable, "enable cli debugging of AOC messages"),
1602 };
1603
1604 int ast_aoc_cli_init(void)
1605 {
1606         return ast_cli_register_multiple(aoc_cli, ARRAY_LEN(aoc_cli));
1607 }