Asterisk - The Open Source Telephony Project  21.4.1
aoc.c
Go to the documentation of this file.
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 
32 #include "asterisk/aoc.h"
33 #include "asterisk/utils.h"
34 #include "asterisk/strings.h"
35 #include "asterisk/_private.h"
36 #include "asterisk/cli.h"
37 #include "asterisk/manager.h"
38 #include "asterisk/stasis_channels.h"
39 #include "asterisk/stasis_message_router.h"
40 
41 /*** DOCUMENTATION
42  <managerEvent language="en_US" name="AOC-S">
43  <managerEventInstance class="EVENT_FLAG_AOC">
44  <synopsis>Raised when an Advice of Charge message is sent at the beginning of a call.</synopsis>
45  <syntax>
46  <channel_snapshot/>
47  <parameter name="Chargeable" />
48  <parameter name="RateType">
49  <enumlist>
50  <enum name="NotAvailable" />
51  <enum name="Free" />
52  <enum name="FreeFromBeginning" />
53  <enum name="Duration" />
54  <enum name="Flag" />
55  <enum name="Volume" />
56  <enum name="SpecialCode" />
57  </enumlist>
58  </parameter>
59  <parameter name="Currency" />
60  <parameter name="Name" />
61  <parameter name="Cost" />
62  <parameter name="Multiplier">
63  <enumlist>
64  <enum name="1/1000" />
65  <enum name="1/100" />
66  <enum name="1/10" />
67  <enum name="1" />
68  <enum name="10" />
69  <enum name="100" />
70  <enum name="1000" />
71  </enumlist>
72  </parameter>
73  <parameter name="ChargingType" />
74  <parameter name="StepFunction" />
75  <parameter name="Granularity" />
76  <parameter name="Length" />
77  <parameter name="Scale" />
78  <parameter name="Unit">
79  <enumlist>
80  <enum name="Octect" />
81  <enum name="Segment" />
82  <enum name="Message" />
83  </enumlist>
84  </parameter>
85  <parameter name="SpecialCode" />
86  </syntax>
87  <see-also>
88  <ref type="managerEvent">AOC-D</ref>
89  <ref type="managerEvent">AOC-E</ref>
90  </see-also>
91  </managerEventInstance>
92  </managerEvent>
93  <managerEvent language="en_US" name="AOC-D">
94  <managerEventInstance class="EVENT_FLAG_AOC">
95  <synopsis>Raised when an Advice of Charge message is sent during a call.</synopsis>
96  <syntax>
97  <channel_snapshot/>
98  <parameter name="Charge" />
99  <parameter name="Type">
100  <enumlist>
101  <enum name="NotAvailable" />
102  <enum name="Free" />
103  <enum name="Currency" />
104  <enum name="Units" />
105  </enumlist>
106  </parameter>
107  <parameter name="BillingID">
108  <enumlist>
109  <enum name="Normal" />
110  <enum name="Reverse" />
111  <enum name="CreditCard" />
112  <enum name="CallForwardingUnconditional" />
113  <enum name="CallForwardingBusy" />
114  <enum name="CallForwardingNoReply" />
115  <enum name="CallDeflection" />
116  <enum name="CallTransfer" />
117  <enum name="NotAvailable" />
118  </enumlist>
119  </parameter>
120  <parameter name="TotalType">
121  <enumlist>
122  <enum name="SubTotal" />
123  <enum name="Total" />
124  </enumlist>
125  </parameter>
126  <parameter name="Currency" />
127  <parameter name="Name" />
128  <parameter name="Cost" />
129  <parameter name="Multiplier">
130  <enumlist>
131  <enum name="1/1000" />
132  <enum name="1/100" />
133  <enum name="1/10" />
134  <enum name="1" />
135  <enum name="10" />
136  <enum name="100" />
137  <enum name="1000" />
138  </enumlist>
139  </parameter>
140  <parameter name="Units" />
141  <parameter name="NumberOf" />
142  <parameter name="TypeOf" />
143  </syntax>
144  <see-also>
145  <ref type="manager">AOCMessage</ref>
146  <ref type="managerEvent">AOC-S</ref>
147  <ref type="managerEvent">AOC-E</ref>
148  </see-also>
149  </managerEventInstance>
150  </managerEvent>
151  <managerEvent language="en_US" name="AOC-E">
152  <managerEventInstance class="EVENT_FLAG_AOC">
153  <synopsis>Raised when an Advice of Charge message is sent at the end of a call.</synopsis>
154  <syntax>
155  <channel_snapshot/>
156  <parameter name="ChargingAssociation" />
157  <parameter name="Number" />
158  <parameter name="Plan" />
159  <parameter name="ID" />
160  <xi:include xpointer="xpointer(/docs/managerEvent[@name='AOC-D']/managerEventInstance/syntax/parameter)" />
161  </syntax>
162  <see-also>
163  <ref type="manager">AOCMessage</ref>
164  <ref type="managerEvent">AOC-S</ref>
165  <ref type="managerEvent">AOC-D</ref>
166  </see-also>
167  </managerEventInstance>
168  </managerEvent>
169 ***/
170 
171 /* Encoded Payload Flags */
172 #define AST_AOC_ENCODED_TYPE_REQUEST (0 << 0)
173 #define AST_AOC_ENCODED_TYPE_D (1 << 0)
174 #define AST_AOC_ENCODED_TYPE_E (2 << 0)
175 #define AST_AOC_ENCODED_TYPE_S (3 << 0)
176 
177 #define AST_AOC_ENCODED_REQUEST_S (1 << 2)
178 #define AST_AOC_ENCODED_REQUEST_D (1 << 3)
179 #define AST_AOC_ENCODED_REQUEST_E (1 << 4)
180 
181 #define AST_AOC_ENCODED_CHARGE_NA (0 << 5)
182 #define AST_AOC_ENCODED_CHARGE_FREE (1 << 5)
183 #define AST_AOC_ENCODED_CHARGE_CURRENCY (2 << 5)
184 #define AST_AOC_ENCODED_CHARGE_UNIT (3 << 5)
185 
186 #define AST_AOC_ENCODED_CHARGE_SUBTOTAL (1 << 7)
187 #define AST_AOC_ENCODED_CHARGE_TOTAL (0 << 7)
188 
189 #define AST_AOC_ENCODE_VERSION 1
190 
191 
192 static char aoc_debug_enabled = 0;
193 static void aoc_display_decoded_debug(const struct ast_aoc_decoded *decoded, int decoding, struct ast_channel *chan);
194 static int aoc_s_add_entry(struct ast_aoc_decoded *decoded, struct ast_aoc_s_entry *entry);
195 
196 /* AOC Payload Header. Holds all the encoded AOC data to pass on the wire */
198  uint8_t version;
199  uint8_t flags;
200  uint16_t datalen;
201  unsigned char data[0];
202 };
203 
204 /* Decoded AOC data */
206  enum ast_aoc_type msg_type;
207  enum ast_aoc_charge_type charge_type;
208  enum ast_aoc_request request_flag;
209  enum ast_aoc_total_type total_type;
210 
211  /* currency information */
212  enum ast_aoc_currency_multiplier multiplier;
213  unsigned int currency_amount;
214  char currency_name[AOC_CURRENCY_NAME_SIZE];
215 
216  /* unit information */
217  int unit_count;
218  struct ast_aoc_unit_entry unit_list[32];
219 
220  /* Billing Id */
221  enum ast_aoc_billing_id billing_id;
222 
223  /* Charging Association information */
224  struct ast_aoc_charging_association charging_association;
225 
226  /* AOC-S charge information */
227  int aoc_s_count;
228  struct ast_aoc_s_entry aoc_s_entries[10];
229 
230  /* Is this an AOC Termination Request */
231  char termination_request;
232 };
233 
234 /*! \brief AOC Payload Information Elements */
235 enum AOC_IE {
236  AOC_IE_CURRENCY = 1,
237  AOC_IE_UNIT = 2,
238  AOC_IE_BILLING = 3,
239  AOC_IE_CHARGING_ASSOCIATION = 4,
240  AOC_IE_RATE = 5,
241  AOC_IE_TERMINATION_REQUEST = 6,
242 };
243 
244 /*! \brief AOC IE payload header */
246  uint8_t ie_id;
247  uint8_t datalen;
248  char data[0];
249 } __attribute__((packed));
250 
252  uint32_t amount;
253  uint8_t multiplier;
254  char name[AOC_CURRENCY_NAME_SIZE];
255 } __attribute__((packed));
256 
257 struct aoc_ie_unit {
258  uint32_t amount;
259  uint8_t valid_type;
260  uint8_t valid_amount;
261  uint8_t type;
262 } __attribute__((packed));
263 
265  uint8_t id;
266 } __attribute__((packed));
267 
270 } __attribute__((packed));
271 
273  struct ast_aoc_s_entry entry;
274 } __attribute__((packed));
275 
276 struct ast_aoc_decoded *ast_aoc_create(const enum ast_aoc_type msg_type,
277  const enum ast_aoc_charge_type charge_type,
278  const enum ast_aoc_request requests)
279 {
280  struct ast_aoc_decoded *decoded = NULL;
281 
282  /* verify input */
283  if (((unsigned int) charge_type > AST_AOC_CHARGE_UNIT) ||
284  ((unsigned int) msg_type > AST_AOC_E) ||
285  ((msg_type == AST_AOC_REQUEST) && !requests)) {
286 
287  ast_log(LOG_WARNING, "Failed to create ast_aoc_decoded object, invalid input\n");
288  return NULL;
289  }
290 
291  if (!(decoded = ast_calloc(1, sizeof(struct ast_aoc_decoded)))) {
292  ast_log(LOG_WARNING, "Failed to create ast_aoc_decoded object \n");
293  return NULL;
294  }
295 
296  decoded->msg_type = msg_type;
297 
298  if (msg_type == AST_AOC_REQUEST) {
299  decoded->request_flag = requests;
300  } else if ((msg_type == AST_AOC_D) || (msg_type == AST_AOC_E)) {
301  decoded->charge_type = charge_type;
302  }
303 
304  return decoded;
305 }
306 
308 {
309  ast_free(decoded);
310  return NULL;
311 }
312 
314 {
315  ast_free(encoded);
316  return NULL;
317 }
318 
319 static void aoc_parse_ie_charging_rate(struct ast_aoc_decoded *decoded, const struct aoc_ie_charging_rate *ie)
320 {
321  struct ast_aoc_s_entry entry = { 0, };
322 
323  entry.charged_item = ntohs(ie->entry.charged_item);
324  entry.rate_type = ntohs(ie->entry.rate_type);
325 
326  switch (entry.rate_type) {
327  case AST_AOC_RATE_TYPE_DURATION:
328  entry.rate.duration.multiplier = ntohs(ie->entry.rate.duration.multiplier);
329  entry.rate.duration.amount = ntohl(ie->entry.rate.duration.amount);
330  entry.rate.duration.time = ntohl(ie->entry.rate.duration.time);
331  entry.rate.duration.time_scale = ntohs(ie->entry.rate.duration.time_scale);
332  entry.rate.duration.granularity_time = ntohl(ie->entry.rate.duration.granularity_time);
333  entry.rate.duration.granularity_time_scale = ntohs(ie->entry.rate.duration.granularity_time_scale);
334  entry.rate.duration.charging_type = ie->entry.rate.duration.charging_type; /* only one byte */
335 
336  if (!ast_strlen_zero(ie->entry.rate.duration.currency_name)) {
337  ast_copy_string(entry.rate.duration.currency_name,
338  ie->entry.rate.duration.currency_name,
339  sizeof(entry.rate.duration.currency_name));
340  }
341  break;
342  case AST_AOC_RATE_TYPE_FLAT:
343  entry.rate.flat.multiplier = ntohs(ie->entry.rate.flat.multiplier);
344  entry.rate.flat.amount = ntohl(ie->entry.rate.flat.amount);
345  if (!ast_strlen_zero(ie->entry.rate.flat.currency_name)) {
347  ie->entry.rate.flat.currency_name,
348  sizeof(entry.rate.flat.currency_name));
349  }
350  break;
351  case AST_AOC_RATE_TYPE_VOLUME:
352  entry.rate.volume.multiplier = ntohs(ie->entry.rate.volume.multiplier);
353  entry.rate.volume.amount = ntohl(ie->entry.rate.volume.amount);
354  entry.rate.volume.volume_unit = ntohs(ie->entry.rate.volume.volume_unit);
355  if (!ast_strlen_zero(ie->entry.rate.volume.currency_name)) {
356  ast_copy_string(entry.rate.volume.currency_name,
357  ie->entry.rate.volume.currency_name,
358  sizeof(entry.rate.volume.currency_name));
359  }
360  break;
361  case AST_AOC_RATE_TYPE_SPECIAL_CODE:
362  entry.rate.special_code = ntohs(ie->entry.rate.special_code);
363  break;
364  }
365 
366  aoc_s_add_entry(decoded, &entry);
367 }
368 
369 static int aoc_parse_ie(struct ast_aoc_decoded *decoded, unsigned char *data, unsigned int datalen)
370 {
371  enum AOC_IE ie_id;
372  unsigned int len;
373 
374  while (datalen >= 2) {
375  ie_id = data[0];
376  len = data[1];
377  if (len > datalen -2) {
378  ast_log(LOG_ERROR, "AOC information element length exceeds the total message size\n");
379  return -1;
380  }
381 
382  switch(ie_id) {
383  case AOC_IE_CURRENCY:
384  if (len == sizeof(struct aoc_ie_currency)) {
385  struct aoc_ie_currency ie;
386  memcpy(&ie, data + 2, len);
387  decoded->currency_amount = ntohl(ie.amount);
388  decoded->multiplier = ie.multiplier; /* only one byte */
389  memcpy(decoded->currency_name, ie.name, sizeof(decoded->currency_name));
390  } else {
391  ast_log(LOG_WARNING, "Received invalid currency ie\n");
392  }
393  break;
394  case AOC_IE_UNIT:
395  if (len == sizeof(struct aoc_ie_unit)) {
396  struct aoc_ie_unit ie;
397  memcpy(&ie, data + 2, len);
398  ast_aoc_add_unit_entry(decoded, ie.valid_amount, ntohl(ie.amount), ie.valid_type, ie.type);
399  } else {
400  ast_log(LOG_WARNING, "Received invalid unit ie\n");
401  }
402  break;
403  case AOC_IE_BILLING:
404  if (len == sizeof(struct aoc_ie_billing)) {
405  struct aoc_ie_billing ie;
406  memcpy(&ie, data + 2, len);
407  decoded->billing_id = ie.id; /* only one byte */
408  } else {
409  ast_log(LOG_WARNING, "Received invalid billing ie\n");
410  }
411  break;
412  case AOC_IE_CHARGING_ASSOCIATION:
413  if (len == sizeof(struct aoc_ie_charging_association)) {
414  memcpy(&decoded->charging_association, data + 2, sizeof(decoded->charging_association));
415  /* everything in the charging_association struct is a single byte except for the id */
416  if (decoded->charging_association.charging_type == AST_AOC_CHARGING_ASSOCIATION_ID) {
417  decoded->charging_association.charge.id = ntohl(decoded->charging_association.charge.id);
418  }
419  } else {
420  ast_log(LOG_WARNING, "Received invalid charging association ie\n");
421  }
422  break;
423  case AOC_IE_RATE:
424  if (len == sizeof(struct aoc_ie_charging_rate)) {
425  struct aoc_ie_charging_rate ie;
426  memcpy(&ie, data + 2, len);
427  aoc_parse_ie_charging_rate(decoded, &ie);
428  } else {
429  ast_log(LOG_WARNING, "Received invalid charging rate ie\n");
430  }
431  break;
432  case AOC_IE_TERMINATION_REQUEST:
433  if (len == 0) {
434  decoded->termination_request = 1;
435  } else {
436  ast_log(LOG_WARNING, "Received invalid termination request ie\n");
437  }
438  break;
439  default:
440  ast_log(LOG_WARNING, "Unknown AOC Information Element, ignoring.\n");
441  }
442 
443  datalen -= (len + 2);
444  data += (len + 2);
445  }
446  return 0;
447 }
448 
449 struct ast_aoc_decoded *ast_aoc_decode(struct ast_aoc_encoded *encoded, size_t size, struct ast_channel *chan)
450 {
451  struct ast_aoc_decoded *decoded;
452 
453  /* verify our encoded payload is actually large enough to hold all the ies */
454  if ((size - (sizeof(struct ast_aoc_encoded)) != ntohs(encoded->datalen))) {
455  ast_log(LOG_WARNING, "Corrupted aoc encoded object, can not decode\n");
456  return NULL;
457  }
458 
459  if (!(decoded = ast_calloc(1, sizeof(struct ast_aoc_decoded)))) {
460  ast_log(LOG_WARNING, "Failed to create ast_aoc_decoded object \n");
461  return NULL;
462  }
463 
464  /* decode flags */
465 
466  if ((encoded->flags & AST_AOC_ENCODED_TYPE_S) == AST_AOC_ENCODED_TYPE_S) {
467  decoded->msg_type = AST_AOC_S;
468  } else if (encoded->flags & AST_AOC_ENCODED_TYPE_E) {
469  decoded->msg_type = AST_AOC_E;
470  } else if (encoded->flags & AST_AOC_ENCODED_TYPE_D) {
471  decoded->msg_type = AST_AOC_D;
472  } else {
473  decoded->msg_type = AST_AOC_REQUEST;
474  }
475 
476  if (decoded->msg_type == AST_AOC_REQUEST) {
477  if (encoded->flags & AST_AOC_ENCODED_REQUEST_S) {
478  decoded->request_flag |= AST_AOC_REQUEST_S;
479  }
480  if (encoded->flags & AST_AOC_ENCODED_REQUEST_D) {
481  decoded->request_flag |= AST_AOC_REQUEST_D;
482  }
483  if (encoded->flags & AST_AOC_ENCODED_REQUEST_E) {
484  decoded->request_flag |= AST_AOC_REQUEST_E;
485  }
486  } else if ((decoded->msg_type == AST_AOC_D) || (decoded->msg_type == AST_AOC_E)) {
487  if ((encoded->flags & AST_AOC_ENCODED_CHARGE_UNIT) == AST_AOC_ENCODED_CHARGE_UNIT) {
488  decoded->charge_type = AST_AOC_CHARGE_UNIT;
489  } else if ((encoded->flags & AST_AOC_ENCODED_CHARGE_CURRENCY) == AST_AOC_ENCODED_CHARGE_CURRENCY) {
490  decoded->charge_type = AST_AOC_CHARGE_CURRENCY;
491  } else if ((encoded->flags & AST_AOC_ENCODED_CHARGE_FREE) == AST_AOC_ENCODED_CHARGE_FREE) {
492  decoded->charge_type = AST_AOC_CHARGE_FREE;
493  } else {
494  decoded->charge_type = AST_AOC_CHARGE_NA;
495  }
496 
497  if (encoded->flags & AST_AOC_ENCODED_CHARGE_SUBTOTAL) {
498  decoded->total_type = AST_AOC_SUBTOTAL;
499  }
500  }
501 
502  /* decode information elements */
503  aoc_parse_ie(decoded, encoded->data, ntohs(encoded->datalen));
504 
505  if (aoc_debug_enabled) {
506  aoc_display_decoded_debug(decoded, 1, chan);
507  }
508 
509  return decoded;
510 }
511 
512 struct aoc_ie_data {
513  unsigned char buf[1024];
514  int pos;
515 };
516 
517 /*!
518  * \internal
519  * \brief append an AOC information element
520  * \note data is expected to already be in network byte order at this point
521  */
522 static int aoc_append_ie(struct aoc_ie_data *ied, unsigned short ie_id, const void *data, unsigned short datalen)
523 {
524  if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
525  ast_log(LOG_WARNING, "Failure to append AOC information element, out of space \n");
526  return -1;
527  }
528  ied->buf[ied->pos++] = ie_id;
529  ied->buf[ied->pos++] = datalen;
530  if (datalen) {
531  memcpy(ied->buf + ied->pos, data, datalen);
532  ied->pos += datalen;
533  }
534  return 0;
535 }
536 
537 static void aoc_create_ie_data_charging_rate(const struct ast_aoc_s_entry *entry, struct aoc_ie_charging_rate *ie)
538 {
539  ie->entry.charged_item = htons(entry->charged_item);
540  ie->entry.rate_type = htons(entry->rate_type);
541 
542  switch (entry->rate_type) {
543  case AST_AOC_RATE_TYPE_DURATION:
544  ie->entry.rate.duration.multiplier = htons(entry->rate.duration.multiplier);
545  ie->entry.rate.duration.amount = htonl(entry->rate.duration.amount);
546  ie->entry.rate.duration.time = htonl(entry->rate.duration.time);
547  ie->entry.rate.duration.time_scale = htons(entry->rate.duration.time_scale);
548  ie->entry.rate.duration.granularity_time = htonl(entry->rate.duration.granularity_time);
549  ie->entry.rate.duration.granularity_time_scale = htons(entry->rate.duration.granularity_time_scale);
550  ie->entry.rate.duration.charging_type = entry->rate.duration.charging_type; /* only one byte */
551 
552  if (!ast_strlen_zero(entry->rate.duration.currency_name)) {
553  ast_copy_string(ie->entry.rate.duration.currency_name,
554  entry->rate.duration.currency_name,
555  sizeof(ie->entry.rate.duration.currency_name));
556  }
557  break;
558  case AST_AOC_RATE_TYPE_FLAT:
559  ie->entry.rate.flat.multiplier = htons(entry->rate.flat.multiplier);
560  ie->entry.rate.flat.amount = htonl(entry->rate.flat.amount);
561  if (!ast_strlen_zero(entry->rate.flat.currency_name)) {
562  ast_copy_string(ie->entry.rate.flat.currency_name,
563  entry->rate.flat.currency_name,
564  sizeof(ie->entry.rate.flat.currency_name));
565  }
566  break;
567  case AST_AOC_RATE_TYPE_VOLUME:
568  ie->entry.rate.volume.multiplier = htons(entry->rate.volume.multiplier);
569  ie->entry.rate.volume.amount = htonl(entry->rate.volume.amount);
570  ie->entry.rate.volume.volume_unit = htons(entry->rate.volume.volume_unit);
571  if (!ast_strlen_zero(entry->rate.volume.currency_name)) {
572  ast_copy_string(ie->entry.rate.volume.currency_name,
573  entry->rate.volume.currency_name,
574  sizeof(ie->entry.rate.volume.currency_name));
575  }
576  break;
577  case AST_AOC_RATE_TYPE_SPECIAL_CODE:
578  ie->entry.rate.special_code = htons(entry->rate.special_code);
579  break;
580  }
581 
582 }
583 static void aoc_create_ie_data(struct ast_aoc_decoded *decoded, struct aoc_ie_data *ied)
584 {
585  ied->pos = 0;
586 
587  if (decoded->currency_amount) {
588  struct aoc_ie_currency ie = {
589  .amount = htonl(decoded->currency_amount),
590  .multiplier = decoded->multiplier, /* only one byte */
591  .name = { 0, },
592  };
593 
594  if (!ast_strlen_zero(decoded->currency_name)) {
595  ast_copy_string(ie.name, decoded->currency_name, sizeof(ie.name));
596  }
597 
598  aoc_append_ie(ied, AOC_IE_CURRENCY, (const void *) &ie, sizeof(ie));
599  }
600 
601  if (decoded->unit_count) {
602  struct aoc_ie_unit ie = { 0 };
603  int i;
604 
605  for (i = 0; i < decoded->unit_count; i++) {
606  ie.valid_amount = decoded->unit_list[i].valid_amount; /* only one byte */
607  ie.amount = htonl(decoded->unit_list[i].amount);
608  ie.valid_type = decoded->unit_list[i].valid_type; /* only one byte */
609  ie.type = decoded->unit_list[i].type; /* only one byte */
610  aoc_append_ie(ied, AOC_IE_UNIT, (const void *) &ie, sizeof(ie));
611  }
612  }
613 
614  if (decoded->billing_id) {
615  struct aoc_ie_billing ie;
616  ie.id = decoded->billing_id; /* only one byte */
617  aoc_append_ie(ied, AOC_IE_BILLING, (const void *) &ie, sizeof(ie));
618  }
619 
620  if (decoded->charging_association.charging_type != AST_AOC_CHARGING_ASSOCIATION_NA) {
621  struct aoc_ie_charging_association ie;
622  memset(&ie, 0, sizeof(ie));
623  ie.ca.charging_type = decoded->charging_association.charging_type; /* only one byte */
624  if (decoded->charging_association.charging_type == AST_AOC_CHARGING_ASSOCIATION_NUMBER) {
625  ie.ca.charge.number.plan = decoded->charging_association.charge.number.plan; /* only one byte */
626  ast_copy_string(ie.ca.charge.number.number,
627  decoded->charging_association.charge.number.number,
628  sizeof(ie.ca.charge.number.number));
629  } else if (decoded->charging_association.charging_type == AST_AOC_CHARGING_ASSOCIATION_ID) {
630  ie.ca.charge.id = htonl(decoded->charging_association.charge.id);
631  }
632  aoc_append_ie(ied, AOC_IE_CHARGING_ASSOCIATION, (const void *) &ie, sizeof(ie));
633  }
634 
635  if (decoded->aoc_s_count) {
636  struct aoc_ie_charging_rate ie;
637  int i;
638  for (i = 0; i < decoded->aoc_s_count; i++) {
639  memset(&ie, 0, sizeof(ie));
640  aoc_create_ie_data_charging_rate(&decoded->aoc_s_entries[i], &ie);
641  aoc_append_ie(ied, AOC_IE_RATE, (const void *) &ie, sizeof(ie));
642  }
643  }
644 
645  if (decoded->termination_request) {
646  aoc_append_ie(ied, AOC_IE_TERMINATION_REQUEST, NULL, 0);
647  }
648 }
649 
650 struct ast_aoc_encoded *ast_aoc_encode(struct ast_aoc_decoded *decoded, size_t *out_size, struct ast_channel *chan)
651 {
652  struct aoc_ie_data ied;
653  struct ast_aoc_encoded *encoded = NULL;
654  size_t size = 0;
655 
656  if (!decoded || !out_size) {
657  return NULL;
658  }
659 
660  *out_size = 0;
661 
662  /* create information element buffer before allocating the payload,
663  * by doing this the exact size of the payload + the id data can be
664  * allocated all at once. */
665  aoc_create_ie_data(decoded, &ied);
666 
667  size = sizeof(struct ast_aoc_encoded) + ied.pos;
668 
669  if (!(encoded = ast_calloc(1, size))) {
670  ast_log(LOG_WARNING, "Failed to create ast_aoc_encoded object during decode routine. \n");
671  return NULL;
672  }
673 
674  /* -- Set ie data buffer */
675  if (ied.pos) {
676  /* this is safe because encoded was allocated to fit this perfectly */
677  memcpy(encoded->data, ied.buf, ied.pos);
678  encoded->datalen = htons(ied.pos);
679  }
680 
681  /* --- Set Flags --- */
682  switch (decoded->msg_type) {
683  case AST_AOC_S:
684  encoded->flags = AST_AOC_ENCODED_TYPE_S;
685  break;
686  case AST_AOC_D:
687  encoded->flags = AST_AOC_ENCODED_TYPE_D;
688  break;
689  case AST_AOC_E:
690  encoded->flags = AST_AOC_ENCODED_TYPE_E;
691  break;
692  case AST_AOC_REQUEST:
693  encoded->flags = AST_AOC_ENCODED_TYPE_REQUEST;
694  default:
695  break;
696  }
697 
698  /* if it is type request, set the types requested, else set charge type */
699  if (decoded->msg_type == AST_AOC_REQUEST) {
700  if (decoded->request_flag & AST_AOC_REQUEST_S) {
701  encoded->flags |= AST_AOC_ENCODED_REQUEST_S;
702  }
703  if (decoded->request_flag & AST_AOC_REQUEST_D) {
704  encoded->flags |= AST_AOC_ENCODED_REQUEST_D;
705  }
706  if (decoded->request_flag & AST_AOC_REQUEST_E) {
707  encoded->flags |= AST_AOC_ENCODED_REQUEST_E;
708  }
709  } else if ((decoded->msg_type == AST_AOC_D) || (decoded->msg_type == AST_AOC_E)) {
710  switch (decoded->charge_type) {
711  case AST_AOC_CHARGE_UNIT:
712  encoded->flags |= AST_AOC_ENCODED_CHARGE_UNIT;
713  break;
714  case AST_AOC_CHARGE_CURRENCY:
715  encoded->flags |= AST_AOC_ENCODED_CHARGE_CURRENCY;
716  break;
717  case AST_AOC_CHARGE_FREE:
718  encoded->flags |= AST_AOC_ENCODED_CHARGE_FREE;
719  case AST_AOC_CHARGE_NA:
720  default:
721  encoded->flags |= AST_AOC_ENCODED_CHARGE_NA;
722  break;
723  }
724 
725  if (decoded->total_type == AST_AOC_SUBTOTAL) {
726  encoded->flags |= AST_AOC_ENCODED_CHARGE_SUBTOTAL;
727  }
728  }
729 
730  /* --- Set Version Number --- */
731  encoded->version = AST_AOC_ENCODE_VERSION;
732 
733  /* set the output size */
734  *out_size = size;
735 
736  if (aoc_debug_enabled) {
737  aoc_display_decoded_debug(decoded, 0, chan);
738  }
739 
740  return encoded;
741 }
742 
743 static int aoc_s_add_entry(struct ast_aoc_decoded *decoded, struct ast_aoc_s_entry *entry)
744 {
745  if (decoded->aoc_s_count >= ARRAY_LEN(decoded->aoc_s_entries)) {
746  return -1;
747  }
748 
749  decoded->aoc_s_entries[decoded->aoc_s_count] = *entry;
750  decoded->aoc_s_count++;
751 
752  return 0;
753 }
754 
755 
756 unsigned int ast_aoc_s_get_count(struct ast_aoc_decoded *decoded)
757 {
758  return decoded->aoc_s_count;
759 }
760 
761 const struct ast_aoc_s_entry *ast_aoc_s_get_rate_info(struct ast_aoc_decoded *decoded, unsigned int entry_number)
762 {
763  if (entry_number >= decoded->aoc_s_count) {
764  return NULL;
765  }
766 
767  return (const struct ast_aoc_s_entry *) &decoded->aoc_s_entries[entry_number];
768 }
769 
771  enum ast_aoc_s_charged_item charged_item,
772  unsigned int amount,
773  enum ast_aoc_currency_multiplier multiplier,
774  const char *currency_name,
775  unsigned long time,
776  enum ast_aoc_time_scale time_scale,
777  unsigned long granularity_time,
778  enum ast_aoc_time_scale granularity_time_scale,
779  int step_function)
780 {
781 
782  struct ast_aoc_s_entry entry = { 0, };
783 
784  entry.charged_item = charged_item;
785  entry.rate_type = AST_AOC_RATE_TYPE_DURATION;
786  entry.rate.duration.amount = amount;
787  entry.rate.duration.multiplier = multiplier;
788  entry.rate.duration.time = time;
789  entry.rate.duration.time_scale = time_scale;
790  entry.rate.duration.granularity_time = granularity_time;
791  entry.rate.duration.granularity_time_scale = granularity_time_scale;
792  entry.rate.duration.charging_type = step_function ? 1 : 0;
793 
794  if (!ast_strlen_zero(currency_name)) {
795  ast_copy_string(entry.rate.duration.currency_name, currency_name, sizeof(entry.rate.duration.currency_name));
796  }
797 
798  return aoc_s_add_entry(decoded, &entry);
799 }
800 
802  enum ast_aoc_s_charged_item charged_item,
803  unsigned int amount,
804  enum ast_aoc_currency_multiplier multiplier,
805  const char *currency_name)
806 {
807  struct ast_aoc_s_entry entry = { 0, };
808 
809  entry.charged_item = charged_item;
810  entry.rate_type = AST_AOC_RATE_TYPE_FLAT;
811  entry.rate.flat.amount = amount;
812  entry.rate.flat.multiplier = multiplier;
813 
814  if (!ast_strlen_zero(currency_name)) {
815  ast_copy_string(entry.rate.flat.currency_name, currency_name, sizeof(entry.rate.flat.currency_name));
816  }
817 
818  return aoc_s_add_entry(decoded, &entry);
819 }
820 
821 
823  enum ast_aoc_s_charged_item charged_item,
824  enum ast_aoc_volume_unit volume_unit,
825  unsigned int amount,
826  enum ast_aoc_currency_multiplier multiplier,
827  const char *currency_name)
828 {
829  struct ast_aoc_s_entry entry = { 0, };
830 
831  entry.charged_item = charged_item;
832  entry.rate_type = AST_AOC_RATE_TYPE_VOLUME;
833  entry.rate.volume.multiplier = multiplier;
834  entry.rate.volume.amount = amount;
835  entry.rate.volume.volume_unit = volume_unit;
836 
837  if (!ast_strlen_zero(currency_name)) {
838  ast_copy_string(entry.rate.volume.currency_name, currency_name, sizeof(entry.rate.volume.currency_name));
839  }
840 
841  return aoc_s_add_entry(decoded, &entry);
842 }
843 
845  enum ast_aoc_s_charged_item charged_item,
846  unsigned int code)
847 {
848  struct ast_aoc_s_entry entry = { 0, };
849 
850  entry.charged_item = charged_item;
851  entry.rate_type = AST_AOC_RATE_TYPE_SPECIAL_CODE;
852  entry.rate.special_code = code;
853 
854  return aoc_s_add_entry(decoded, &entry);
855 }
856 
858  enum ast_aoc_s_charged_item charged_item,
859  int from_beginning)
860 {
861  struct ast_aoc_s_entry entry = { 0, };
862 
863  entry.charged_item = charged_item;
864  entry.rate_type = from_beginning ? AST_AOC_RATE_TYPE_FREE_FROM_BEGINNING : AST_AOC_RATE_TYPE_FREE;
865 
866  return aoc_s_add_entry(decoded, &entry);
867 }
868 
870  enum ast_aoc_s_charged_item charged_item)
871 {
872  struct ast_aoc_s_entry entry = { 0, };
873 
874  entry.charged_item = charged_item;
875  entry.rate_type = AST_AOC_RATE_TYPE_NA;
876 
877  return aoc_s_add_entry(decoded, &entry);
878 }
879 
881  unsigned int code)
882 {
883  struct ast_aoc_s_entry entry = { 0, };
884 
885  entry.charged_item = AST_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT;
886  entry.rate_type = AST_AOC_RATE_TYPE_SPECIAL_CODE;
887  entry.rate.special_code = code;
888 
889  return aoc_s_add_entry(decoded, &entry);
890 }
891 
892 enum ast_aoc_type ast_aoc_get_msg_type(struct ast_aoc_decoded *decoded)
893 {
894  return decoded->msg_type;
895 }
896 
897 enum ast_aoc_charge_type ast_aoc_get_charge_type(struct ast_aoc_decoded *decoded)
898 {
899  return decoded->charge_type;
900 }
901 
902 enum ast_aoc_request ast_aoc_get_request(struct ast_aoc_decoded *decoded)
903 {
904  return decoded->request_flag;
905 }
906 
908  const enum ast_aoc_total_type type)
909 {
910  decoded->total_type = type;
911  return 0;
912 }
913 
914 enum ast_aoc_total_type ast_aoc_get_total_type(struct ast_aoc_decoded *decoded)
915 {
916  return decoded->total_type;
917 }
918 
920  const unsigned int amount,
921  const enum ast_aoc_currency_multiplier multiplier,
922  const char *name)
923 {
924 
925  if (!ast_strlen_zero(name)) {
926  ast_copy_string(decoded->currency_name, name, sizeof(decoded->currency_name));
927  }
928 
929  decoded->currency_amount = amount;
930 
931  if (multiplier && (multiplier < AST_AOC_MULT_NUM_ENTRIES)) {
932  decoded->multiplier = multiplier;
933  } else {
934  decoded->multiplier = AST_AOC_MULT_ONE;
935  }
936 
937  return 0;
938 }
939 
940 unsigned int ast_aoc_get_currency_amount(struct ast_aoc_decoded *decoded)
941 {
942  return decoded->currency_amount;
943 }
944 
946 {
947  return decoded->multiplier;
948 }
949 
951 {
952  switch (decoded->multiplier) {
953  case AST_AOC_MULT_ONETHOUSANDTH:
954  return "0.001";
955  case AST_AOC_MULT_ONEHUNDREDTH:
956  return "0.01";
957  case AST_AOC_MULT_ONETENTH:
958  return "0.1";
959  case AST_AOC_MULT_ONE:
960  return "1.0";
961  case AST_AOC_MULT_TEN:
962  return "10.0";
963  case AST_AOC_MULT_HUNDRED:
964  return "100.0";
965  case AST_AOC_MULT_THOUSAND:
966  return "1000.0";
967  default:
968  return "1.0";
969  }
970 }
971 
972 const char *ast_aoc_get_currency_name(struct ast_aoc_decoded *decoded)
973 {
974  return decoded->currency_name;
975 }
976 
978  const unsigned int amount_is_present,
979  const unsigned int amount,
980  const unsigned int type_is_present,
981  const unsigned int type)
982 {
983  if ((decoded->msg_type == AST_AOC_REQUEST) ||
984  (decoded->unit_count >= ARRAY_LEN(decoded->unit_list))) {
985  return -1;
986  }
987 
988  if (!amount_is_present && !type_is_present) {
989  return -1;
990  }
991 
992  decoded->unit_list[decoded->unit_count].valid_amount = amount_is_present;
993  if (amount_is_present) {
994  decoded->unit_list[decoded->unit_count].amount = amount;
995  } else {
996  decoded->unit_list[decoded->unit_count].amount = 0;
997  }
998 
999  decoded->unit_list[decoded->unit_count].valid_type = type_is_present;
1000  if (type_is_present) {
1001  decoded->unit_list[decoded->unit_count].type = type;
1002  } else {
1003  decoded->unit_list[decoded->unit_count].type = 0;
1004  }
1005  decoded->unit_count++;
1006 
1007  return 0;
1008 }
1009 
1010 const struct ast_aoc_unit_entry *ast_aoc_get_unit_info(struct ast_aoc_decoded *decoded, unsigned int entry_number)
1011 {
1012  if (entry_number >= decoded->unit_count) {
1013  return NULL;
1014  }
1015 
1016  return (const struct ast_aoc_unit_entry *) &decoded->unit_list[entry_number];
1017 }
1018 
1019 unsigned int ast_aoc_get_unit_count(struct ast_aoc_decoded *decoded)
1020 {
1021  return decoded->unit_count;
1022 }
1023 
1024 int ast_aoc_set_billing_id(struct ast_aoc_decoded *decoded, const enum ast_aoc_billing_id id)
1025 {
1026  if ((id >= AST_AOC_BILLING_NUM_ENTRIES) || (id < AST_AOC_BILLING_NA)) {
1027  return -1;
1028  }
1029 
1030  decoded->billing_id = id;
1031 
1032  return 0;
1033 }
1034 
1036 {
1037  return decoded->billing_id;
1038 }
1039 
1040 int ast_aoc_set_association_id(struct ast_aoc_decoded *decoded, const int id)
1041 {
1042  if (decoded->msg_type != AST_AOC_E) {
1043  return -1;
1044  }
1045  memset(&decoded->charging_association, 0, sizeof(decoded->charging_association));
1046  decoded->charging_association.charging_type = AST_AOC_CHARGING_ASSOCIATION_ID;
1047  decoded->charging_association.charge.id = id;
1048  return 0;
1049 }
1050 
1052 {
1053  return &decoded->charging_association;
1054 }
1055 
1056 int ast_aoc_set_association_number(struct ast_aoc_decoded *decoded, const char *num, uint8_t plan)
1057 {
1058  if ((decoded->msg_type != AST_AOC_E) || ast_strlen_zero(num)) {
1059  return -1;
1060  }
1061  memset(&decoded->charging_association, 0, sizeof(decoded->charging_association));
1062  decoded->charging_association.charging_type = AST_AOC_CHARGING_ASSOCIATION_NUMBER;
1063  decoded->charging_association.charge.number.plan = plan;
1064  ast_copy_string(decoded->charging_association.charge.number.number, num, sizeof(decoded->charging_association.charge.number.number));
1065 
1066  return 0;
1067 }
1068 
1070 {
1071  if (decoded->msg_type != AST_AOC_REQUEST) {
1072  return -1;
1073  }
1074  decoded->termination_request = 1;
1075 
1076  return 0;
1077 }
1078 
1080 {
1081  return decoded->termination_request;
1082 }
1083 
1084 /*!
1085  * \internal
1086  * \brief Convert AST_AOC_VOLUME_UNIT to string.
1087  * \since 1.8
1088  *
1089  * \param value Value to convert to string.
1090  *
1091  * \return String equivalent.
1092  */
1093 static const char *aoc_volume_unit_str(enum ast_aoc_volume_unit value)
1094 {
1095  const char *str;
1096 
1097  switch (value) {
1098  default:
1099  case AST_AOC_VOLUME_UNIT_OCTET:
1100  str = "Octet";
1101  break;
1102  case AST_AOC_VOLUME_UNIT_SEGMENT:
1103  str = "Segment";
1104  break;
1105  case AST_AOC_VOLUME_UNIT_MESSAGE:
1106  str = "Message";
1107  break;
1108  }
1109  return str;
1110 }
1111 
1112 /*!
1113  * \internal
1114  * \brief Convert ast_aoc_charged_item to string.
1115  * \since 1.8
1116  *
1117  * \param value Value to convert to string.
1118  *
1119  * \return String equivalent.
1120  */
1121 static const char *aoc_charged_item_str(enum ast_aoc_s_charged_item value)
1122 {
1123  const char *str;
1124 
1125  switch (value) {
1126  default:
1127  case AST_AOC_CHARGED_ITEM_NA:
1128  str = "NotAvailable";
1129  break;
1130  case AST_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT:
1131  str = "SpecialArrangement";
1132  break;
1133  case AST_AOC_CHARGED_ITEM_BASIC_COMMUNICATION:
1134  str = "BasicCommunication";
1135  break;
1136  case AST_AOC_CHARGED_ITEM_CALL_ATTEMPT:
1137  str = "CallAttempt";
1138  break;
1139  case AST_AOC_CHARGED_ITEM_CALL_SETUP:
1140  str = "CallSetup";
1141  break;
1142  case AST_AOC_CHARGED_ITEM_USER_USER_INFO:
1143  str = "UserUserInfo";
1144  break;
1145  case AST_AOC_CHARGED_ITEM_SUPPLEMENTARY_SERVICE:
1146  str = "SupplementaryService";
1147  break;
1148  }
1149  return str;
1150 }
1151 
1152 /*!
1153  * \internal
1154  * \brief Convert ast_aoc_total_type to string.
1155  * \since 1.8
1156  *
1157  * \param value Value to convert to string.
1158  *
1159  * \return String equivalent.
1160  */
1161 static const char *aoc_type_of_totaling_str(enum ast_aoc_total_type value)
1162 {
1163  const char *str;
1164 
1165  switch (value) {
1166  default:
1167  case AST_AOC_SUBTOTAL:
1168  str = "SubTotal";
1169  break;
1170  case AST_AOC_TOTAL:
1171  str = "Total";
1172  break;
1173  }
1174  return str;
1175 }
1176 
1177 /*!
1178  * \internal
1179  * \brief Convert ast_aoc_rate_type to string.
1180  * \since 1.8
1181  *
1182  * \param value Value to convert to string.
1183  *
1184  * \return String equivalent.
1185  */
1186 static const char *aoc_rate_type_str(enum ast_aoc_s_rate_type value)
1187 {
1188  const char *str;
1189 
1190  switch (value) {
1191  default:
1192  case AST_AOC_RATE_TYPE_NA:
1193  str = "NotAvailable";
1194  break;
1195  case AST_AOC_RATE_TYPE_FREE:
1196  str = "Free";
1197  break;
1198  case AST_AOC_RATE_TYPE_FREE_FROM_BEGINNING:
1199  str = "FreeFromBeginning";
1200  break;
1201  case AST_AOC_RATE_TYPE_DURATION:
1202  str = "Duration";
1203  break;
1204  case AST_AOC_RATE_TYPE_FLAT:
1205  str = "Flat";
1206  break;
1207  case AST_AOC_RATE_TYPE_VOLUME:
1208  str = "Volume";
1209  break;
1210  case AST_AOC_RATE_TYPE_SPECIAL_CODE:
1211  str = "SpecialCode";
1212  break;
1213  }
1214  return str;
1215 }
1216 
1217 /*!
1218  * \internal
1219  * \brief Convert AST_AOC_TIME_SCALE to string.
1220  * \since 1.8
1221  *
1222  * \param value Value to convert to string.
1223  *
1224  * \return String equivalent.
1225  */
1226 static const char *aoc_scale_str(enum ast_aoc_time_scale value)
1227 {
1228  const char *str;
1229 
1230  switch (value) {
1231  default:
1232  case AST_AOC_TIME_SCALE_HUNDREDTH_SECOND:
1233  str = "OneHundredthSecond";
1234  break;
1235  case AST_AOC_TIME_SCALE_TENTH_SECOND:
1236  str = "OneTenthSecond";
1237  break;
1238  case AST_AOC_TIME_SCALE_SECOND:
1239  str = "Second";
1240  break;
1241  case AST_AOC_TIME_SCALE_TEN_SECOND:
1242  str = "TenSeconds";
1243  break;
1244  case AST_AOC_TIME_SCALE_MINUTE:
1245  str = "Minute";
1246  break;
1247  case AST_AOC_TIME_SCALE_HOUR:
1248  str = "Hour";
1249  break;
1250  case AST_AOC_TIME_SCALE_DAY:
1251  str = "Day";
1252  break;
1253  }
1254  return str;
1255 }
1256 
1257 static const char *aoc_charge_type_str(enum ast_aoc_charge_type value)
1258 {
1259  const char *str;
1260 
1261  switch (value) {
1262  default:
1263  case AST_AOC_CHARGE_NA:
1264  str = "NotAvailable";
1265  break;
1266  case AST_AOC_CHARGE_FREE:
1267  str = "Free";
1268  break;
1269  case AST_AOC_CHARGE_CURRENCY:
1270  str = "Currency";
1271  break;
1272  case AST_AOC_CHARGE_UNIT:
1273  str = "Units";
1274  break;
1275  }
1276 
1277  return str;
1278 }
1279 
1280 static const char *aoc_multiplier_str(enum ast_aoc_currency_multiplier mult)
1281 {
1282  switch (mult) {
1283  case AST_AOC_MULT_ONETHOUSANDTH:
1284  return "1/1000";
1285  case AST_AOC_MULT_ONEHUNDREDTH:
1286  return "1/100";
1287  case AST_AOC_MULT_ONETENTH:
1288  return "1/10";
1289  case AST_AOC_MULT_ONE:
1290  return "1";
1291  case AST_AOC_MULT_TEN:
1292  return "10";
1293  case AST_AOC_MULT_HUNDRED:
1294  return "100";
1295  case AST_AOC_MULT_THOUSAND:
1296  return "1000";
1297  case AST_AOC_MULT_NUM_ENTRIES:
1298  break;
1299  }
1300  return "1";
1301 }
1302 
1303 static const char *aoc_billingid_str(enum ast_aoc_billing_id billing_id)
1304 {
1305  switch (billing_id) {
1306  case AST_AOC_BILLING_NORMAL:
1307  return "Normal";
1308  case AST_AOC_BILLING_REVERSE_CHARGE:
1309  return "Reverse";
1310  case AST_AOC_BILLING_CREDIT_CARD:
1311  return "CreditCard";
1312  case AST_AOC_BILLING_CALL_FWD_UNCONDITIONAL:
1313  return "CallForwardingUnconditional";
1314  case AST_AOC_BILLING_CALL_FWD_BUSY:
1315  return "CallForwardingBusy";
1316  case AST_AOC_BILLING_CALL_FWD_NO_REPLY:
1317  return "CallForwardingNoReply";
1318  case AST_AOC_BILLING_CALL_DEFLECTION:
1319  return "CallDeflection";
1320  case AST_AOC_BILLING_CALL_TRANSFER:
1321  return "CallTransfer";
1322  case AST_AOC_BILLING_NA:
1323  return "NotAvailable";
1324  case AST_AOC_BILLING_NUM_ENTRIES:
1325  break;
1326  }
1327  return "NotAvailable";
1328 }
1329 
1331 {
1332  struct ast_aoc_decoded *new_decoded = NULL;
1333  struct ast_aoc_encoded *encoded = NULL;
1334  size_t size;
1335  int res = 0;
1336 
1337  if (!(encoded = ast_aoc_encode(decoded, &size, NULL))) {
1338  return -1;
1339  }
1340 
1341  if (!(new_decoded = ast_aoc_decode(encoded, size, NULL))) {
1342  ast_free(encoded);
1343  return -1;
1344  }
1345 
1346  if (memcmp(new_decoded, decoded, sizeof(struct ast_aoc_decoded))) {
1347  res = -1;
1348  }
1349 
1350  ast_aoc_destroy_decoded(new_decoded);
1351  ast_aoc_destroy_encoded(encoded);
1352  return res;
1353 }
1354 
1355 static char *aoc_cli_debug_enable(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1356 {
1357  switch (cmd) {
1358  case CLI_INIT:
1359  e->command = "aoc set debug";
1360  e->usage =
1361  "Usage: 'aoc set debug on' to enable aoc debug, 'aoc set debug off' to disable debug.\n";
1362  return NULL;
1363  case CLI_GENERATE:
1364  return NULL;
1365  case CLI_HANDLER:
1366  if (a->argc != 4) {
1367  return CLI_SHOWUSAGE;
1368  } else if(ast_true(a->argv[3])) {
1369  ast_cli(a->fd, "aoc debug enabled\n");
1370  aoc_debug_enabled = 1;
1371  } else if (ast_false(a->argv[3])) {
1372  ast_cli(a->fd, "aoc debug disabled\n");
1373  aoc_debug_enabled = 0;
1374  } else {
1375  return CLI_SHOWUSAGE;
1376  }
1377  }
1378 
1379  return CLI_SUCCESS;
1380 }
1381 
1382 /*!
1383  * \internal
1384  * \brief Append the time structure to the event message string.
1385  * \since 1.8
1386  *
1387  * \param msg Event message string being built.
1388  * \param prefix Prefix to add to the amount lines.
1389  * \param name Name of the time structure to convert.
1390  * \param time Data to convert.
1391  * \param scale Data to convert.
1392  */
1393 static void aoc_time_str(struct ast_str **msg, const char *prefix, const char *name, unsigned long time, enum ast_aoc_time_scale scale)
1394 {
1395  ast_str_append(msg, 0, "%s/%s/Length: %lu\r\n", prefix, name, time);
1396  ast_str_append(msg, 0, "%s/%s/Scale: %s\r\n", prefix, name,
1397  aoc_scale_str(scale));
1398 }
1399 
1400 /*!
1401  * \internal
1402  * \brief Append the amount structure to the event message string.
1403  * \since 1.8
1404  *
1405  * \param msg Event message string being built.
1406  * \param prefix Prefix to add to the amount lines.
1407  * \param amount Data to convert.
1408  * \param mult to convert
1409  */
1410 static void aoc_amount_str(struct ast_str **msg, const char *prefix, unsigned int amount, enum ast_aoc_currency_multiplier mult)
1411 {
1412  static const char name[] = "Amount";
1413 
1414  ast_str_append(msg, 0, "%s/%s/Cost: %u\r\n", prefix, name, amount);
1415  ast_str_append(msg, 0, "%s/%s/Multiplier: %s\r\n", prefix, name,
1416  aoc_multiplier_str(mult));
1417 }
1418 
1419 static void aoc_request_event(const struct ast_aoc_decoded *decoded, struct ast_str **msg)
1420 {
1421  if (decoded->request_flag) {
1422  ast_str_append(msg, 0, "AOCRequest:");
1423  if (decoded->request_flag & AST_AOC_REQUEST_S) {
1424  ast_str_append(msg, 0, "S");
1425  }
1426  if (decoded->request_flag & AST_AOC_REQUEST_D) {
1427  ast_str_append(msg, 0, "D");
1428  }
1429  if (decoded->request_flag & AST_AOC_REQUEST_E) {
1430  ast_str_append(msg, 0, "E");
1431  }
1432  ast_str_append(msg, 0, "\r\n");
1433 
1434  } else {
1435  ast_str_append(msg, 0, "AOCRequest: NONE\r\n");
1436  }
1437 }
1438 
1439 static void aoc_s_event(const struct ast_aoc_decoded *decoded, struct ast_str **msg)
1440 {
1441  const char *rate_str;
1442  char prefix[32];
1443  int idx;
1444 
1445  ast_str_append(msg, 0, "NumberRates: %d\r\n", decoded->aoc_s_count);
1446  for (idx = 0; idx < decoded->aoc_s_count; ++idx) {
1447  snprintf(prefix, sizeof(prefix), "Rate(%d)", idx);
1448 
1449  ast_str_append(msg, 0, "%s/Chargeable: %s\r\n", prefix,
1450  aoc_charged_item_str(decoded->aoc_s_entries[idx].charged_item));
1451  if (decoded->aoc_s_entries[idx].charged_item == AST_AOC_CHARGED_ITEM_NA) {
1452  continue;
1453  }
1454  rate_str = aoc_rate_type_str(decoded->aoc_s_entries[idx].rate_type);
1455  ast_str_append(msg, 0, "%s/Type: %s\r\n", prefix, rate_str);
1456  switch (decoded->aoc_s_entries[idx].rate_type) {
1457  case AST_AOC_RATE_TYPE_DURATION:
1458  strcat(prefix, "/");
1459  strcat(prefix, rate_str);
1460  ast_str_append(msg, 0, "%s/Currency: %s\r\n", prefix,
1461  decoded->aoc_s_entries[idx].rate.duration.currency_name);
1462  aoc_amount_str(msg, prefix,
1463  decoded->aoc_s_entries[idx].rate.duration.amount,
1464  decoded->aoc_s_entries[idx].rate.duration.multiplier);
1465  ast_str_append(msg, 0, "%s/ChargingType: %s\r\n", prefix,
1466  decoded->aoc_s_entries[idx].rate.duration.charging_type ?
1467  "StepFunction" : "ContinuousCharging");
1468  aoc_time_str(msg, prefix, "Time",
1469  decoded->aoc_s_entries[idx].rate.duration.time,
1470  decoded->aoc_s_entries[idx].rate.duration.time_scale);
1471  if (decoded->aoc_s_entries[idx].rate.duration.granularity_time) {
1472  aoc_time_str(msg, prefix, "Granularity",
1473  decoded->aoc_s_entries[idx].rate.duration.granularity_time,
1474  decoded->aoc_s_entries[idx].rate.duration.granularity_time_scale);
1475  }
1476  break;
1477  case AST_AOC_RATE_TYPE_FLAT:
1478  strcat(prefix, "/");
1479  strcat(prefix, rate_str);
1480  ast_str_append(msg, 0, "%s/Currency: %s\r\n", prefix,
1481  decoded->aoc_s_entries[idx].rate.flat.currency_name);
1482  aoc_amount_str(msg, prefix,
1483  decoded->aoc_s_entries[idx].rate.flat.amount,
1484  decoded->aoc_s_entries[idx].rate.flat.multiplier);
1485  break;
1486  case AST_AOC_RATE_TYPE_VOLUME:
1487  strcat(prefix, "/");
1488  strcat(prefix, rate_str);
1489  ast_str_append(msg, 0, "%s/Currency: %s\r\n", prefix,
1490  decoded->aoc_s_entries[idx].rate.volume.currency_name);
1491  aoc_amount_str(msg, prefix,
1492  decoded->aoc_s_entries[idx].rate.volume.amount,
1493  decoded->aoc_s_entries[idx].rate.volume.multiplier);
1494  ast_str_append(msg, 0, "%s/Unit: %s\r\n", prefix,
1495  aoc_volume_unit_str(decoded->aoc_s_entries[idx].rate.volume.volume_unit));
1496  break;
1497  case AST_AOC_RATE_TYPE_SPECIAL_CODE:
1498  ast_str_append(msg, 0, "%s/%s: %d\r\n", prefix, rate_str,
1499  decoded->aoc_s_entries[idx].rate.special_code);
1500  break;
1501  default:
1502  break;
1503  }
1504  }
1505 }
1506 
1507 static void aoc_d_event(const struct ast_aoc_decoded *decoded, struct ast_str **msg)
1508 {
1509  const char *charge_str;
1510  int idx;
1511  char prefix[32];
1512 
1513  charge_str = aoc_charge_type_str(decoded->charge_type);
1514  ast_str_append(msg, 0, "Type: %s\r\n", charge_str);
1515 
1516  switch (decoded->charge_type) {
1517  case AST_AOC_CHARGE_CURRENCY:
1518  case AST_AOC_CHARGE_UNIT:
1519  ast_str_append(msg, 0, "BillingID: %s\r\n",
1520  aoc_billingid_str(decoded->billing_id));
1521  ast_str_append(msg, 0, "TypeOfCharging: %s\r\n",
1522  aoc_type_of_totaling_str(decoded->total_type));
1523  break;
1524  default:
1525  break;
1526  }
1527 
1528  switch (decoded->charge_type) {
1529  case AST_AOC_CHARGE_CURRENCY:
1530  ast_str_append(msg, 0, "%s: %s\r\n", charge_str,
1531  decoded->currency_name);
1532  aoc_amount_str(msg, charge_str,
1533  decoded->currency_amount,
1534  decoded->multiplier);
1535  break;
1536  case AST_AOC_CHARGE_UNIT:
1537  ast_str_append(msg, 0, "%s/NumberItems: %d\r\n", charge_str,
1538  decoded->unit_count);
1539  for (idx = 0; idx < decoded->unit_count; ++idx) {
1540  snprintf(prefix, sizeof(prefix), "%s/Item(%d)", charge_str, idx);
1541  if (decoded->unit_list[idx].valid_amount) {
1542  ast_str_append(msg, 0, "%s/NumberOf: %u\r\n", prefix,
1543  decoded->unit_list[idx].amount);
1544  }
1545  if (decoded->unit_list[idx].valid_type) {
1546  ast_str_append(msg, 0, "%s/TypeOf: %u\r\n", prefix,
1547  decoded->unit_list[idx].type);
1548  }
1549  }
1550  break;
1551  default:
1552  break;
1553  }
1554 }
1555 
1556 static void aoc_e_event(const struct ast_aoc_decoded *decoded, struct ast_str **msg)
1557 {
1558  const char *charge_str;
1559  int idx;
1560  char prefix[32];
1561 
1562  charge_str = "ChargingAssociation";
1563 
1564  switch (decoded->charging_association.charging_type) {
1565  case AST_AOC_CHARGING_ASSOCIATION_NUMBER:
1566  snprintf(prefix, sizeof(prefix), "%s/Number", charge_str);
1567  ast_str_append(msg, 0, "%s: %s\r\n", prefix,
1568  decoded->charging_association.charge.number.number);
1569  ast_str_append(msg, 0, "%s/Plan: %d\r\n", prefix,
1570  decoded->charging_association.charge.number.plan);
1571  break;
1572  case AST_AOC_CHARGING_ASSOCIATION_ID:
1573  ast_str_append(msg, 0, "%s/ID: %d\r\n", charge_str, decoded->charging_association.charge.id);
1574  break;
1575  case AST_AOC_CHARGING_ASSOCIATION_NA:
1576  default:
1577  break;
1578  }
1579 
1580  charge_str = aoc_charge_type_str(decoded->charge_type);
1581  ast_str_append(msg, 0, "Type: %s\r\n", charge_str);
1582  switch (decoded->charge_type) {
1583  case AST_AOC_CHARGE_CURRENCY:
1584  case AST_AOC_CHARGE_UNIT:
1585  ast_str_append(msg, 0, "BillingID: %s\r\n",
1586  aoc_billingid_str(decoded->billing_id));
1587  break;
1588  default:
1589  break;
1590  }
1591  switch (decoded->charge_type) {
1592  case AST_AOC_CHARGE_CURRENCY:
1593  ast_str_append(msg, 0, "%s: %s\r\n", charge_str,
1594  decoded->currency_name);
1595  aoc_amount_str(msg, charge_str,
1596  decoded->currency_amount,
1597  decoded->multiplier);
1598  break;
1599  case AST_AOC_CHARGE_UNIT:
1600  ast_str_append(msg, 0, "%s/NumberItems: %d\r\n", charge_str,
1601  decoded->unit_count);
1602  for (idx = 0; idx < decoded->unit_count; ++idx) {
1603  snprintf(prefix, sizeof(prefix), "%s/Item(%d)", charge_str, idx);
1604  if (decoded->unit_list[idx].valid_amount) {
1605  ast_str_append(msg, 0, "%s/NumberOf: %u\r\n", prefix,
1606  decoded->unit_list[idx].amount);
1607  }
1608  if (decoded->unit_list[idx].valid_type) {
1609  ast_str_append(msg, 0, "%s/TypeOf: %u\r\n", prefix,
1610  decoded->unit_list[idx].type);
1611  }
1612  }
1613  break;
1614  default:
1615  break;
1616  }
1617 }
1618 
1619 static struct ast_json *units_to_json(const struct ast_aoc_decoded *decoded)
1620 {
1621  int i;
1622  struct ast_json *units = ast_json_array_create();
1623 
1624  if (!units) {
1625  return ast_json_null();
1626  }
1627 
1628  for (i = 0; i < decoded->unit_count; ++i) {
1629  struct ast_json *unit = ast_json_object_create();
1630 
1631  if (decoded->unit_list[i].valid_amount) {
1633  unit, "NumberOf", ast_json_stringf(
1634  "%u", decoded->unit_list[i].amount));
1635  }
1636 
1637  if (decoded->unit_list[i].valid_type) {
1639  unit, "TypeOf", ast_json_stringf(
1640  "%u", decoded->unit_list[i].type));
1641  }
1642 
1643  if (ast_json_array_append(units, unit)) {
1644  break;
1645  }
1646  }
1647 
1648  return units;
1649 }
1650 
1651 static struct ast_json *currency_to_json(const char *name, int cost,
1652  enum ast_aoc_currency_multiplier mult)
1653 {
1654  return ast_json_pack("{s:s, s:i, s:s}",
1655  "Name", AST_JSON_UTF8_VALIDATE(name),
1656  "Cost", cost,
1657  "Multiplier", aoc_multiplier_str(mult));
1658 }
1659 
1660 static struct ast_json *charge_to_json(const struct ast_aoc_decoded *decoded)
1661 {
1662  struct ast_json *obj;
1663  const char *obj_type;
1664 
1665  if (decoded->charge_type != AST_AOC_CHARGE_CURRENCY &&
1666  decoded->charge_type != AST_AOC_CHARGE_UNIT) {
1667  return ast_json_pack("{s:s}",
1668  "Type", aoc_charge_type_str(decoded->charge_type));
1669  }
1670 
1671  if (decoded->charge_type == AST_AOC_CHARGE_CURRENCY) {
1672  obj_type = "Currency";
1673  obj = currency_to_json(decoded->currency_name, decoded->currency_amount,
1674  decoded->multiplier);
1675  } else { /* decoded->charge_type == AST_AOC_CHARGE_UNIT */
1676  obj_type = "Units";
1677  obj = units_to_json(decoded);
1678  }
1679 
1680  return ast_json_pack("{s:s, s:s, s:s, s:o}",
1681  "Type", aoc_charge_type_str(decoded->charge_type),
1682  "BillingID", aoc_billingid_str(decoded->billing_id),
1683  "TotalType", aoc_type_of_totaling_str(decoded->total_type),
1684  obj_type, obj);
1685 }
1686 
1687 static struct ast_json *association_to_json(const struct ast_aoc_decoded *decoded)
1688 {
1689  switch (decoded->charging_association.charging_type) {
1690  case AST_AOC_CHARGING_ASSOCIATION_NUMBER:
1691  return ast_json_pack("{s:s, s:i}",
1692  "Number", AST_JSON_UTF8_VALIDATE(decoded->charging_association.charge.number.number),
1693  "Plan", decoded->charging_association.charge.number.plan);
1694  case AST_AOC_CHARGING_ASSOCIATION_ID:
1695  return ast_json_pack("{s:i}", "ID", decoded->charging_association.charge.id);
1696  case AST_AOC_CHARGING_ASSOCIATION_NA:
1697  default:
1698  return ast_json_null();
1699  }
1700 }
1701 
1702 static struct ast_json *s_to_json(const struct ast_aoc_decoded *decoded)
1703 {
1704  int i;
1705  struct ast_json *rates = ast_json_array_create();
1706 
1707  if (!rates) {
1708  return ast_json_null();
1709  }
1710 
1711  for (i = 0; i < decoded->aoc_s_count; ++i) {
1712  struct ast_json *rate;
1713  struct ast_json *type = NULL;
1714  struct ast_json *currency;
1715  const char *charge_item = aoc_charged_item_str(
1716  decoded->aoc_s_entries[i].charged_item);
1717 
1718  if (decoded->aoc_s_entries[i].charged_item == AST_AOC_CHARGED_ITEM_NA) {
1719  rate = ast_json_pack("{s:s}", "Chargeable", charge_item);
1720  if (ast_json_array_append(rates, rate)) {
1721  break;
1722  }
1723  continue;
1724  }
1725 
1726  switch (decoded->aoc_s_entries[i].rate_type) {
1727  case AST_AOC_RATE_TYPE_DURATION:
1728  {
1729  struct ast_json *time;
1730  struct ast_json *granularity = NULL;
1731 
1732  currency = currency_to_json(
1733  decoded->aoc_s_entries[i].rate.duration.currency_name,
1734  decoded->aoc_s_entries[i].rate.duration.amount,
1735  decoded->aoc_s_entries[i].rate.duration.multiplier);
1736 
1737  time = ast_json_pack("{s:I, s:i}",
1738  "Length", (ast_json_int_t)decoded->aoc_s_entries[i].rate.duration.time,
1739  "Scale", decoded->aoc_s_entries[i].rate.duration.time_scale);
1740 
1741  if (decoded->aoc_s_entries[i].rate.duration.granularity_time) {
1742  granularity = ast_json_pack("{s:I, s:i}",
1743  "Length", (ast_json_int_t)decoded->aoc_s_entries[i].rate.duration.granularity_time,
1744  "Scale", decoded->aoc_s_entries[i].rate.duration.granularity_time_scale);
1745  }
1746 
1747  type = ast_json_pack("{s:o, s:s, s:o, s:o}",
1748  "Currency", currency,
1749  "ChargingType", decoded->aoc_s_entries[i].rate.duration.charging_type
1750  ? "StepFunction" : "ContinuousCharging",
1751  "Time", time,
1752  "Granularity", granularity ?: ast_json_null());
1753 
1754  break;
1755  }
1756  case AST_AOC_RATE_TYPE_FLAT:
1757  currency = currency_to_json(
1758  decoded->aoc_s_entries[i].rate.flat.currency_name,
1759  decoded->aoc_s_entries[i].rate.flat.amount,
1760  decoded->aoc_s_entries[i].rate.flat.multiplier);
1761 
1762  type = ast_json_pack("{s:o}", "Currency", currency);
1763  break;
1764  case AST_AOC_RATE_TYPE_VOLUME:
1765  currency = currency_to_json(
1766  decoded->aoc_s_entries[i].rate.volume.currency_name,
1767  decoded->aoc_s_entries[i].rate.volume.amount,
1768  decoded->aoc_s_entries[i].rate.volume.multiplier);
1769 
1770  type = ast_json_pack("{s:s, s:o}",
1771  "Unit", aoc_volume_unit_str(
1772  decoded->aoc_s_entries[i].rate.volume.volume_unit),
1773  "Currency", currency);
1774  break;
1775  case AST_AOC_RATE_TYPE_SPECIAL_CODE:
1776  type = ast_json_pack("{s:i}",
1777  "SpecialCode", decoded->aoc_s_entries[i].rate.special_code);
1778  break;
1779  default:
1780  break;
1781  }
1782 
1783  rate = ast_json_pack("{s:s, s:o}",
1784  "Chargeable", charge_item,
1785  aoc_rate_type_str(decoded->aoc_s_entries[i].rate_type), type);
1786  if (ast_json_array_append(rates, rate)) {
1787  break;
1788  }
1789  }
1790  return rates;
1791 }
1792 
1793 static struct ast_json *d_to_json(const struct ast_aoc_decoded *decoded)
1794 {
1795  return ast_json_pack("{s:o}", "Charge", charge_to_json(decoded));
1796 }
1797 
1798 static struct ast_json *e_to_json(const struct ast_aoc_decoded *decoded)
1799 {
1800  return ast_json_pack("{s:o, s:o}",
1801  "ChargingAssociation", association_to_json(decoded),
1802  "Charge", charge_to_json(decoded));
1803 }
1804 
1806  /*! Channel AOC event is associated with (NULL for unassociated) */
1808  /*! AOC JSON blob of data */
1809  struct ast_json *blob;
1810 };
1811 
1812 static void aoc_event_blob_dtor(void *obj)
1813 {
1814  struct aoc_event_blob *aoc_event = obj;
1815 
1816  ao2_cleanup(aoc_event->snapshot);
1817  ast_json_unref(aoc_event->blob);
1818 }
1819 
1820 /*!
1821  * \internal
1822  * \brief Publish an AOC event.
1823  * \since 13.3.0
1824  *
1825  * \param chan Channel associated with the AOC event. (May be NULL if no channel)
1826  * \param msg_type What kind of AOC event.
1827  * \param blob AOC data blob to publish.
1828  */
1829 static void aoc_publish_blob(struct ast_channel *chan, struct stasis_message_type *msg_type, struct ast_json *blob)
1830 {
1831  struct stasis_message *msg;
1832  struct aoc_event_blob *aoc_event;
1833 
1834  if (!blob || ast_json_is_null(blob)) {
1835  /* No AOC blob information? Nothing to send an event about. */
1836  return;
1837  }
1838 
1839  aoc_event = ao2_alloc_options(sizeof(*aoc_event), aoc_event_blob_dtor,
1841  if (!aoc_event) {
1842  return;
1843  }
1844 
1845  if (chan) {
1846  ast_channel_lock(chan);
1847  aoc_event->snapshot = ao2_bump(ast_channel_snapshot(chan));
1848  ast_channel_unlock(chan);
1849  if (!aoc_event->snapshot) {
1850  ao2_ref(aoc_event, -1);
1851  return;
1852  }
1853  }
1854  aoc_event->blob = ast_json_ref(blob);
1855 
1856  msg = stasis_message_create(msg_type, aoc_event);
1857  ao2_ref(aoc_event, -1);
1858 
1859  if (msg) {
1861  ao2_ref(msg, -1);
1862  }
1863 }
1864 
1865 static struct ast_manager_event_blob *aoc_to_ami(struct stasis_message *message,
1866  const char *event_name)
1867 {
1868  struct aoc_event_blob *aoc_event = stasis_message_data(message);
1869  struct ast_str *channel = NULL;
1870  struct ast_str *aoc;
1871  struct ast_manager_event_blob *ev = NULL;
1872 
1873  if (aoc_event->snapshot) {
1874  channel = ast_manager_build_channel_state_string(aoc_event->snapshot);
1875  if (!channel) {
1876  return NULL;
1877  }
1878  }
1879 
1880  aoc = ast_manager_str_from_json_object(aoc_event->blob, NULL);
1881  if (aoc && !ast_strlen_zero(ast_str_buffer(aoc))) {
1882  ev = ast_manager_event_blob_create(EVENT_FLAG_AOC, event_name, "%s%s",
1883  AS_OR(channel, ""), ast_str_buffer(aoc));
1884  }
1885 
1886  ast_free(aoc);
1887  ast_free(channel);
1888  return ev;
1889 }
1890 
1891 static struct ast_manager_event_blob *aoc_s_to_ami(struct stasis_message *message)
1892 {
1893  return aoc_to_ami(message, "AOC-S");
1894 }
1895 
1896 static struct ast_manager_event_blob *aoc_d_to_ami(struct stasis_message *message)
1897 {
1898  return aoc_to_ami(message, "AOC-D");
1899 }
1900 
1901 static struct ast_manager_event_blob *aoc_e_to_ami(struct stasis_message *message)
1902 {
1903  return aoc_to_ami(message, "AOC-E");
1904 }
1905 
1906 struct stasis_message_type *aoc_s_type(void);
1907 struct stasis_message_type *aoc_d_type(void);
1908 struct stasis_message_type *aoc_e_type(void);
1909 
1911  aoc_s_type,
1912  .to_ami = aoc_s_to_ami);
1913 
1915  aoc_d_type,
1916  .to_ami = aoc_d_to_ami);
1917 
1919  aoc_e_type,
1920  .to_ami = aoc_e_to_ami);
1921 
1922 int ast_aoc_manager_event(const struct ast_aoc_decoded *decoded, struct ast_channel *chan)
1923 {
1924  struct ast_json *blob;
1925  struct stasis_message_type *msg_type;
1926 
1927  if (!decoded) {
1928  return -1;
1929  }
1930 
1931  switch (decoded->msg_type) {
1932  case AST_AOC_S:
1933  blob = s_to_json(decoded);
1934  msg_type = aoc_s_type();
1935  break;
1936  case AST_AOC_D:
1937  blob = d_to_json(decoded);
1938  msg_type = aoc_d_type();
1939  break;
1940  case AST_AOC_E:
1941  blob = e_to_json(decoded);
1942  msg_type = aoc_e_type();
1943  break;
1944  default:
1945  /* events for AST_AOC_REQUEST are not generated here */
1946  return 0;
1947  }
1948 
1949  aoc_publish_blob(chan, msg_type, blob);
1950  ast_json_unref(blob);
1951  return 0;
1952 }
1953 
1954 int ast_aoc_decoded2str(const struct ast_aoc_decoded *decoded, struct ast_str **msg)
1955 {
1956  if (!decoded || !msg) {
1957  return -1;
1958  }
1959 
1960  switch (decoded->msg_type) {
1961  case AST_AOC_S:
1962  ast_str_append(msg, 0, "AOC-S\r\n");
1963  aoc_s_event(decoded, msg);
1964  break;
1965  case AST_AOC_D:
1966  ast_str_append(msg, 0, "AOC-D\r\n");
1967  aoc_d_event(decoded, msg);
1968  break;
1969  case AST_AOC_E:
1970  ast_str_append(msg, 0, "AOC-E\r\n");
1971  aoc_e_event(decoded, msg);
1972  break;
1973  case AST_AOC_REQUEST:
1974  ast_str_append(msg, 0, "AOC-Request\r\n");
1975  aoc_request_event(decoded, msg);
1976  break;
1977  }
1978 
1979  return 0;
1980 }
1981 
1982 static void aoc_display_decoded_debug(const struct ast_aoc_decoded *decoded, int decoding, struct ast_channel *chan)
1983 {
1984  struct ast_str *msg;
1985 
1986  if (!decoded || !(msg = ast_str_create(1024))) {
1987  return;
1988  }
1989 
1990  if (decoding) {
1991  ast_str_append(&msg, 0, "---- DECODED AOC MSG ----\r\n");
1992  } else {
1993  ast_str_append(&msg, 0, "---- ENCODED AOC MSG ----\r\n");
1994  }
1995  if (chan) {
1996  ast_str_append(&msg, 0, "CHANNEL: %s\r\n", ast_channel_name(chan));
1997  }
1998 
1999  if (ast_aoc_decoded2str(decoded, &msg)) {
2000  ast_free(msg);
2001  return;
2002  }
2003 
2004  ast_verb(1, "%s\r\n", ast_str_buffer(msg));
2005  ast_free(msg);
2006 }
2007 
2008 static struct ast_cli_entry aoc_cli[] = {
2009  AST_CLI_DEFINE(aoc_cli_debug_enable, "enable cli debugging of AOC messages"),
2010 };
2011 
2012 static void aoc_shutdown(void)
2013 {
2014  STASIS_MESSAGE_TYPE_CLEANUP(aoc_s_type);
2015  STASIS_MESSAGE_TYPE_CLEANUP(aoc_d_type);
2016  STASIS_MESSAGE_TYPE_CLEANUP(aoc_e_type);
2017 
2018  ast_cli_unregister_multiple(aoc_cli, ARRAY_LEN(aoc_cli));
2019 }
2021 {
2022  STASIS_MESSAGE_TYPE_INIT(aoc_s_type);
2023  STASIS_MESSAGE_TYPE_INIT(aoc_d_type);
2024  STASIS_MESSAGE_TYPE_INIT(aoc_e_type);
2025 
2026  ast_register_cleanup(aoc_shutdown);
2027  return ast_cli_register_multiple(aoc_cli, ARRAY_LEN(aoc_cli));
2028 }
Struct containing info for an AMI event to send out.
Definition: manager.h:502
Main Channel structure associated with a channel.
struct ast_json * ast_json_ref(struct ast_json *value)
Increase refcount on value.
Definition: json.c:67
Asterisk main include file. File version handling, generic pbx functions.
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:612
enum ast_aoc_type ast_aoc_get_msg_type(struct ast_aoc_decoded *decoded)
get the message type, AOC-D, AOC-E, or AOC Request
Definition: aoc.c:892
String manipulation functions.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
void * ast_aoc_destroy_encoded(struct ast_aoc_encoded *encoded)
free an ast_aoc_encoded object
Definition: aoc.c:313
const struct ast_aoc_s_entry * ast_aoc_s_get_rate_info(struct ast_aoc_decoded *decoded, unsigned int entry_number)
get a specific AOC-S rate entry.
Definition: aoc.c:761
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
union ast_aoc_s_entry::@180 rate
Charge rate being applied.
unsigned int ast_aoc_get_unit_count(struct ast_aoc_decoded *decoded)
get the number of unit entries for AOC-D and AOC-E messages
Definition: aoc.c:1019
#define STASIS_MESSAGE_TYPE_INIT(name)
Boiler-plate messaging macro for initializing message types.
Definition: stasis.h:1493
descriptor for a cli entry.
Definition: cli.h:171
int ast_aoc_test_encode_decode_match(struct ast_aoc_decoded *decoded)
test aoc encode decode routines.
Definition: aoc.c:1330
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
enum ast_aoc_charge_type ast_aoc_get_charge_type(struct ast_aoc_decoded *decoded)
get the charging type for an AOC-D or AOC-E message
Definition: aoc.c:897
AST_JSON_INT_T ast_json_int_t
Primarily used to cast when packing to an "I" type.
Definition: json.h:87
Definition: aoc.h:165
Structure representing a snapshot of channel state.
#define STASIS_MESSAGE_TYPE_CLEANUP(name)
Boiler-plate messaging macro for cleaning up message types.
Definition: stasis.h:1515
int ast_aoc_decoded2str(const struct ast_aoc_decoded *decoded, struct ast_str **msg)
Convert decoded aoc msg to string representation.
Definition: aoc.c:1954
struct ast_json * ast_json_stringf(const char *format,...)
Create a JSON string, printf style.
Definition: json.c:293
AOC_IE
AOC Payload Information Elements.
Definition: aoc.c:235
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
int ast_aoc_set_association_id(struct ast_aoc_decoded *decoded, const int id)
set the charging association id for an AST_AOC_E message
Definition: aoc.c:1040
uint8_t charging_type
Charging interval type.
Definition: aoc.h:122
int ast_aoc_set_total_type(struct ast_aoc_decoded *decoded, const enum ast_aoc_total_type type)
Sets the type of total for a AOC-D message.
Definition: aoc.c:907
ast_aoc_currency_multiplier
Defines the currency multiplier for an aoc message.
Definition: aoc.h:34
enum ast_aoc_currency_multiplier ast_aoc_get_currency_multiplier(struct ast_aoc_decoded *decoded)
get the currency multiplier for AOC-D and AOC-E messages
Definition: aoc.c:945
int ast_json_is_null(const struct ast_json *value)
Check if value is JSON null.
Definition: json.c:273
int ast_aoc_set_termination_request(struct ast_aoc_decoded *decoded)
Mark the AST_AOC_REQUEST message as a termination request.
Definition: aoc.c:1069
const char * ast_aoc_get_currency_name(struct ast_aoc_decoded *decoded)
get the currency name for AOC-D and AOC-E messages
Definition: aoc.c:972
struct ast_manager_event_blob * ast_manager_event_blob_create(int event_flags, const char *manager_event, const char *extra_fields_fmt,...)
Construct a ast_manager_event_blob.
Definition: manager.c:10563
int ast_aoc_add_unit_entry(struct ast_aoc_decoded *decoded, const unsigned int amount_is_present, const unsigned int amount, const unsigned int type_is_present, const unsigned int type)
Adds a unit entry into the list of units.
Definition: aoc.c:977
struct ast_json * blob
Definition: aoc.c:1809
int ast_aoc_s_add_special_arrangement(struct ast_aoc_decoded *decoded, unsigned int code)
Add AOC-S special arrangement entry.
Definition: aoc.c:880
int ast_json_object_set(struct ast_json *object, const char *key, struct ast_json *value)
Set a field in a JSON object.
Definition: json.c:414
Utility functions.
struct ast_str * ast_manager_str_from_json_object(struct ast_json *blob, key_exclusion_cb exclusion_cb)
Convert a JSON object into an AMI compatible string.
Definition: manager.c:1981
struct ast_json * ast_json_null(void)
Get the JSON null value.
Definition: json.c:248
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
const struct ast_aoc_unit_entry * ast_aoc_get_unit_info(struct ast_aoc_decoded *decoded, unsigned int entry_number)
get a specific unit entry.
Definition: aoc.c:1010
struct ast_channel_snapshot * snapshot
Definition: aoc.c:1807
int ast_aoc_set_currency_info(struct ast_aoc_decoded *decoded, const unsigned int amount, const enum ast_aoc_currency_multiplier multiplier, const char *name)
Sets the currency values for a AOC-D or AOC-E message.
Definition: aoc.c:919
const struct ast_aoc_charging_association * ast_aoc_get_association_info(struct ast_aoc_decoded *decoded)
get the charging association info for AOC-E messages
Definition: aoc.c:1051
Generic Advice of Charge encode and decode routines.
int ast_register_cleanup(void(*func)(void))
Register a function to be executed before Asterisk gracefully exits.
Definition: clicompat.c:19
enum ast_aoc_billing_id ast_aoc_get_billing_id(struct ast_aoc_decoded *decoded)
get the billing id for AOC-D and AOC-E messages
Definition: aoc.c:1035
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
int ast_aoc_s_add_rate_special_charge_code(struct ast_aoc_decoded *decoded, enum ast_aoc_s_charged_item charged_item, unsigned int code)
Add AOC-S special rate entry.
Definition: aoc.c:844
char currency_name[AOC_CURRENCY_NAME_SIZE]
Definition: aoc.h:142
int ast_aoc_manager_event(const struct ast_aoc_decoded *decoded, struct ast_channel *chan)
generate AOC manager event for an AOC-S, AOC-D, or AOC-E msg
Definition: aoc.c:1922
struct ast_str * ast_manager_build_channel_state_string(const struct ast_channel_snapshot *snapshot)
Generate the AMI message body from a channel snapshot.
struct ast_aoc_decoded * ast_aoc_create(const enum ast_aoc_type msg_type, const enum ast_aoc_charge_type charge_type, const enum ast_aoc_request requests)
creates a ast_aoc_decode object of a specific message type
Definition: aoc.c:276
struct ast_json * ast_json_array_create(void)
Create a empty JSON array.
Definition: json.c:362
int ast_json_array_append(struct ast_json *array, struct ast_json *value)
Append to an array.
Definition: json.c:378
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
#define STASIS_MESSAGE_TYPE_DEFN(name,...)
Boiler-plate messaging macro for defining public message types.
Definition: stasis.h:1440
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: utils.c:2199
Support for dynamic strings.
Definition: strings.h:623
int ast_aoc_set_association_number(struct ast_aoc_decoded *decoded, const char *num, uint8_t plan)
set the charging association number for an AOC-E message
Definition: aoc.c:1056
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
struct stasis_message * stasis_message_create(struct stasis_message_type *type, void *data)
Create a new message.
void * ast_aoc_destroy_decoded(struct ast_aoc_decoded *decoded)
free an ast_aoc_decoded object
Definition: aoc.c:307
struct ast_aoc_decoded * ast_aoc_decode(struct ast_aoc_encoded *encoded, size_t size, struct ast_channel *chan)
decodes an encoded aoc payload.
Definition: aoc.c:449
uint32_t granularity_time
Definition: aoc.h:107
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
Definition: stasis.c:1511
char * command
Definition: cli.h:186
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
int ast_aoc_s_add_rate_na(struct ast_aoc_decoded *decoded, enum ast_aoc_s_charged_item charged_item)
Add AOC-S entry indicating charge item is not available.
Definition: aoc.c:869
struct ast_aoc_encoded * ast_aoc_encode(struct ast_aoc_decoded *decoded, size_t *out_size, struct ast_channel *chan)
encodes a decoded aoc structure so it can be passed on the wire
Definition: aoc.c:650
enum ast_aoc_request ast_aoc_get_request(struct ast_aoc_decoded *decoded)
get the types of AOC requested for when message type is AOC Request
Definition: aoc.c:902
Prototypes for public functions only of internal interest,.
char currency_name[AOC_CURRENCY_NAME_SIZE]
Definition: aoc.h:114
const char * ast_aoc_get_currency_multiplier_decimal(struct ast_aoc_decoded *decoded)
get the currency multiplier for AOC-D and AOC-E messages in decimal format
Definition: aoc.c:950
struct stasis_topic * ast_manager_get_topic(void)
Get the Stasis Message Bus API topic for AMI.
Definition: manager.c:1880
AOC IE payload header.
Definition: aoc.c:245
int ast_aoc_cli_init(void)
enable aoc cli options
Definition: aoc.c:2020
const char * usage
Definition: cli.h:177
unsigned int ast_aoc_get_currency_amount(struct ast_aoc_decoded *decoded)
get the currency amount for AOC-D and AOC-E messages
Definition: aoc.c:940
int ast_aoc_s_add_rate_flat(struct ast_aoc_decoded *decoded, enum ast_aoc_s_charged_item charged_item, unsigned int amount, enum ast_aoc_currency_multiplier multiplier, const char *currency_name)
Add AOC-S flat rate entry.
Definition: aoc.c:801
struct ast_json * ast_json_object_create(void)
Create a new JSON object.
Definition: json.c:399
unsigned int ast_aoc_s_get_count(struct ast_aoc_decoded *decoded)
get the number rates associated with an AOC-S message
Definition: aoc.c:756
Standard Command Line Interface.
enum ast_aoc_total_type ast_aoc_get_total_type(struct ast_aoc_decoded *decoded)
get the type of total for a AOC-D message
Definition: aoc.c:914
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
int ast_aoc_s_add_rate_duration(struct ast_aoc_decoded *decoded, enum ast_aoc_s_charged_item charged_item, unsigned int amount, enum ast_aoc_currency_multiplier multiplier, const char *currency_name, unsigned long time, enum ast_aoc_time_scale time_scale, unsigned long granularity_time, enum ast_aoc_time_scale granularity_time_scale, int step_function)
Add AOC-S duration rate entry.
Definition: aoc.c:770
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is "false"...
Definition: utils.c:2216
ast_aoc_billing_id
Defines the billing id options for an aoc message.
Definition: aoc.h:49
Abstract JSON element (object, array, string, int, ...).
Definition: search.h:40
int ast_aoc_get_termination_request(struct ast_aoc_decoded *decoded)
get whether or not the AST_AOC_REQUEST message as a termination request.
Definition: aoc.c:1079
int ast_aoc_s_add_rate_volume(struct ast_aoc_decoded *decoded, enum ast_aoc_s_charged_item charged_item, enum ast_aoc_volume_unit volume_unit, unsigned int amount, enum ast_aoc_currency_multiplier multiplier, const char *currency_name)
Add AOC-S volume rate entry.
Definition: aoc.c:822
Definition: aoc.h:178
int ast_aoc_s_add_rate_free(struct ast_aoc_decoded *decoded, enum ast_aoc_s_charged_item charged_item, int from_beginning)
Add AOC-S indicating charge item is free.
Definition: aoc.c:857
#define AST_JSON_UTF8_VALIDATE(str)
Check str for UTF-8 and replace with an empty string if fails the check.
Definition: json.h:224
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
int ast_aoc_set_billing_id(struct ast_aoc_decoded *decoded, const enum ast_aoc_billing_id id)
set the billing id for a AOC-D or AST_AOC_E message
Definition: aoc.c:1024