summaryrefslogtreecommitdiff
path: root/pjsip
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2006-10-18 17:16:34 +0000
committerBenny Prijono <bennylp@teluu.com>2006-10-18 17:16:34 +0000
commit487c0862f3e84a42d6fd0e73561c12e0c0db3c9f (patch)
tree36517e0f94e24c32840afe55082bd57e9619a6d7 /pjsip
parent44a124d9f6fcf4ecef9574aa66bf26b4f5018f57 (diff)
Some changes to REFER handling in PJSUA-LIB:
- added callback to report call transfer progress - changed the call transfer request callback name in pjsua - added "--norefersub" option in pjsua. - fixed bug when call transfer is done more than once in the same dialog (dialog usage can not be added) Also removed 7xx status from the SIP status codes. And added pjsip_parse_status_line() to parse sipfrag. git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@780 74dad513-b988-da41-8d7b-12977e46ad98
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);