Asterisk - The Open Source Telephony Project  21.4.1
res_pjsip_aoc.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2022, Michael Kuron
5  *
6  * Michael Kuron <m.kuron@gmx.de>
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 /*** MODULEINFO
20  <depend>pjproject</depend>
21  <depend>res_pjsip</depend>
22  <support_level>extended</support_level>
23  ***/
24 
25 #include "asterisk.h"
26 
27 #include <pjsip.h>
28 #include <pjlib.h>
29 
30 #include "asterisk/aoc.h"
31 #include "asterisk/module.h"
32 #include "asterisk/pbx.h"
33 #include "asterisk/res_pjsip.h"
34 #include "asterisk/res_pjsip_session.h"
35 
36 static pj_xml_attr *aoc_xml_create_attr(pj_pool_t *pool, pj_xml_node *node,
37  const char *name, const char *value)
38 {
39  pj_xml_attr *attr;
40 
41  attr = PJ_POOL_ALLOC_T(pool, pj_xml_attr);
42 
43  pj_strdup2(pool, &attr->name, name);
44  pj_strdup2(pool, &attr->value, value);
45 
46  pj_xml_add_attr(node, attr);
47  return attr;
48 }
49 
50 static pj_xml_node *aoc_xml_create_node(pj_pool_t *pool, pj_xml_node *parent,
51  const char *name)
52 {
53  pj_xml_node *node;
54 
55  node = PJ_POOL_ZALLOC_T(pool, pj_xml_node);
56 
57  pj_list_init(&node->attr_head);
58  pj_list_init(&node->node_head);
59 
60  pj_strdup2(pool, &node->name, name);
61 
62  if (parent) {
63  pj_xml_add_node(parent, node);
64  }
65 
66  return node;
67 }
68 
69 static void aoc_xml_set_node_content(pj_pool_t *pool, pj_xml_node *node,
70  const char *content)
71 {
72  pj_strdup2(pool, &node->content, content);
73 }
74 
75 static char * aoc_format_amount(pj_pool_t *pool, unsigned int amount,
76  enum ast_aoc_currency_multiplier multiplier)
77 {
78  const size_t amount_max_size = 16;
79  char *amount_str;
80 
81  amount_str = pj_pool_alloc(pool, amount_max_size);
82 
83  switch (multiplier) {
84  case AST_AOC_MULT_ONETHOUSANDTH:
85  pj_ansi_snprintf(amount_str, amount_max_size, "%.3f", amount*0.001f);
86  break;
87  case AST_AOC_MULT_ONEHUNDREDTH:
88  pj_ansi_snprintf(amount_str, amount_max_size, "%.2f", amount*0.01f);
89  break;
90  case AST_AOC_MULT_ONETENTH:
91  pj_ansi_snprintf(amount_str, amount_max_size, "%.1f", amount*0.1f);
92  break;
93  case AST_AOC_MULT_ONE:
94  pj_ansi_snprintf(amount_str, amount_max_size, "%d", amount);
95  break;
96  case AST_AOC_MULT_TEN:
97  pj_ansi_snprintf(amount_str, amount_max_size, "%d", amount*10);
98  break;
99  case AST_AOC_MULT_HUNDRED:
100  pj_ansi_snprintf(amount_str, amount_max_size, "%d", amount*100);
101  break;
102  case AST_AOC_MULT_THOUSAND:
103  pj_ansi_snprintf(amount_str, amount_max_size, "%d", amount*1000);
104  break;
105  default:
106  pj_ansi_snprintf(amount_str, amount_max_size, "%d", amount);
107  }
108 
109  return amount_str;
110 }
111 
112 static const char *aoc_time_scale_str(enum ast_aoc_time_scale value)
113 {
114  const char *str;
115 
116  switch (value) {
117  default:
118  case AST_AOC_TIME_SCALE_HUNDREDTH_SECOND:
119  str = "one-hundredth-second";
120  break;
121  case AST_AOC_TIME_SCALE_TENTH_SECOND:
122  str = "one-tenth-second";
123  break;
124  case AST_AOC_TIME_SCALE_SECOND:
125  str = "one-second";
126  break;
127  case AST_AOC_TIME_SCALE_TEN_SECOND:
128  str = "ten-seconds";
129  break;
130  case AST_AOC_TIME_SCALE_MINUTE:
131  str = "one-minute";
132  break;
133  case AST_AOC_TIME_SCALE_HOUR:
134  str = "one-hour";
135  break;
136  case AST_AOC_TIME_SCALE_DAY:
137  str = "twenty-four-hours";
138  break;
139  }
140  return str;
141 }
142 
143 static void aoc_datastore_destroy(void *obj)
144 {
145  char *xml = obj;
146  ast_free(xml);
147 }
148 
149 static const struct ast_datastore_info aoc_s_datastore = {
150  .type = "AOC-S",
151  .destroy = aoc_datastore_destroy,
152 };
153 
154 static const struct ast_datastore_info aoc_d_datastore = {
155  .type = "AOC-D",
156  .destroy = aoc_datastore_destroy,
157 };
158 
159 static const struct ast_datastore_info aoc_e_datastore = {
160  .type = "AOC-E",
161  .destroy = aoc_datastore_destroy,
162 };
163 
164 struct aoc_data {
165  struct ast_sip_session *session;
166  struct ast_aoc_decoded *decoded;
168 };
169 
170 static void aoc_release_pool(void * data)
171 {
172  pj_pool_t *pool = data;
173  pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
174 }
175 
176 static int aoc_send_as_xml(void * data)
177 {
178  RAII_VAR(struct aoc_data *, adata, data, ao2_cleanup);
179  RAII_VAR(pj_pool_t *, pool, NULL, aoc_release_pool);
180 
181  pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "AOC", 2048, 512);
182 
183  if (!pool) {
184  ast_log(LOG_ERROR, "Could not create a memory pool for AOC XML\n");
185  return 1;
186  }
187 
188  if (ast_aoc_get_msg_type(adata->decoded) == AST_AOC_D ||
189  ast_aoc_get_msg_type(adata->decoded) == AST_AOC_E) {
190  pj_xml_node *aoc;
191  pj_xml_node *aoc_type;
192  pj_xml_node *charging_info = NULL;
193  pj_xml_node *charges;
194  pj_xml_node *charge;
195  char *xml;
196  size_t size;
197  const size_t xml_max_size = 512;
198 
199  aoc = aoc_xml_create_node(pool, NULL, "aoc");
200  aoc_xml_create_attr(pool, aoc, "xmlns",
201  "http://uri.etsi.org/ngn/params/xml/simservs/aoc");
202  aoc_type = aoc_xml_create_node(pool, aoc,
203  ast_aoc_get_msg_type(adata->decoded) == AST_AOC_D ? "aoc-d" : "aoc-e");
204  if (ast_aoc_get_msg_type(adata->decoded) == AST_AOC_D) {
205  charging_info = aoc_xml_create_node(pool, aoc_type, "charging-info");
206  aoc_xml_set_node_content(pool, charging_info,
207  ast_aoc_get_total_type(adata->decoded) == AST_AOC_SUBTOTAL ? "subtotal" : "total");
208  }
209  charges = aoc_xml_create_node(pool, aoc_type, "recorded-charges");
210 
211  if (ast_aoc_get_charge_type(adata->decoded) == AST_AOC_CHARGE_FREE) {
212  charge = aoc_xml_create_node(pool, charges, "free-charge");
213  } else if (ast_aoc_get_charge_type(adata->decoded) == AST_AOC_CHARGE_CURRENCY ||
214  ast_aoc_get_charge_type(adata->decoded) == AST_AOC_CHARGE_UNIT) {
215  charge = aoc_xml_create_node(pool, charges, "recorded-currency-units");
216  } else {
217  charge = aoc_xml_create_node(pool, charges, "not-available");
218  }
219 
220  if (ast_aoc_get_charge_type(adata->decoded) == AST_AOC_CHARGE_CURRENCY) {
221  const char *currency;
222  pj_xml_node *amount;
223  char *amount_str;
224 
225  currency = ast_aoc_get_currency_name(adata->decoded);
226  if (!ast_strlen_zero(currency)) {
227  pj_xml_node *currency_id;
228 
229  currency_id = aoc_xml_create_node(pool, charge, "currency-id");
230  aoc_xml_set_node_content(pool, currency_id, currency);
231  }
232 
233  amount = aoc_xml_create_node(pool, charge, "currency-amount");
234  amount_str = aoc_format_amount(pool, ast_aoc_get_currency_amount(adata->decoded),
235  ast_aoc_get_currency_multiplier(adata->decoded));
236  aoc_xml_set_node_content(pool, amount, amount_str);
237  } else if (ast_aoc_get_charge_type(adata->decoded) == AST_AOC_CHARGE_UNIT) {
238  pj_xml_node *currency_id;
239  const struct ast_aoc_unit_entry *unit_entry;
240 
241  currency_id = aoc_xml_create_node(pool, charge, "currency-id");
242  aoc_xml_set_node_content(pool, currency_id, "UNIT");
243 
244  unit_entry = ast_aoc_get_unit_info(adata->decoded, 0);
245  if (unit_entry) {
246  pj_xml_node *amount;
247  char *amount_str;
248 
249  amount = aoc_xml_create_node(pool, charge, "currency-amount");
250  amount_str = aoc_format_amount(pool, unit_entry->amount,
251  AST_AOC_MULT_ONE);
252  aoc_xml_set_node_content(pool, amount, amount_str);
253  }
254  }
255 
256  xml = pj_pool_alloc(pool, xml_max_size);
257  size = pj_xml_print(aoc, xml, xml_max_size - 1, PJ_TRUE);
258  if (size >= xml_max_size) {
259  ast_log(LOG_ERROR, "aoc+xml body text too large\n");
260  return 1;
261  }
262  xml[size] = 0;
263 
264  if (ast_aoc_get_msg_type(adata->decoded) == AST_AOC_D) {
265  RAII_VAR(struct ast_datastore *, datastore,
266  ast_sip_session_get_datastore(adata->session, aoc_d_datastore.type),
267  ao2_cleanup);
268  struct pjsip_tx_data *tdata;
269  struct ast_sip_body body = {
270  .type = "application",
271  .subtype = "vnd.etsi.aoc+xml",
272  .body_text = xml
273  };
274 
275  if (ast_sip_create_request("INFO", adata->session->inv_session->dlg,
276  adata->session->endpoint, NULL, NULL, &tdata)) {
277  ast_log(LOG_ERROR, "Could not create AOC INFO request\n");
278  return 1;
279  }
280  if (ast_sip_add_body(tdata, &body)) {
281  ast_log(LOG_ERROR, "Could not add body to AOC INFO request\n");
282  pjsip_tx_data_dec_ref(tdata);
283  return 1;
284  }
285  ast_sip_session_send_request(adata->session, tdata);
286 
287  if (!datastore) {
288  datastore = ast_sip_session_alloc_datastore(&aoc_d_datastore, aoc_d_datastore.type);
289  if (!datastore) {
290  ast_log(LOG_ERROR, "Unable to create datastore for AOC-D.\n");
291  return 1;
292  }
293  datastore->data = NULL;
294  if (ast_sip_session_add_datastore(adata->session, datastore)) {
295  ast_log(LOG_ERROR, "Unable to create datastore for AOC-D.\n");
296  return 1;
297  }
298  } else {
299  ast_free(datastore->data);
300  }
301 
302  aoc_xml_set_node_content(pool, charging_info, "total");
303  size = pj_xml_print(aoc, xml, xml_max_size - 1, PJ_TRUE);
304  xml[size] = 0;
305  datastore->data = ast_strdup(xml);
306  } else if (ast_aoc_get_msg_type(adata->decoded) == AST_AOC_E) {
307  RAII_VAR(struct ast_datastore *, datastore,
308  ast_sip_session_get_datastore(adata->session, aoc_e_datastore.type),
309  ao2_cleanup);
310  if (!datastore) {
311  datastore = ast_sip_session_alloc_datastore(&aoc_e_datastore, aoc_e_datastore.type);
312  if (!datastore) {
313  ast_log(LOG_ERROR, "Unable to create datastore for AOC-E.\n");
314  return 1;
315  }
316  datastore->data = NULL;
317  if (ast_sip_session_add_datastore(adata->session, datastore)) {
318  ast_log(LOG_ERROR, "Unable to create datastore for AOC-E.\n");
319  return 1;
320  }
321  } else {
322  ast_free(datastore->data);
323  }
324  datastore->data = ast_strdup(xml);
325  }
326  } else if (ast_aoc_get_msg_type(adata->decoded) == AST_AOC_S) {
327  pj_xml_node *aoc;
328  pj_xml_node *aoc_type;
329  pj_xml_node *charged_items;
330  const struct ast_aoc_s_entry *entry;
331  int idx;
332  char *xml;
333  size_t size;
334  const size_t xml_max_size = 1024;
335 
336  aoc = aoc_xml_create_node(pool, NULL, "aoc");
337  aoc_xml_create_attr(pool, aoc, "xmlns",
338  "http://uri.etsi.org/ngn/params/xml/simservs/aoc");
339  aoc_type = aoc_xml_create_node(pool, aoc, "aoc-s");
340  charged_items = aoc_xml_create_node(pool, aoc_type, "charged-items");
341 
342  for (idx = 0; idx < ast_aoc_s_get_count(adata->decoded); idx++) {
343  pj_xml_node *charged_item;
344  pj_xml_node *charge;
345 
346  if (!(entry = ast_aoc_s_get_rate_info(adata->decoded, idx))) {
347  break;
348  }
349 
350  if (entry->charged_item == AST_AOC_CHARGED_ITEM_BASIC_COMMUNICATION) {
351  charged_item = aoc_xml_create_node(pool, charged_items, "basic");
352  } else if (entry->charged_item == AST_AOC_CHARGED_ITEM_CALL_ATTEMPT) {
353  charged_item = aoc_xml_create_node(pool, charged_items,
354  "communication-attempt");
355  } else if (entry->charged_item == AST_AOC_CHARGED_ITEM_CALL_SETUP) {
356  charged_item = aoc_xml_create_node(pool, charged_items,
357  "communication-setup");
358  } else {
359  continue;
360  }
361 
362  if (entry->rate_type == AST_AOC_RATE_TYPE_FREE) {
363  charge = aoc_xml_create_node(pool, charged_item, "free-charge");
364  } else if (entry->rate_type == AST_AOC_RATE_TYPE_FLAT) {
365  charge = aoc_xml_create_node(pool, charged_item, "flat-rate");
366  } else if (entry->rate_type == AST_AOC_RATE_TYPE_DURATION &&
367  entry->charged_item == AST_AOC_CHARGED_ITEM_BASIC_COMMUNICATION) {
368  charge = aoc_xml_create_node(pool, charged_item, "price-time");
369  } else {
370  continue;
371  }
372 
373  if (entry->rate_type == AST_AOC_RATE_TYPE_DURATION ||
374  entry->rate_type == AST_AOC_RATE_TYPE_FLAT) {
375  const char *currency;
376  pj_xml_node *amount;
377  uint32_t amount_val;
378  enum ast_aoc_currency_multiplier multiplier_val;
379  char *amount_str;
380 
381  currency = (entry->rate_type == AST_AOC_RATE_TYPE_DURATION ?
382  entry->rate.duration.currency_name :
383  entry->rate.flat.currency_name);
384  if (!ast_strlen_zero(currency)) {
385  pj_xml_node *currency_id;
386 
387  currency_id = aoc_xml_create_node(pool, charge, "currency-id");
388  aoc_xml_set_node_content(pool, currency_id, currency);
389  }
390 
391  amount = aoc_xml_create_node(pool, charge, "currency-amount");
392  amount_val = (entry->rate_type == AST_AOC_RATE_TYPE_DURATION ?
393  entry->rate.duration.amount : entry->rate.flat.amount);
394  multiplier_val = (entry->rate_type == AST_AOC_RATE_TYPE_DURATION ?
395  entry->rate.duration.multiplier : entry->rate.flat.multiplier);
396  amount_str = aoc_format_amount(pool, amount_val, multiplier_val);
397  aoc_xml_set_node_content(pool, amount, amount_str);
398  }
399 
400  if (entry->rate_type == AST_AOC_RATE_TYPE_DURATION) {
401  pj_xml_node *length_time_unit;
402  pj_xml_node *time_unit;
403  char *time_str;
404  pj_xml_node *scale;
405  pj_xml_node *charging_type;
406 
407  length_time_unit = aoc_xml_create_node(pool, charge, "length-time-unit");
408  time_unit = aoc_xml_create_node(pool, length_time_unit, "time-unit");
409  time_str = aoc_format_amount(pool, entry->rate.duration.time,
410  AST_AOC_MULT_ONE);
411  aoc_xml_set_node_content(pool, time_unit, time_str);
412  scale = aoc_xml_create_node(pool, length_time_unit, "scale");
413  aoc_xml_set_node_content(pool, scale,
414  aoc_time_scale_str(entry->rate.duration.time_scale));
415  charging_type = aoc_xml_create_node(pool, charge, "charging-type");
416  aoc_xml_set_node_content(pool, charging_type,
417  entry->rate.duration.charging_type ? "step-function" :
418  "continuous");
419  }
420  }
421 
422  xml = pj_pool_alloc(pool, xml_max_size);
423  size = pj_xml_print(aoc, xml, xml_max_size - 1, PJ_TRUE);
424  if (size >= xml_max_size) {
425  ast_log(LOG_ERROR, "aoc+xml body text too large\n");
426  return 1;
427  }
428  xml[size] = 0;
429 
430  if (adata->channel_state == AST_STATE_UP ||
431  adata->session->call_direction == AST_SIP_SESSION_OUTGOING_CALL) {
432  struct pjsip_tx_data *tdata;
433  struct ast_sip_body body = {
434  .type = "application",
435  .subtype = "vnd.etsi.aoc+xml",
436  .body_text = xml
437  };
438 
439  if (ast_sip_create_request("INFO", adata->session->inv_session->dlg,
440  adata->session->endpoint, NULL, NULL, &tdata)) {
441  ast_log(LOG_ERROR, "Could not create AOC INFO request\n");
442  return 1;
443  }
444  if (ast_sip_add_body(tdata, &body)) {
445  ast_log(LOG_ERROR, "Could not add body to AOC INFO request\n");
446  pjsip_tx_data_dec_ref(tdata);
447  return 1;
448  }
449  ast_sip_session_send_request(adata->session, tdata);
450  } else {
451  RAII_VAR(struct ast_datastore *, datastore,
452  ast_sip_session_get_datastore(adata->session, aoc_s_datastore.type),
453  ao2_cleanup);
454  if (!datastore) {
455  datastore = ast_sip_session_alloc_datastore(&aoc_s_datastore, aoc_s_datastore.type);
456  if (!datastore) {
457  ast_log(LOG_ERROR, "Unable to create datastore for AOC-S.\n");
458  return 1;
459  }
460  if (ast_sip_session_add_datastore(adata->session, datastore)) {
461  ast_log(LOG_ERROR, "Unable to create datastore for AOC-S.\n");
462  return 1;
463  }
464  } else {
465  ast_free(datastore->data);
466  }
467  datastore->data = ast_strdup(xml);
468  }
469  }
470 
471  return 0;
472 }
473 
474 static void aoc_data_destroy(void * data)
475 {
476  struct aoc_data *adata = data;
477 
478  ast_aoc_destroy_decoded(adata->decoded);
479  ao2_cleanup(adata->session);
480 }
481 
482 static struct ast_frame *aoc_framehook(struct ast_channel *ast, struct ast_frame *f,
483  enum ast_framehook_event event, void *data)
484 {
485  struct ast_sip_channel_pvt *channel;
486  struct aoc_data *adata;
487 
488  if (!f || f->frametype != AST_FRAME_CONTROL || event != AST_FRAMEHOOK_EVENT_WRITE ||
490  return f;
491  }
492 
493  adata = ao2_alloc(sizeof(struct aoc_data), aoc_data_destroy);
494  if (!adata) {
495  ast_log(LOG_ERROR, "Failed to allocate AOC data\n");
496  return f;
497  }
498 
499  adata->decoded = ast_aoc_decode((struct ast_aoc_encoded *) f->data.ptr, f->datalen, ast);
500  if (!adata->decoded) {
501  ast_log(LOG_ERROR, "Error decoding indicated AOC data\n");
502  ao2_ref(adata, -1);
503  return f;
504  }
505 
506  channel = ast_channel_tech_pvt(ast);
507  adata->session = ao2_bump(channel->session);
508  adata->channel_state = ast_channel_state(ast);
509 
510  if (ast_sip_push_task(adata->session->serializer, aoc_send_as_xml, adata)) {
511  ast_log(LOG_ERROR, "Unable to send AOC XML for channel %s\n", ast_channel_name(ast));
512  ao2_ref(adata, -1);
513  }
514  return &ast_null_frame;
515 }
516 
517 static int aoc_consume(void *data, enum ast_frame_type type)
518 {
519  return (type == AST_FRAME_CONTROL) ? 1 : 0;
520 }
521 
522 static void aoc_attach_framehook(struct ast_sip_session *session)
523 {
524  int framehook_id;
525  static struct ast_framehook_interface hook = {
526  .version = AST_FRAMEHOOK_INTERFACE_VERSION,
527  .event_cb = aoc_framehook,
528  .consume_cb = aoc_consume,
529  };
530 
531  if (!session->channel || !session->endpoint->send_aoc) {
532  return;
533  }
534 
535  ast_channel_lock(session->channel);
536 
537  framehook_id = ast_framehook_attach(session->channel, &hook);
538  if (framehook_id < 0) {
539  ast_log(LOG_WARNING, "Could not attach AOC Frame hook, AOC will be unavailable on '%s'\n",
540  ast_channel_name(session->channel));
541  }
542 
543  ast_channel_unlock(session->channel);
544 }
545 
546 static int aoc_incoming_invite_request(struct ast_sip_session *session,
547  struct pjsip_rx_data *rdata)
548 {
549  aoc_attach_framehook(session);
550  return 0;
551 }
552 
553 static void aoc_outgoing_invite_request(struct ast_sip_session *session,
554  struct pjsip_tx_data *tdata)
555 {
556  aoc_attach_framehook(session);
557 }
558 
559 static void aoc_bye_outgoing_response(struct ast_sip_session *session,
560  struct pjsip_tx_data *tdata)
561 {
562  struct ast_sip_body body = {
563  .type = "application",
564  .subtype = "vnd.etsi.aoc+xml",
565  };
566  RAII_VAR(struct ast_datastore *, datastore_d, ast_sip_session_get_datastore(session,
567  aoc_d_datastore.type), ao2_cleanup);
568  RAII_VAR(struct ast_datastore *, datastore_e, ast_sip_session_get_datastore(session,
569  aoc_e_datastore.type), ao2_cleanup);
570 
571  if (datastore_e) {
572  body.body_text = datastore_e->data;
573  } else if (datastore_d) {
574  body.body_text = datastore_d->data;
575  }
576  else {
577  return;
578  }
579 
580  if (ast_sip_add_body(tdata, &body)) {
581  ast_log(LOG_ERROR, "Could not add body to AOC INFO request\n");
582  }
583 }
584 
585 static void aoc_bye_outgoing_request(struct ast_sip_session *session,
586  struct pjsip_tx_data *tdata)
587 {
588  struct ast_sip_body body = {
589  .type = "application",
590  .subtype = "vnd.etsi.aoc+xml",
591  };
592  RAII_VAR(struct ast_datastore *, datastore_d, ast_sip_session_get_datastore(session,
593  aoc_d_datastore.type), ao2_cleanup);
594  RAII_VAR(struct ast_datastore *, datastore_e, ast_sip_session_get_datastore(session,
595  aoc_e_datastore.type), ao2_cleanup);
596 
597  if (datastore_e) {
598  body.body_text = datastore_e->data;
599  } else if (datastore_d) {
600  body.body_text = datastore_d->data;
601  }
602  else {
603  return;
604  }
605 
606  if (ast_sip_add_body(tdata, &body)) {
607  ast_log(LOG_ERROR, "Could not add body to AOC INFO request\n");
608  }
609 }
610 
611 static void aoc_invite_outgoing_response(struct ast_sip_session *session,
612  struct pjsip_tx_data *tdata)
613 {
614  pjsip_msg_body *multipart_body;
615  pjsip_multipart_part *part;
616  pj_str_t body_text;
617  pj_str_t type;
618  pj_str_t subtype;
619  RAII_VAR(struct ast_datastore *, datastore, ast_sip_session_get_datastore(session,
620  aoc_s_datastore.type), ao2_cleanup);
621 
622  if (tdata->msg->line.status.code != 180 && tdata->msg->line.status.code != 183 &&
623  tdata->msg->line.status.code != 200) {
624  return;
625  }
626 
627  if (!datastore) {
628  return;
629  }
630 
631  if (tdata->msg->body && pjsip_media_type_cmp(&tdata->msg->body->content_type,
632  &pjsip_media_type_multipart_mixed, 0) == 0) {
633  multipart_body = tdata->msg->body;
634  } else {
635  pjsip_sdp_info *tdata_sdp_info;
636 
637  tdata_sdp_info = pjsip_tdata_get_sdp_info(tdata);
638  if (tdata_sdp_info->sdp) {
639  pj_status_t rc;
640 
641  rc = pjsip_create_multipart_sdp_body(tdata->pool, tdata_sdp_info->sdp,
642  &multipart_body);
643  if (rc != PJ_SUCCESS) {
644  ast_log(LOG_ERROR, "Unable to create sdp multipart body\n");
645  return;
646  }
647  } else {
648  multipart_body = pjsip_multipart_create(tdata->pool,
649  &pjsip_media_type_multipart_mixed, NULL);
650  }
651  }
652 
653  part = pjsip_multipart_create_part(tdata->pool);
654  pj_strdup2(tdata->pool, &body_text, datastore->data);
655  pj_cstr(&type, "application");
656  pj_cstr(&subtype, "vnd.etsi.aoc+xml");
657  part->body = pjsip_msg_body_create(tdata->pool, &type, &subtype, &body_text);
658  pjsip_multipart_add_part(tdata->pool, multipart_body, part);
659 
660  tdata->msg->body = multipart_body;
661 }
662 
663 static struct ast_sip_session_supplement aoc_bye_supplement = {
664  .method = "BYE",
665  .priority = AST_SIP_SUPPLEMENT_PRIORITY_LAST,
666  .outgoing_request = aoc_bye_outgoing_request,
667  .outgoing_response = aoc_bye_outgoing_response,
668 };
669 
670 static struct ast_sip_session_supplement aoc_invite_supplement = {
671  .method = "INVITE",
672  .priority = AST_SIP_SUPPLEMENT_PRIORITY_LAST,
673  .incoming_request = aoc_incoming_invite_request,
674  .outgoing_request = aoc_outgoing_invite_request,
675  .outgoing_response = aoc_invite_outgoing_response,
676 };
677 
678 static int load_module(void)
679 {
680  ast_sip_session_register_supplement(&aoc_bye_supplement);
681  ast_sip_session_register_supplement(&aoc_invite_supplement);
683 }
684 
685 static int unload_module(void)
686 {
687  ast_sip_session_unregister_supplement(&aoc_bye_supplement);
688  ast_sip_session_unregister_supplement(&aoc_invite_supplement);
689  return 0;
690 }
691 
692 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP AOC Support",
693  .support_level = AST_MODULE_SUPPORT_EXTENDED,
694  .load = load_module,
695  .unload = unload_module,
696  .load_pri = AST_MODPRI_CHANNEL_DEPEND,
697  .requires = "res_pjsip",
698 );
const char * type
Definition: datastore.h:32
const char * body_text
Definition: res_pjsip.h:2315
Main Channel structure associated with a channel.
struct ast_sip_endpoint * endpoint
Definition: test_heap.c:38
Asterisk main include file. File version handling, generic pbx functions.
union ast_aoc_s_entry::@180 rate
Charge rate being applied.
ast_framehook_event
These are the types of events that the framehook's event callback can receive.
Definition: framehook.h:151
Definition: aoc.h:165
static pj_pool_t * pool
Global memory pool for configuration and timers.
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_aoc_destroy_decoded(struct ast_aoc_decoded *decoded)
free an ast_aoc_decoded object
Definition: aoc.c:307
unsigned int send_aoc
Definition: res_pjsip.h:1058
Structure for a data store type.
Definition: datastore.h:31
ast_channel_state
ast_channel states
Definition: channelstate.h:35
A structure which contains a channel implementation and session.
Definition: astman.c:222
struct ast_sip_session * session
Pointer to session.
uint8_t charging_type
Charging interval type.
Definition: aoc.h:122
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
Structure for a data store object.
Definition: datastore.h:64
ast_aoc_currency_multiplier
Defines the currency multiplier for an aoc message.
Definition: aoc.h:34
A structure describing a SIP session.
static struct ast_json * channel_state(struct ast_channel_snapshot *old_snapshot, struct ast_channel_snapshot *new_snapshot, const struct timeval *tv)
Handle channel state changes.
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
struct ast_frame_subclass subclass
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
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
int ast_framehook_attach(struct ast_channel *chan, struct ast_framehook_interface *i)
Attach an framehook onto a channel for frame interception.
Definition: framehook.c:132
Generic Advice of Charge encode and decode routines.
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
const char * type
Definition: res_pjsip.h:2311
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
char currency_name[AOC_CURRENCY_NAME_SIZE]
Definition: aoc.h:142
struct ast_channel * channel
Core PBX routines and definitions.
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
struct ast_taskprocessor * serializer
ast_frame_type
Frame types.
int ast_sip_push_task(struct ast_taskprocessor *serializer, int(*sip_task)(void *), void *task_data)
Pushes a task to SIP servants.
Definition: res_pjsip.c:2099
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
const char * subtype
Definition: res_pjsip.h:2313
union ast_frame::@224 data
char currency_name[AOC_CURRENCY_NAME_SIZE]
Definition: aoc.h:114
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
A supplement to SIP message processing.
struct ast_frame ast_null_frame
Definition: main/frame.c:79
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
Data structure associated with a single frame of data.
Definition: search.h:40
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
enum ast_frame_type frametype
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
Definition: aoc.h:178
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
SIP body description.
Definition: res_pjsip.h:2309