summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2008-07-17 14:19:10 +0000
committerBenny Prijono <bennylp@teluu.com>2008-07-17 14:19:10 +0000
commit85a2dd3b4a8aff9faf95415c8e8384ee2999cd04 (patch)
treec8e1d31762c36f65b7b539e4c417b6cc7d678f08
parentd6f361a21e8709fe8b18d8a3bd673f830f3920e9 (diff)
Ticket #192: Add callback to notify application about incoming SUBSCRIBE request, and add subscription state and termination reason in buddy info
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@2150 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjsip/include/pjsua-lib/pjsua.h128
-rw-r--r--pjsip/include/pjsua-lib/pjsua_internal.h15
-rw-r--r--pjsip/src/pjsua-lib/pjsua_pres.c222
3 files changed, 328 insertions, 37 deletions
diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h
index cbda0587..326f5306 100644
--- a/pjsip/include/pjsua-lib/pjsua.h
+++ b/pjsip/include/pjsua-lib/pjsua.h
@@ -393,6 +393,11 @@ typedef int pjsua_recorder_id;
/** Conference port identification */
typedef int pjsua_conf_port_id;
+/** Opaque declaration for server side presence subscription */
+typedef struct pjsua_srv_pres pjsua_srv_pres;
+
+/** Forward declaration for pjsua_msg_data */
+typedef struct pjsua_msg_data pjsua_msg_data;
/**
@@ -793,6 +798,79 @@ typedef struct pjsua_callback
void (*on_reg_state)(pjsua_acc_id acc_id);
/**
+ * Notification when incoming SUBSCRIBE request is received. Application
+ * may use this callback to authorize the incoming subscribe request
+ * (e.g. ask user permission if the request should be granted).
+ *
+ * If this callback is not implemented, all incoming presence subscription
+ * requests will be accepted.
+ *
+ * If this callback is implemented, application has several choices on
+ * what to do with the incoming request:
+ * - it may reject the request immediately by specifying non-200 class
+ * final response in the \a code argument.
+ * - it may immediately accept the request by specifying 200 as the
+ * \a code argument. This is the default value if application doesn't
+ * set any value to the \a code argument. In this case, the library
+ * will automatically send NOTIFY request upon returning from this
+ * callback.
+ * - it may delay the processing of the request, for example to request
+ * user permission whether to accept or reject the request. In this
+ * case, the application MUST set the \a code argument to 202, and
+ * later calls #pjsua_pres_notify() to accept or reject the
+ * subscription request.
+ *
+ * Any \a code other than 200 and 202 will be treated as 200.
+ *
+ * Application MUST return from this callback immediately (e.g. it must
+ * not block in this callback while waiting for user confirmation).
+ *
+ * @param srv_pres Server presence subscription instance. If
+ * application delays the acceptance of the request,
+ * it will need to specify this object when calling
+ * #pjsua_pres_notify().
+ * @param acc_id Account ID most appropriate for this request.
+ * @param buddy_id ID of the buddy matching the sender of the
+ * request, if any, or PJSUA_INVALID_ID if no
+ * matching buddy is found.
+ * @param from The From URI of the request.
+ * @param rdata The incoming request.
+ * @param code The status code to respond to the request. The
+ * default value is 200. Application may set this
+ * to other final status code to accept or reject
+ * the request.
+ * @param reason The reason phrase to respond to the request.
+ * @param msg_data If the application wants to send additional
+ * headers in the response, it can put it in this
+ * parameter.
+ */
+ void (*on_incoming_subscribe)(pjsua_acc_id acc_id,
+ pjsua_srv_pres *srv_pres,
+ pjsua_buddy_id buddy_id,
+ const pj_str_t *from,
+ pjsip_rx_data *rdata,
+ pjsip_status_code *code,
+ pj_str_t *reason,
+ pjsua_msg_data *msg_data);
+
+ /**
+ * Notification when server side subscription state has changed.
+ * This callback is optional as application normally does not need
+ * to do anything to maintain server side presence subscription.
+ *
+ * @param acc_id The account ID.
+ * @param srv_pres Server presence subscription object.
+ * @param remote_uri Remote URI string.
+ * @param state New subscription state.
+ * @param event PJSIP event that triggers the state change.
+ */
+ void (*on_srv_subscribe_state)(pjsua_acc_id acc_id,
+ pjsua_srv_pres *srv_pres,
+ const pj_str_t *remote_uri,
+ pjsip_evsub_state state,
+ pjsip_event *event);
+
+ /**
* Notify application when the buddy state has changed.
* Application may then query the buddy into to get the details.
*
@@ -1193,7 +1271,7 @@ PJ_DECL(void) pjsua_config_dup(pj_pool_t *pool,
msg_data = py_pjsua.msg_data_init()
* \endcode
*/
-typedef struct pjsua_msg_data
+struct pjsua_msg_data
{
/**
* Additional message headers as linked list. Application can add
@@ -1221,7 +1299,7 @@ typedef struct pjsua_msg_data
*/
pj_str_t msg_body;
-} pjsua_msg_data;
+};
/**
@@ -2412,7 +2490,6 @@ PJ_DECL(pj_status_t) pjsua_acc_set_online_status2(pjsua_acc_id acc_id,
PJ_DECL(pj_status_t) pjsua_acc_set_registration(pjsua_acc_id acc_id,
pj_bool_t renew);
-
/**
* Get information about the specified account.
*
@@ -3352,6 +3429,22 @@ typedef struct pjsua_buddy_info
pj_bool_t monitor_pres;
/**
+ * If \a monitor_pres is enabled, this specifies the last state of the
+ * presence subscription. If presence subscription session is currently
+ * active, the value will be PJSIP_EVSUB_STATE_ACTIVE. If presence
+ * subscription request has been rejected, the value will be
+ * PJSIP_EVSUB_STATE_TERMINATED, and the termination reason will be
+ * specified in \a sub_term_reason.
+ */
+ pjsip_evsub_state sub_state;
+
+ /**
+ * Specifies the last presence subscription terminatino reason. If
+ * presence subscription is currently active, the value will be empty.
+ */
+ pj_str_t sub_term_reason;
+
+ /**
* Extended RPID information about the person.
*/
pjrpid_element rpid;
@@ -3521,6 +3614,35 @@ PJ_DECL(pj_status_t) pjsua_buddy_update_pres(pjsua_buddy_id buddy_id);
/**
+ * Send NOTIFY to inform account presence status or to terminate server
+ * side presence subscription. If application wants to reject the incoming
+ * request, it should set the \a state to PJSIP_EVSUB_STATE_TERMINATED.
+ *
+ * @param acc_id Account ID.
+ * @param srv_pres Server presence subscription instance.
+ * @param state New state to set.
+ * @param state_str Optionally specify the state string name, if state
+ * is not "active", "pending", or "terminated".
+ * @param reason If the new state is PJSIP_EVSUB_STATE_TERMINATED,
+ * optionally specify the termination reason.
+ * @param with_body If the new state is PJSIP_EVSUB_STATE_TERMINATED,
+ * this specifies whether the NOTIFY request should
+ * contain message body containing account's presence
+ * information.
+ * @param msg_data Optional list of headers to be sent with the NOTIFY
+ * request.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsua_pres_notify(pjsua_acc_id acc_id,
+ pjsua_srv_pres *srv_pres,
+ pjsip_evsub_state state,
+ const pj_str_t *state_str,
+ const pj_str_t *reason,
+ pj_bool_t with_body,
+ const pjsua_msg_data *msg_data);
+
+/**
* Dump presence subscriptions to log.
*
* @param verbose Yes or no.
diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h
index 3660540f..7245469c 100644
--- a/pjsip/include/pjsua-lib/pjsua_internal.h
+++ b/pjsip/include/pjsua-lib/pjsua_internal.h
@@ -85,12 +85,15 @@ typedef struct pjsua_call
/**
* Server presence subscription list head.
*/
-typedef struct pjsua_srv_pres
+struct pjsua_srv_pres
{
PJ_DECL_LIST_MEMBER(struct pjsua_srv_pres);
- pjsip_evsub *sub;
- char *remote;
-} pjsua_srv_pres;
+ pjsip_evsub *sub; /**< The evsub. */
+ char *remote; /**< Remote URI. */
+ int acc_id; /**< Account ID. */
+ pjsip_dialog *dlg; /**< Dialog. */
+ int expires; /**< "expires" value in the request. */
+};
/**
@@ -151,6 +154,9 @@ typedef struct pjsua_transport_data
} pjsua_transport_data;
+/** Maximum length of subscription termination reason. */
+#define PJSUA_BUDDY_SUB_TERM_REASON_LEN 32
+
/**
* Buddy data.
*/
@@ -167,6 +173,7 @@ typedef struct pjsua_buddy
pj_bool_t monitor; /**< Should we monitor? */
pjsip_dialog *dlg; /**< The underlying dialog. */
pjsip_evsub *sub; /**< Buddy presence subscription */
+ pj_str_t term_reason;/**< Subscription termination reason */
pjsip_pres_status status; /**< Buddy presence status. */
} pjsua_buddy;
diff --git a/pjsip/src/pjsua-lib/pjsua_pres.c b/pjsip/src/pjsua-lib/pjsua_pres.c
index e1d71113..1bf23098 100644
--- a/pjsip/src/pjsua-lib/pjsua_pres.c
+++ b/pjsip/src/pjsua-lib/pjsua_pres.c
@@ -163,6 +163,29 @@ PJ_DEF(pj_status_t) pjsua_buddy_get_info( pjsua_buddy_id buddy_id,
/* monitor pres */
info->monitor_pres = buddy->monitor;
+ /* subscription state and termination reason */
+ if (buddy->sub) {
+ info->sub_state = pjsip_evsub_get_state(buddy->sub);
+ if (info->sub_state == PJSIP_EVSUB_STATE_TERMINATED &&
+ total < sizeof(info->buf_))
+ {
+ info->sub_term_reason.ptr = info->buf_ + total;
+ pj_strncpy(&info->sub_term_reason,
+ pjsip_evsub_get_termination_reason(buddy->sub),
+ sizeof(info->buf_) - total);
+ total += info->sub_term_reason.slen;
+ } else {
+ info->sub_term_reason = pj_str("");
+ }
+ } else if (total < sizeof(info->buf_)) {
+ info->sub_term_reason.ptr = info->buf_ + total;
+ pj_strncpy(&info->sub_term_reason, &buddy->term_reason,
+ sizeof(info->buf_) - total);
+ total += info->sub_term_reason.slen;
+ } else {
+ info->sub_term_reason = pj_str("");
+ }
+
PJSUA_UNLOCK();
return PJ_SUCCESS;
}
@@ -223,6 +246,11 @@ PJ_DEF(pj_status_t) pjsua_buddy_add( const pjsua_buddy_config *cfg,
buddy->pool = pjsua_pool_create(name, 512, 256);
}
+ /* Init buffers for presence subscription status */
+ buddy->term_reason.ptr = (char*)
+ pj_pool_alloc(buddy->pool,
+ PJSUA_BUDDY_SUB_TERM_REASON_LEN);
+
/* Get name and display name for buddy */
pj_strdup_with_null(buddy->pool, &tmp, &cfg->uri);
url = (pjsip_name_addr*)pjsip_parse_uri(buddy->pool, tmp.ptr, tmp.slen,
@@ -524,10 +552,23 @@ static void pres_evsub_on_srv_state( pjsip_evsub *sub, pjsip_event *event)
uapres = (pjsua_srv_pres*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
if (uapres) {
+ pjsip_evsub_state state;
+
PJ_LOG(4,(THIS_FILE, "Server subscription to %s is %s",
uapres->remote, pjsip_evsub_get_state_name(sub)));
- if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
+ state = pjsip_evsub_get_state(sub);
+
+ if (pjsua_var.ua_cfg.cb.on_srv_subscribe_state) {
+ pj_str_t from;
+
+ from = uapres->dlg->remote.info_str;
+ (*pjsua_var.ua_cfg.cb.on_srv_subscribe_state)(uapres->acc_id,
+ uapres, &from,
+ state, event);
+ }
+
+ if (state == PJSIP_EVSUB_STATE_TERMINATED) {
pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
pj_list_erase(uapres);
}
@@ -548,12 +589,11 @@ static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
pjsua_srv_pres *uapres;
pjsip_evsub *sub;
pjsip_evsub_user pres_cb;
- pjsip_tx_data *tdata;
- pjsip_pres_status pres_status;
pjsip_dialog *dlg;
+ pjsip_status_code st_code;
+ pj_str_t reason;
pjsip_expires_hdr *expires_hdr;
- pjsip_evsub_state ev_state;
- pjsua_buddy_id buddy_id;
+ pjsua_msg_data msg_data;
pj_status_t status;
if (pjsip_method_cmp(req_method, pjsip_get_subscribe_method()) != 0)
@@ -627,6 +667,8 @@ static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
uapres = PJ_POOL_ALLOC_T(dlg->pool, pjsua_srv_pres);
uapres->sub = sub;
uapres->remote = (char*) pj_pool_alloc(dlg->pool, PJSIP_MAX_URL_SIZE);
+ uapres->acc_id = acc_id;
+ uapres->dlg = dlg;
status = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, dlg->remote.info->uri,
uapres->remote, PJSIP_MAX_URL_SIZE);
if (status < 1)
@@ -640,8 +682,66 @@ static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
pj_list_push_back(&pjsua_var.acc[acc_id].pres_srv_list, uapres);
- /* Create and send 200 (OK) to the SUBSCRIBE request: */
- status = pjsip_pres_accept(sub, rdata, 200, NULL);
+ /* Capture the value of Expires header. */
+ expires_hdr = (pjsip_expires_hdr*)
+ pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES,
+ NULL);
+ if (expires_hdr)
+ uapres->expires = expires_hdr->ivalue;
+ else
+ uapres->expires = -1;
+
+ st_code = 200;
+ reason = pj_str("OK");
+ pjsua_msg_data_init(&msg_data);
+
+ /* Notify application callback, if any */
+ if (pjsua_var.ua_cfg.cb.on_incoming_subscribe) {
+ pjsua_buddy_id buddy_id;
+
+ buddy_id = pjsua_find_buddy(rdata->msg_info.from->uri);
+
+ (*pjsua_var.ua_cfg.cb.on_incoming_subscribe)(acc_id, uapres, buddy_id,
+ &dlg->remote.info_str,
+ rdata, &st_code, &reason,
+ &msg_data);
+ }
+
+ /* Handle rejection case */
+ if (st_code >= 300) {
+ pjsip_tx_data *tdata;
+
+ /* Create response */
+ status = pjsip_dlg_create_response(dlg, rdata, st_code,
+ &reason, &tdata);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Error creating response", status);
+ pj_list_erase(uapres);
+ pjsip_pres_terminate(sub, PJ_FALSE);
+ PJSUA_UNLOCK();
+ return PJ_FALSE;
+ }
+
+ /* Add header list, if any */
+ pjsua_process_msg_data(tdata, &msg_data);
+
+ /* Send the response */
+ status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata),
+ tdata);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Error sending response", status);
+ /* This is not fatal */
+ }
+
+ /* Terminate presence subscription */
+ pj_list_erase(uapres);
+ pjsip_pres_terminate(sub, PJ_FALSE);
+ PJSUA_UNLOCK();
+ return PJ_TRUE;
+ }
+
+ /* Create and send 2xx response to the SUBSCRIBE request: */
+ status = pjsip_pres_accept(sub, rdata, st_code, &msg_data.hdr_list);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to accept presence subscription",
status);
@@ -651,48 +751,98 @@ static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
return PJ_FALSE;
}
+ /* If code is 200, send NOTIFY now */
+ if (st_code == 200) {
+ pjsua_pres_notify(acc_id, uapres, PJSIP_EVSUB_STATE_ACTIVE,
+ NULL, NULL, PJ_TRUE, &msg_data);
+ }
+
+ /* Done: */
+
+ PJSUA_UNLOCK();
+
+ return PJ_TRUE;
+}
+
+
+/*
+ * Send NOTIFY.
+ */
+PJ_DEF(pj_status_t) pjsua_pres_notify( pjsua_acc_id acc_id,
+ pjsua_srv_pres *srv_pres,
+ pjsip_evsub_state ev_state,
+ const pj_str_t *state_str,
+ const pj_str_t *reason,
+ pj_bool_t with_body,
+ const pjsua_msg_data *msg_data)
+{
+ pjsua_acc *acc;
+ pjsip_pres_status pres_status;
+ pjsua_buddy_id buddy_id;
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ /* Check parameters */
+ PJ_ASSERT_RETURN(acc_id!=-1 && srv_pres, PJ_EINVAL);
+
+ /* Check that account ID is valid */
+ PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
+ PJ_EINVAL);
+ /* Check that account is valid */
+ PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
+
+ PJSUA_LOCK();
+
+ acc = &pjsua_var.acc[acc_id];
+
+ /* Check that the server presence subscription is still valid */
+ if (pj_list_find_node(&acc->pres_srv_list, srv_pres) == NULL) {
+ /* Subscription has been terminated */
+ PJSUA_UNLOCK();
+ return PJ_EINVALIDOP;
+ }
/* Set our online status: */
pj_bzero(&pres_status, sizeof(pres_status));
pres_status.info_cnt = 1;
- pres_status.info[0].basic_open = pjsua_var.acc[acc_id].online_status;
- pres_status.info[0].id = pjsua_var.acc[acc_id].cfg.pidf_tuple_id;
+ pres_status.info[0].basic_open = acc->online_status;
+ pres_status.info[0].id = acc->cfg.pidf_tuple_id;
//Both pjsua_var.local_uri and pjsua_var.contact_uri are enclosed in "<" and ">"
//causing XML parsing to fail.
//pres_status.info[0].contact = pjsua_var.local_uri;
- pjsip_pres_set_status(sub, &pres_status);
+ pjsip_pres_set_status(srv_pres->sub, &pres_status);
/* Check expires value. If it's zero, send our presense state but
* set subscription state to TERMINATED.
*/
- expires_hdr=(pjsip_expires_hdr*)
- pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
-
- if (expires_hdr && expires_hdr->ivalue == 0)
+ if (srv_pres->expires == 0)
ev_state = PJSIP_EVSUB_STATE_TERMINATED;
- else
- ev_state = PJSIP_EVSUB_STATE_ACTIVE;
- /* Create and send the first NOTIFY to active subscription: */
- status = pjsip_pres_notify( sub, ev_state, NULL, NULL, &tdata);
+ /* Create and send the NOTIFY to active subscription: */
+ status = pjsip_pres_notify(srv_pres->sub, ev_state, state_str,
+ reason, &tdata);
if (status == PJ_SUCCESS) {
- pjsua_process_msg_data(tdata, NULL);
- status = pjsip_pres_send_request( sub, tdata);
+ /* Force removal of message body if msg_body==FALSE */
+ if (!with_body) {
+ tdata->msg->body = NULL;
+ }
+ pjsua_process_msg_data(tdata, msg_data);
+ status = pjsip_pres_send_request( srv_pres->sub, tdata);
}
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create/send NOTIFY",
status);
- pj_list_erase(uapres);
- pjsip_pres_terminate(sub, PJ_FALSE);
+ pj_list_erase(srv_pres);
+ pjsip_pres_terminate(srv_pres->sub, PJ_FALSE);
PJSUA_UNLOCK();
- return PJ_FALSE;
+ return status;
}
/* Subscribe to buddy's presence if we're not subscribed */
- buddy_id = pjsua_find_buddy(dlg->remote.info->uri);
+ buddy_id = pjsua_find_buddy(srv_pres->dlg->remote.info->uri);
if (buddy_id != PJSUA_INVALID_ID) {
pjsua_buddy *b = &pjsua_var.buddy[buddy_id];
if (b->monitor && b->sub == NULL) {
@@ -702,11 +852,9 @@ static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
}
}
- /* Done: */
-
PJSUA_UNLOCK();
- return PJ_TRUE;
+ return PJ_SUCCESS;
}
@@ -1009,14 +1157,28 @@ static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
pjsip_evsub_get_state_name(sub)));
if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
- buddy->sub = NULL;
- buddy->status.info_cnt = 0;
- pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
+ if (buddy->term_reason.ptr == NULL) {
+ buddy->term_reason.ptr = (char*)
+ pj_pool_alloc(buddy->pool,
+ PJSUA_BUDDY_SUB_TERM_REASON_LEN);
+ }
+ pj_strncpy(&buddy->term_reason,
+ pjsip_evsub_get_termination_reason(sub),
+ PJSUA_BUDDY_SUB_TERM_REASON_LEN);
+ } else {
+ buddy->term_reason.slen = 0;
}
/* Call callback */
if (pjsua_var.ua_cfg.cb.on_buddy_state)
(*pjsua_var.ua_cfg.cb.on_buddy_state)(buddy->index);
+
+ /* Clear subscription */
+ if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
+ buddy->sub = NULL;
+ buddy->status.info_cnt = 0;
+ pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
+ }
}
PJSUA_UNLOCK();