diff options
author | Benny Prijono <bennylp@teluu.com> | 2008-07-17 14:19:10 +0000 |
---|---|---|
committer | Benny Prijono <bennylp@teluu.com> | 2008-07-17 14:19:10 +0000 |
commit | 85a2dd3b4a8aff9faf95415c8e8384ee2999cd04 (patch) | |
tree | c8e1d31762c36f65b7b539e4c417b6cc7d678f08 | |
parent | d6f361a21e8709fe8b18d8a3bd673f830f3920e9 (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.h | 128 | ||||
-rw-r--r-- | pjsip/include/pjsua-lib/pjsua_internal.h | 15 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_pres.c | 222 |
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(); |