summaryrefslogtreecommitdiff
path: root/pjsip
diff options
context:
space:
mode:
Diffstat (limited to 'pjsip')
-rw-r--r--pjsip/include/pjsip/sip_msg.h4
-rw-r--r--pjsip/include/pjsip/sip_parser.h15
-rw-r--r--pjsip/include/pjsua-lib/pjsua.h29
-rw-r--r--pjsip/src/pjsip/sip_dialog.c18
-rw-r--r--pjsip/src/pjsip/sip_parser.c30
-rw-r--r--pjsip/src/pjsua-lib/pjsua_call.c191
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);