diff options
Diffstat (limited to 'pjsip')
-rw-r--r-- | pjsip/include/pjsip/sip_msg.h | 4 | ||||
-rw-r--r-- | pjsip/include/pjsip/sip_parser.h | 15 | ||||
-rw-r--r-- | pjsip/include/pjsua-lib/pjsua.h | 29 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_dialog.c | 18 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_parser.c | 30 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_call.c | 191 |
6 files changed, 254 insertions, 33 deletions
diff --git a/pjsip/include/pjsip/sip_msg.h b/pjsip/include/pjsip/sip_msg.h index b27b2708..6ae33085 100644 --- a/pjsip/include/pjsip/sip_msg.h +++ b/pjsip/include/pjsip/sip_msg.h @@ -431,9 +431,9 @@ typedef enum pjsip_status_code PJSIP_SC_DOES_NOT_EXIST_ANYWHERE = 604, PJSIP_SC_NOT_ACCEPTABLE_ANYWHERE = 606, - PJSIP_SC_TSX_TIMEOUT = 701, + PJSIP_SC_TSX_TIMEOUT = PJSIP_SC_REQUEST_TIMEOUT, /*PJSIP_SC_TSX_RESOLVE_ERROR = 702,*/ - PJSIP_SC_TSX_TRANSPORT_ERROR = 703 + PJSIP_SC_TSX_TRANSPORT_ERROR = PJSIP_SC_SERVICE_UNAVAILABLE } pjsip_status_code; diff --git a/pjsip/include/pjsip/sip_parser.h b/pjsip/include/pjsip/sip_parser.h index d0eb838e..72c103a7 100644 --- a/pjsip/include/pjsip/sip_parser.h +++ b/pjsip/include/pjsip/sip_parser.h @@ -24,7 +24,7 @@ * @brief SIP Message Parser */ -#include <pjsip/sip_types.h> +#include <pjsip/sip_msg.h> #include <pjlib-util/scanner.h> #include <pj/list.h> @@ -191,6 +191,19 @@ PJ_DECL(pjsip_uri*) pjsip_parse_uri( pj_pool_t *pool, unsigned options); /** + * Parse SIP status line. + * + * @param buf Text buffer to parse. + * @param size The size of the buffer. + * @param status_line Structure to receive the parsed elements. + * + * @return PJ_SUCCESS if a status line is parsed successfully. + */ +PJ_DECL(pj_status_t) pjsip_parse_status_line(char *buf, pj_size_t size, + pjsip_status_line *status_line); + + +/** * Parse a packet buffer and build a full SIP message from the packet. This * function parses all parts of the message, including request/status line, * all headers, and the message body. The message body however is only diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 2b11df01..39d5caf3 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -309,9 +309,32 @@ typedef struct pjsua_callback * is not defined, the default behavior is to accept the * transfer. */ - void (*on_call_transfered)(pjsua_call_id call_id, - const pj_str_t *dst, - pjsip_status_code *code); + void (*on_call_transfer_request)(pjsua_call_id call_id, + const pj_str_t *dst, + pjsip_status_code *code); + + /** + * Notify application of the status of previously sent call + * transfer request. Application can monitor the status of the + * call transfer request, for example to decide whether to + * terminate existing call. + * + * @param call_id Call ID. + * @param status_code Status progress of the transfer request. + * @param status_text Status progress text. + * @param final If non-zero, no further notification will + * be reported. The status_code specified in + * this callback is the final status. + * @param p_cont Initially will be set to non-zero, application + * can set this to FALSE if it no longer wants + * to receie further notification (for example, + * after it hangs up the call). + */ + void (*on_call_transfer_status)(pjsua_call_id call_id, + int status_code, + const pj_str_t *status_text, + pj_bool_t final, + pj_bool_t *p_cont); /** * Notify application when registration status has changed. diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c index aab06704..8c2f287c 100644 --- a/pjsip/src/pjsip/sip_dialog.c +++ b/pjsip/src/pjsip/sip_dialog.c @@ -793,9 +793,23 @@ PJ_DEF(pj_status_t) pjsip_dlg_add_usage( pjsip_dialog *dlg, */ for (index=0; index<dlg->usage_cnt; ++index) { if (dlg->usage[index] == mod) { - pj_assert(!"This module is already registered"); + /* Module may be registered more than once in the same dialog. + * For example, when call transfer fails, application may retry + * call transfer on the same dialog. + * So return PJ_SUCCESS here. + */ + PJ_LOG(4,(dlg->obj_name, + "Module %.*s already registered as dialog usage, " + "updating the data %p", + (int)mod->name.slen, mod->name.ptr, mod_data)); + dlg->mod_data[mod->id] = mod_data; + pjsip_dlg_dec_lock(dlg); - return PJSIP_ETYPEEXISTS; + return PJ_SUCCESS; + + //pj_assert(!"This module is already registered"); + //pjsip_dlg_dec_lock(dlg); + //return PJSIP_ETYPEEXISTS; } if (dlg->usage[index]->priority > mod->priority) diff --git a/pjsip/src/pjsip/sip_parser.c b/pjsip/src/pjsip/sip_parser.c index b3f13bdb..c3f456a7 100644 --- a/pjsip/src/pjsip/sip_parser.c +++ b/pjsip/src/pjsip/sip_parser.c @@ -1471,6 +1471,36 @@ static void int_parse_status_line( pj_scanner *scanner, pj_scan_get_newline( scanner ); } + +/* + * Public API to parse SIP status line. + */ +PJ_DEF(pj_status_t) pjsip_parse_status_line( char *buf, pj_size_t size, + pjsip_status_line *status_line) +{ + pj_scanner scanner; + PJ_USE_EXCEPTION; + + pj_bzero(status_line, sizeof(*status_line)); + pj_scan_init(&scanner, buf, size, 0, &on_syntax_error); + + PJ_TRY { + int_parse_status_line(&scanner, status_line); + } + PJ_CATCH_ANY { + /* Tolerate the error if it is caused only by missing newline */ + if (status_line->code == 0 && status_line->reason.slen == 0) { + pj_scan_fini(&scanner); + return PJSIP_EINVALIDMSG; + } + } + PJ_END; + + pj_scan_fini(&scanner); + return PJ_SUCCESS; +} + + /* Parse ending of header. */ static void parse_hdr_end( pj_scanner *scanner ) { diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index d3b88a7b..36e5f737 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -71,7 +71,19 @@ static pj_status_t create_inactive_sdp(pjsua_call *call, * Callback called by event framework when the xfer subscription state * has changed. */ -static void xfer_on_evsub_state( pjsip_evsub *sub, pjsip_event *event); +static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event); +static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event); + +/* + * Callback called by event framework when NOTIFY is received for outgoing + * REFER subscription. + */ +static void xfer_on_rx_notify(pjsip_evsub *sub, + pjsip_rx_data *rdata, + int *p_st_code, + pj_str_t **p_st_text, + pjsip_hdr *res_hdr, + pjsip_msg_body **p_body); /* * Reset call descriptor. @@ -1078,7 +1090,7 @@ PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id, /* Create xfer client subscription. */ pj_bzero(&xfer_cb, sizeof(xfer_cb)); - xfer_cb.on_evsub_state = &xfer_on_evsub_state; + xfer_cb.on_evsub_state = &xfer_client_on_evsub_state; status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub); if (status != PJ_SUCCESS) { @@ -1087,6 +1099,9 @@ PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id, return status; } + /* Associate this call with the client subscription */ + pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call); + /* * Create REFER request. */ @@ -2144,37 +2159,23 @@ static void pjsua_call_on_rx_offer(pjsip_inv_session *inv, * Callback called by event framework when the xfer subscription state * has changed. */ -static void xfer_on_evsub_state( pjsip_evsub *sub, pjsip_event *event) +static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event) { PJ_UNUSED_ARG(event); /* - * When subscription is terminated, clear the xfer_sub member of - * the inv_data. - */ - if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { - pjsua_call *call; - - call = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id); - if (!call) - return; - - pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL); - call->xfer_sub = NULL; - - PJ_LOG(4,(THIS_FILE, "Xfer subscription terminated")); - - } - /* * When subscription is accepted (got 200/OK to REFER), check if * subscription suppressed. */ - else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) { + if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) { pjsip_rx_data *rdata; pjsip_generic_string_hdr *refer_sub; const pj_str_t REFER_SUB = { "Refer-Sub", 9 }; + pjsua_call *call; + + call = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id); /* Must be receipt of response message */ pj_assert(event->type == PJSIP_EVENT_TSX_STATE && @@ -2188,13 +2189,152 @@ static void xfer_on_evsub_state( pjsip_evsub *sub, pjsip_event *event) /* Check if subscription is suppressed */ if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) { + /* Since no subscription is desired, assume that call has been + * transfered successfully. + */ + if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) { + const pj_str_t ACCEPTED = { "Accepted", 8 }; + pj_bool_t cont = PJ_FALSE; + (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index, + 200, + &ACCEPTED, + PJ_TRUE, + &cont); + } + /* Yes, subscription is suppressed. * Terminate our subscription now. */ PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating " "event subcription...")); pjsip_evsub_terminate(sub, PJ_TRUE); + + } else { + /* Notify application about call transfer progress. + * Initially notify with 100/Accepted status. + */ + if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) { + const pj_str_t ACCEPTED = { "Accepted", 8 }; + pj_bool_t cont = PJ_FALSE; + (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index, + 100, + &ACCEPTED, + PJ_FALSE, + &cont); + } + } + } + /* + * On incoming NOTIFY, notify application about call transfer progress. + */ + else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE || + pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) + { + pjsua_call *call; + pjsip_msg *msg; + pjsip_msg_body *body; + pjsip_status_line status_line; + pj_bool_t is_last; + pj_bool_t cont; + pj_status_t status; + + call = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id); + + /* When subscription is terminated, clear the xfer_sub member of + * the inv_data. + */ + if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { + pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL); + PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated")); + + } + + if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) { + /* Application is not interested with call progress status */ + return; } + + /* This better be a NOTIFY request */ + if (event->type == PJSIP_EVENT_TSX_STATE && + event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) + { + pjsip_rx_data *rdata; + + rdata = event->body.tsx_state.src.rdata; + + /* Check if there's body */ + msg = rdata->msg_info.msg; + body = msg->body; + if (!body) { + PJ_LOG(4,(THIS_FILE, + "Warning: received NOTIFY without message body")); + return; + } + + /* Check for appropriate content */ + if (pj_stricmp2(&body->content_type.type, "message") != 0 || + pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0) + { + PJ_LOG(4,(THIS_FILE, + "Warning: received NOTIFY with non message/sipfrag " + "content")); + return; + } + + /* Try to parse the content */ + status = pjsip_parse_status_line(body->data, body->len, + &status_line); + if (status != PJ_SUCCESS) { + PJ_LOG(4,(THIS_FILE, + "Warning: received NOTIFY with invalid " + "message/sipfrag content")); + return; + } + + } else { + status_line.code = 500; + status_line.reason = *pjsip_get_status_text(500); + } + + /* Notify application */ + is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED); + cont = !is_last; + (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index, + status_line.code, + &status_line.reason, + is_last, &cont); + + if (!cont) { + pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL); + } + } +} + + +/* + * Callback called by event framework when the xfer subscription state + * has changed. + */ +static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event) +{ + + PJ_UNUSED_ARG(event); + + /* + * When subscription is terminated, clear the xfer_sub member of + * the inv_data. + */ + if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { + pjsua_call *call; + + call = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id); + if (!call) + return; + + pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL); + call->xfer_sub = NULL; + + PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated")); } } @@ -2246,9 +2386,10 @@ static void on_call_transfered( pjsip_inv_session *inv, /* Notify callback */ code = PJSIP_SC_OK; - if (pjsua_var.ua_cfg.cb.on_call_transfered) - (*pjsua_var.ua_cfg.cb.on_call_transfered)(existing_call->index, - &refer_to->hvalue, &code); + if (pjsua_var.ua_cfg.cb.on_call_transfer_request) + (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index, + &refer_to->hvalue, + &code); if (code < 200) code = 200; @@ -2304,7 +2445,7 @@ static void on_call_transfered( pjsip_inv_session *inv, /* Init callback */ pj_bzero(&xfer_cb, sizeof(xfer_cb)); - xfer_cb.on_evsub_state = &xfer_on_evsub_state; + xfer_cb.on_evsub_state = &xfer_server_on_evsub_state; /* Init additional header list to be sent with REFER response */ pj_list_init(&hdr_list); |