33 #include "asterisk/res_pjsip.h"
34 #include "asterisk/res_pjsip_session.h"
36 static pj_xml_attr *aoc_xml_create_attr(pj_pool_t *
pool, pj_xml_node *
node,
37 const char *name,
const char *value)
41 attr = PJ_POOL_ALLOC_T(pool, pj_xml_attr);
43 pj_strdup2(pool, &attr->name, name);
44 pj_strdup2(pool, &attr->value, value);
46 pj_xml_add_attr(node, attr);
50 static pj_xml_node *aoc_xml_create_node(pj_pool_t *
pool, pj_xml_node *parent,
55 node = PJ_POOL_ZALLOC_T(pool, pj_xml_node);
57 pj_list_init(&node->attr_head);
58 pj_list_init(&node->node_head);
60 pj_strdup2(pool, &node->name, name);
63 pj_xml_add_node(parent, node);
69 static void aoc_xml_set_node_content(pj_pool_t *pool, pj_xml_node *node,
72 pj_strdup2(pool, &node->content, content);
75 static char * aoc_format_amount(pj_pool_t *pool,
unsigned int amount,
78 const size_t amount_max_size = 16;
81 amount_str = pj_pool_alloc(pool, amount_max_size);
84 case AST_AOC_MULT_ONETHOUSANDTH:
85 pj_ansi_snprintf(amount_str, amount_max_size,
"%.3f", amount*0.001f);
87 case AST_AOC_MULT_ONEHUNDREDTH:
88 pj_ansi_snprintf(amount_str, amount_max_size,
"%.2f", amount*0.01f);
90 case AST_AOC_MULT_ONETENTH:
91 pj_ansi_snprintf(amount_str, amount_max_size,
"%.1f", amount*0.1f);
93 case AST_AOC_MULT_ONE:
94 pj_ansi_snprintf(amount_str, amount_max_size,
"%d", amount);
96 case AST_AOC_MULT_TEN:
97 pj_ansi_snprintf(amount_str, amount_max_size,
"%d", amount*10);
99 case AST_AOC_MULT_HUNDRED:
100 pj_ansi_snprintf(amount_str, amount_max_size,
"%d", amount*100);
102 case AST_AOC_MULT_THOUSAND:
103 pj_ansi_snprintf(amount_str, amount_max_size,
"%d", amount*1000);
106 pj_ansi_snprintf(amount_str, amount_max_size,
"%d", amount);
112 static const char *aoc_time_scale_str(
enum ast_aoc_time_scale value)
118 case AST_AOC_TIME_SCALE_HUNDREDTH_SECOND:
119 str =
"one-hundredth-second";
121 case AST_AOC_TIME_SCALE_TENTH_SECOND:
122 str =
"one-tenth-second";
124 case AST_AOC_TIME_SCALE_SECOND:
127 case AST_AOC_TIME_SCALE_TEN_SECOND:
130 case AST_AOC_TIME_SCALE_MINUTE:
133 case AST_AOC_TIME_SCALE_HOUR:
136 case AST_AOC_TIME_SCALE_DAY:
137 str =
"twenty-four-hours";
143 static void aoc_datastore_destroy(
void *obj)
151 .destroy = aoc_datastore_destroy,
156 .destroy = aoc_datastore_destroy,
161 .destroy = aoc_datastore_destroy,
170 static void aoc_release_pool(
void * data)
172 pj_pool_t *pool = data;
173 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
176 static int aoc_send_as_xml(
void * data)
179 RAII_VAR(pj_pool_t *, pool, NULL, aoc_release_pool);
181 pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(),
"AOC", 2048, 512);
184 ast_log(LOG_ERROR,
"Could not create a memory pool for AOC XML\n");
191 pj_xml_node *aoc_type;
192 pj_xml_node *charging_info = NULL;
193 pj_xml_node *charges;
197 const size_t xml_max_size = 512;
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,
205 charging_info = aoc_xml_create_node(pool, aoc_type,
"charging-info");
206 aoc_xml_set_node_content(pool, charging_info,
209 charges = aoc_xml_create_node(pool, aoc_type,
"recorded-charges");
212 charge = aoc_xml_create_node(pool, charges,
"free-charge");
215 charge = aoc_xml_create_node(pool, charges,
"recorded-currency-units");
217 charge = aoc_xml_create_node(pool, charges,
"not-available");
221 const char *currency;
226 if (!ast_strlen_zero(currency)) {
227 pj_xml_node *currency_id;
229 currency_id = aoc_xml_create_node(pool, charge,
"currency-id");
230 aoc_xml_set_node_content(pool, currency_id, currency);
233 amount = aoc_xml_create_node(pool, charge,
"currency-amount");
236 aoc_xml_set_node_content(pool, amount, amount_str);
238 pj_xml_node *currency_id;
241 currency_id = aoc_xml_create_node(pool, charge,
"currency-id");
242 aoc_xml_set_node_content(pool, currency_id,
"UNIT");
249 amount = aoc_xml_create_node(pool, charge,
"currency-amount");
250 amount_str = aoc_format_amount(pool, unit_entry->amount,
252 aoc_xml_set_node_content(pool, amount, amount_str);
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");
266 ast_sip_session_get_datastore(adata->session, aoc_d_datastore.
type),
268 struct pjsip_tx_data *tdata;
270 .
type =
"application",
271 .subtype =
"vnd.etsi.aoc+xml",
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");
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);
285 ast_sip_session_send_request(adata->session, tdata);
288 datastore = ast_sip_session_alloc_datastore(&aoc_d_datastore, aoc_d_datastore.
type);
290 ast_log(LOG_ERROR,
"Unable to create datastore for AOC-D.\n");
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");
299 ast_free(datastore->data);
302 aoc_xml_set_node_content(pool, charging_info,
"total");
303 size = pj_xml_print(aoc, xml, xml_max_size - 1, PJ_TRUE);
308 ast_sip_session_get_datastore(adata->session, aoc_e_datastore.
type),
311 datastore = ast_sip_session_alloc_datastore(&aoc_e_datastore, aoc_e_datastore.
type);
313 ast_log(LOG_ERROR,
"Unable to create datastore for AOC-E.\n");
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");
322 ast_free(datastore->data);
328 pj_xml_node *aoc_type;
329 pj_xml_node *charged_items;
334 const size_t xml_max_size = 1024;
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");
343 pj_xml_node *charged_item;
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");
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");
373 if (entry->rate_type == AST_AOC_RATE_TYPE_DURATION ||
374 entry->rate_type == AST_AOC_RATE_TYPE_FLAT) {
375 const char *currency;
381 currency = (entry->rate_type == AST_AOC_RATE_TYPE_DURATION ?
384 if (!ast_strlen_zero(currency)) {
385 pj_xml_node *currency_id;
387 currency_id = aoc_xml_create_node(pool, charge,
"currency-id");
388 aoc_xml_set_node_content(pool, currency_id, currency);
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);
400 if (entry->rate_type == AST_AOC_RATE_TYPE_DURATION) {
401 pj_xml_node *length_time_unit;
402 pj_xml_node *time_unit;
405 pj_xml_node *charging_type;
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,
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,
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");
431 adata->session->call_direction == AST_SIP_SESSION_OUTGOING_CALL) {
432 struct pjsip_tx_data *tdata;
434 .
type =
"application",
435 .subtype =
"vnd.etsi.aoc+xml",
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");
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);
449 ast_sip_session_send_request(adata->session, tdata);
452 ast_sip_session_get_datastore(adata->session, aoc_s_datastore.
type),
455 datastore = ast_sip_session_alloc_datastore(&aoc_s_datastore, aoc_s_datastore.
type);
457 ast_log(LOG_ERROR,
"Unable to create datastore for AOC-S.\n");
460 if (ast_sip_session_add_datastore(adata->session, datastore)) {
461 ast_log(LOG_ERROR,
"Unable to create datastore for AOC-S.\n");
465 ast_free(datastore->data);
474 static void aoc_data_destroy(
void * data)
479 ao2_cleanup(adata->session);
493 adata = ao2_alloc(
sizeof(
struct aoc_data), aoc_data_destroy);
495 ast_log(LOG_ERROR,
"Failed to allocate AOC data\n");
500 if (!adata->decoded) {
501 ast_log(LOG_ERROR,
"Error decoding indicated AOC data\n");
506 channel = ast_channel_tech_pvt(ast);
511 ast_log(LOG_ERROR,
"Unable to send AOC XML for channel %s\n", ast_channel_name(ast));
526 .
version = AST_FRAMEHOOK_INTERFACE_VERSION,
527 .event_cb = aoc_framehook,
528 .consume_cb = aoc_consume,
535 ast_channel_lock(session->
channel);
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));
543 ast_channel_unlock(session->
channel);
546 static int aoc_incoming_invite_request(
struct ast_sip_session *session,
547 struct pjsip_rx_data *rdata)
549 aoc_attach_framehook(session);
553 static void aoc_outgoing_invite_request(
struct ast_sip_session *session,
554 struct pjsip_tx_data *tdata)
556 aoc_attach_framehook(session);
560 struct pjsip_tx_data *tdata)
563 .
type =
"application",
564 .subtype =
"vnd.etsi.aoc+xml",
567 aoc_d_datastore.
type), ao2_cleanup);
569 aoc_e_datastore.
type), ao2_cleanup);
573 }
else if (datastore_d) {
580 if (ast_sip_add_body(tdata, &body)) {
581 ast_log(LOG_ERROR,
"Could not add body to AOC INFO request\n");
586 struct pjsip_tx_data *tdata)
589 .
type =
"application",
590 .subtype =
"vnd.etsi.aoc+xml",
593 aoc_d_datastore.
type), ao2_cleanup);
595 aoc_e_datastore.
type), ao2_cleanup);
599 }
else if (datastore_d) {
606 if (ast_sip_add_body(tdata, &body)) {
607 ast_log(LOG_ERROR,
"Could not add body to AOC INFO request\n");
611 static void aoc_invite_outgoing_response(
struct ast_sip_session *session,
612 struct pjsip_tx_data *tdata)
614 pjsip_msg_body *multipart_body;
615 pjsip_multipart_part *part;
620 aoc_s_datastore.
type), ao2_cleanup);
622 if (tdata->msg->line.status.code != 180 && tdata->msg->line.status.code != 183 &&
623 tdata->msg->line.status.code != 200) {
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;
635 pjsip_sdp_info *tdata_sdp_info;
637 tdata_sdp_info = pjsip_tdata_get_sdp_info(tdata);
638 if (tdata_sdp_info->sdp) {
641 rc = pjsip_create_multipart_sdp_body(tdata->pool, tdata_sdp_info->sdp,
643 if (rc != PJ_SUCCESS) {
644 ast_log(LOG_ERROR,
"Unable to create sdp multipart body\n");
648 multipart_body = pjsip_multipart_create(tdata->pool,
649 &pjsip_media_type_multipart_mixed, NULL);
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);
660 tdata->msg->body = multipart_body;
665 .priority = AST_SIP_SUPPLEMENT_PRIORITY_LAST,
666 .outgoing_request = aoc_bye_outgoing_request,
667 .outgoing_response = aoc_bye_outgoing_response,
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,
678 static int load_module(
void)
680 ast_sip_session_register_supplement(&aoc_bye_supplement);
681 ast_sip_session_register_supplement(&aoc_invite_supplement);
685 static int unload_module(
void)
687 ast_sip_session_unregister_supplement(&aoc_bye_supplement);
688 ast_sip_session_unregister_supplement(&aoc_invite_supplement);
692 AST_MODULE_INFO(
ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER,
"PJSIP AOC Support",
693 .support_level = AST_MODULE_SUPPORT_EXTENDED,
695 .unload = unload_module,
697 .requires =
"res_pjsip",
Main Channel structure associated with a channel.
struct ast_sip_endpoint * endpoint
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.
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
void * ast_aoc_destroy_decoded(struct ast_aoc_decoded *decoded)
free an ast_aoc_decoded object
Structure for a data store type.
ast_channel_state
ast_channel states
A structure which contains a channel implementation and session.
struct ast_sip_session * session
Pointer to session.
uint8_t charging_type
Charging interval type.
#define ast_strdup(str)
A wrapper for strdup()
Structure for a data store object.
ast_aoc_currency_multiplier
Defines the currency multiplier for an aoc message.
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.
struct ast_frame_subclass subclass
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
unsigned int ast_aoc_s_get_count(struct ast_aoc_decoded *decoded)
get the number rates associated with an AOC-S message
int ast_framehook_attach(struct ast_channel *chan, struct ast_framehook_interface *i)
Attach an framehook onto a channel for frame interception.
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.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
char currency_name[AOC_CURRENCY_NAME_SIZE]
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
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.
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.
union ast_frame::@224 data
char currency_name[AOC_CURRENCY_NAME_SIZE]
const char * ast_aoc_get_currency_name(struct ast_aoc_decoded *decoded)
get the currency name for AOC-D and AOC-E messages
A supplement to SIP message processing.
struct ast_frame ast_null_frame
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
Data structure associated with a single frame of data.
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
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
#define ASTERISK_GPL_KEY
The text the key() function should return.
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.