diff --git a/modules/b2b_logic/b2b_logic.c b/modules/b2b_logic/b2b_logic.c index 70e81212346..7f792262f25 100644 --- a/modules/b2b_logic/b2b_logic.c +++ b/modules/b2b_logic/b2b_logic.c @@ -57,6 +57,7 @@ static void mod_destroy(void); static int child_init(int rank); static int fixup_init_flags(void** param); static int fixup_free_init_flags(void** param); +static int fixup_bridge_flags(void** param); static int fixup_init_id(void** param); static int fixup_check_avp(void** param); static int fixup_route(void** param); @@ -208,7 +209,8 @@ static cmd_export_t cmds[]= {CMD_PARAM_STR,0,0}, {CMD_PARAM_STR,0,0}, {CMD_PARAM_STR|CMD_PARAM_OPT,0,0}, - {CMD_PARAM_INT|CMD_PARAM_OPT,0,0}, {0,0,0}}, + {CMD_PARAM_STR|CMD_PARAM_OPT|CMD_PARAM_FIX_NULL, + fixup_bridge_flags, fixup_free_init_flags}, {0,0,0}}, REQUEST_ROUTE}, {"b2b_bridge_request", (cmd_function)b2b_bridge_request, {{CMD_PARAM_STR,0,0}, {CMD_PARAM_INT,0,0}, {0,0,0}}, @@ -727,6 +729,50 @@ static int fixup_free_init_flags(void** param) return 0; } +static int fixup_bridge_flags(void** param) +{ + str *s = (str*)*param; + int st; + struct b2b_bridge_params *bridge_params; + + bridge_params = pkg_malloc(sizeof *bridge_params); + if (!bridge_params) { + LM_ERR("out of pkg memory\n"); + return -1; + } + memset(bridge_params, 0, sizeof *bridge_params); + + bridge_params->lifetime = 0; + + *param = (void*)bridge_params; + + if (!s) + return 0; + + for( st=0 ; st< s->len ; st++ ) { + switch (s->s[st]) + { + case 't': + while (stlen-1 && isdigit(s->s[st+1])) { + bridge_params->lifetime = + bridge_params->lifetime*10 + s->s[st+1] - '0'; + st++; + } + break; + case 'n': + bridge_params->flags |= B2BL_BR_FLAG_NOTIFY; + break; + case 'f': + bridge_params->flags |= B2BL_BR_FLAG_RETURN_AFTER_FAILURE; + break; + default: + LM_WARN("unknown option `%c'\n", s->s[st]); + } + } + + return 0; +} + static int fixup_init_id(void** param) { str *s = (str*)*param; diff --git a/modules/b2b_logic/b2b_logic.h b/modules/b2b_logic/b2b_logic.h index 54c7cae61a1..299f868bec8 100644 --- a/modules/b2b_logic/b2b_logic.h +++ b/modules/b2b_logic/b2b_logic.h @@ -53,6 +53,10 @@ #define B2BL_FLAG_TRANSPARENT_TO 0x02 #define B2BL_FLAG_USE_INIT_SDP 0x04 +/* B2BL_BR_FLAGS constants */ +#define B2BL_BR_FLAG_NOTIFY 0x01 +#define B2BL_BR_FLAG_RETURN_AFTER_FAILURE 0x02 + /* modes to write in db */ #define NO_DB 0 #define WRITE_THROUGH 1 @@ -84,6 +88,12 @@ struct b2b_params str *init_body_type; }; +struct b2b_bridge_params +{ + unsigned int flags; + unsigned int lifetime; +}; + enum pv_entity_field { PV_ENTITY_KEY, PV_ENTITY_CALLID, diff --git a/modules/b2b_logic/doc/b2b_logic_admin.xml b/modules/b2b_logic/doc/b2b_logic_admin.xml index 3d9f2904b70..83ddc26e5f9 100644 --- a/modules/b2b_logic/doc/b2b_logic_admin.xml +++ b/modules/b2b_logic/doc/b2b_logic_admin.xml @@ -635,7 +635,7 @@ b2b_client_new("client1", "sip:alice@opensips.org");
- <function moreinfo="none">b2b_bridge(entity1, entity2, [provmedia_uri], [lifetime])</function> + <function moreinfo="none">b2b_bridge(entity1, entity2, [provmedia_uri], [flags])</function> This function bridges two entities, in the context of an existing B2B session @@ -660,9 +660,23 @@ b2b_client_new("client1", "sip:alice@opensips.org"); media server to be connected with the caller while the callee answers. - lifetime (int, optional) - maximum duration of the B2B - session. If the lifetime expires, the B2BUA will send BYE messages to both - ends and delete the record. + flags (string, optional) - meanings of the flags is as follows: + + + t[nn] - Maximum duration of the B2B + session. If the lifetime expires, the B2BUA will send BYE messages to both + ends and delete the record. + Example: t300. + + + n - Enable rfc3515 NOTIFY to inform the agent sending the + REFER of the status of the reference. + + + f - Rollback call to state before bridging in case of transfer + failed, don't hangup the call (default behaviour). + + diff --git a/modules/b2b_logic/logic.c b/modules/b2b_logic/logic.c index 8c838c5210b..a8ddf013252 100644 --- a/modules/b2b_logic/logic.c +++ b/modules/b2b_logic/logic.c @@ -84,6 +84,7 @@ static str method_invite= {INVITE, INVITE_LEN}; static str method_ack = {ACK, ACK_LEN}; static str method_bye = {BYE, BYE_LEN}; static str method_cancel= {CANCEL, CANCEL_LEN}; +static str method_notify= {NOTIFY, NOTIFY_LEN}; static str ok = str_init("OK"); static str notAcceptable = str_init("Not Acceptable"); @@ -473,9 +474,25 @@ int process_bridge_dialog_end(b2bl_tuple_t* tuple, unsigned int hash_index, } else { - /* the entity to connect replied with negative reply */ - b2b_end_dialog(tuple->bridge_entities[0], tuple, hash_index); - b2b_mark_todel(tuple); + if(tuple->bridge_flags & B2BL_BR_FLAG_RETURN_AFTER_FAILURE) + { + /* Delete failed entity */ + b2bl_delete_entity(bentity, tuple, hash_index, 1); + + /* Restore initial bridge */ + tuple->bridge_entities[1] = tuple->bridge_entities[0]; + tuple->bridge_entities[0] = tuple->servers[0]; + + tuple->bridge_entities[1]->peer = tuple->bridge_entities[0]; + tuple->bridge_entities[0]->peer = tuple->bridge_entities[1]; + + /* Disable bridging state */ + tuple->state = B2B_NOTDEF_STATE; + } else { + /* the entity to connect replied with negative reply */ + b2b_end_dialog(tuple->bridge_entities[0], tuple, hash_index); + b2b_mark_todel(tuple); + } } } else @@ -512,6 +529,45 @@ int process_bridge_bye(struct sip_msg* msg, b2bl_tuple_t* tuple, return process_bridge_dialog_end(tuple, hash_index, entity_no, entity); } +int process_bridge_notify(b2bl_entity_id_t *entity, unsigned int hash_index, struct sip_msg* msg) +{ + b2b_req_data_t req_data; + static char def_hdrs[] = "Event: refer\r\nContent-Type: message/sipfrag\r\nSubscription-State: "; + static char buf[BUF_LEN]; + static str body = str_init("SIP/2.0 100 Trying"); + static str hdrs = {buf, 0}; + + memset(&req_data, 0, sizeof(b2b_req_data_t)); + PREP_REQ_DATA(entity); + req_data.method = &method_notify; + req_data.client_headers = &entity->hdrs; + req_data.body = 0; + if (!msg) { + hdrs.len = snprintf(buf, BUF_LEN, "%sactive;expires=%d\r\n", def_hdrs, 60); + } else { + body.s = msg->first_line.u.reply.version.s; + body.len = msg->first_line.u.reply.version.len + + msg->first_line.u.reply.status.len + + msg->first_line.u.reply.reason.len + 2; + hdrs.len = snprintf(buf, BUF_LEN, "%sterminated;reason=noresource\r\n", def_hdrs); + } + LM_DBG("Sending notify [%.*s]\n", body.len, body.s); + if ((unsigned)hdrs.len >= BUF_LEN) { + LM_ERR("Buffer is too small\n"); + return -1; + } + req_data.extra_headers = &hdrs; + req_data.body = &body; + b2bl_htable[hash_index].locked_by = process_no; + if (b2b_api.send_request(&req_data) < 0) { + LM_ERR("Failed to send NOTIFY\n"); + b2bl_htable[hash_index].locked_by = -1; + return -1; + } + b2bl_htable[hash_index].locked_by = -1; + + return 0; +} int process_bridge_negreply(b2bl_tuple_t* tuple, unsigned int hash_index, b2bl_entity_id_t* entity, struct sip_msg* msg) @@ -643,6 +699,7 @@ static b2bl_entity_id_t* b2bl_new_client(str* to_uri, str *proxy, str* from_uri, return entity; } + int process_bridge_200OK(struct sip_msg* msg, str* extra_headers, str* body, b2bl_tuple_t* tuple, unsigned int hash_index, b2bl_entity_id_t* entity) @@ -1123,15 +1180,26 @@ int _b2b_handle_reply(struct sip_msg *msg, b2bl_tuple_t *tuple, if (tuple->state == B2B_BRIDGING_STATE) { LM_DBG("Received a reply [%d] while in BRIDGING scenario\n", statuscode); + /* if the scenario state is B2B_BRIDGING_STATE -> we should have a reply for INVITE */ /* extract the method from Cseq header */ + if(method_value == METHOD_NOTIFY) goto done1; /* Silently ignore reply on NOTIFY */ + if(method_value != METHOD_INVITE) { LM_ERR("Wrong scenario state [B2B_BRIDGING_STATE] for this" " reply(for method %d)\n", method_value); goto error; } + + if(statuscode >= 200 && entity == tuple->bridge_entities[1]) /* Reply from new bridge entity */ + { + process_bridge_notify(tuple->servers[0], cur_route_ctx.hash_index, msg); + if(statuscode == 200 || !(tuple->bridge_flags & B2BL_BR_FLAG_RETURN_AFTER_FAILURE)) + b2bl_delete_entity(tuple->servers[0], tuple, tuple->hash_index, 1); + } + /* if a negative reply */ if(statuscode >= 300) { @@ -1996,7 +2064,7 @@ static struct b2bl_new_entity *get_ent_to_bridge(b2bl_tuple_t *tuple, } int b2b_scenario_bridge(struct sip_msg *msg, str *br_ent1_str, str *br_ent2_str, - str *provmedia_uri, int *lifetime) + str *provmedia_uri, struct b2b_bridge_params *params) { b2bl_tuple_t *tuple; b2bl_entity_id_t *entity, *e = NULL, *old_entity = NULL; @@ -2019,6 +2087,8 @@ int b2b_scenario_bridge(struct sip_msg *msg, str *br_ent1_str, str *br_ent2_str, goto done; } + tuple->bridge_flags = params->flags; + entity = b2bl_search_entity(tuple, &cur_route_ctx.entity_key, cur_route_ctx.entity_type, &entity_head); if(entity == NULL) @@ -2057,8 +2127,11 @@ int b2b_scenario_bridge(struct sip_msg *msg, str *br_ent1_str, str *br_ent2_str, } else if (!new_br_ent[1]) goto done; + if (params->flags & B2BL_BR_FLAG_NOTIFY) + process_bridge_notify(entity, cur_route_ctx.hash_index, NULL); + if (process_bridge_action(msg, tuple, cur_route_ctx.hash_index, - old_entity, new_br_ent, provmedia_uri, lifetime ? *lifetime : 0) < 0) { + old_entity, new_br_ent, provmedia_uri, params->lifetime) < 0) { LM_ERR("Failed to process bridge action\n"); goto done; } @@ -2744,7 +2817,7 @@ int process_bridge_action(struct sip_msg* msg, b2bl_tuple_t* tuple, else tuple->lifetime = -1; - LM_DBG("be[0]= %p, be1=[%p]\n", tuple->bridge_entities[0], tuple->bridge_entities[1]); + LM_DBG("be[0] = [%p], be[1] = [%p]\n", tuple->bridge_entities[0], tuple->bridge_entities[1]); return 0; error1: diff --git a/modules/b2b_logic/records.h b/modules/b2b_logic/records.h index a901b7f4b75..412801f312b 100644 --- a/modules/b2b_logic/records.h +++ b/modules/b2b_logic/records.h @@ -100,6 +100,7 @@ typedef struct b2bl_tuple b2bl_entity_id_t* servers[MAX_B2BL_ENT]; b2bl_entity_id_t* clients[MAX_B2BL_ENT]; b2bl_entity_id_t* bridge_entities[MAX_BRIDGE_ENT]; + int bridge_flags; int to_del; str* extra_headers; struct b2bl_tuple* next;