diff options
author | Benny Prijono <bennylp@teluu.com> | 2007-08-25 13:36:15 +0000 |
---|---|---|
committer | Benny Prijono <bennylp@teluu.com> | 2007-08-25 13:36:15 +0000 |
commit | dd175b64509a079ee5167a397dc8b7b0ac3ced99 (patch) | |
tree | ec2bf9601c8605bb4e221c53c71536f4ff0344ba /pjsip/src | |
parent | bcd742c4466d6cf4e7d465c16e71c17e0e326dc3 (diff) |
Implement ticket #336: custom presence status in NOTIFY/PUBLISH, supporting subset of RPID elements
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1424 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjsip/src')
-rw-r--r-- | pjsip/src/pjsip-simple/errno.c | 1 | ||||
-rw-r--r-- | pjsip/src/pjsip-simple/presence.c | 11 | ||||
-rw-r--r-- | pjsip/src/pjsip-simple/presence_body.c | 8 | ||||
-rw-r--r-- | pjsip/src/pjsip-simple/rpid.c | 268 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_acc.c | 30 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_pres.c | 27 |
6 files changed, 337 insertions, 8 deletions
diff --git a/pjsip/src/pjsip-simple/errno.c b/pjsip/src/pjsip-simple/errno.c index c224b0db..4248fdaf 100644 --- a/pjsip/src/pjsip-simple/errno.c +++ b/pjsip/src/pjsip-simple/errno.c @@ -43,6 +43,7 @@ static const struct { PJSIP_SIMPLE_EBADCONTENT, "Bad Content-Type for presence" }, { PJSIP_SIMPLE_EBADPIDF, "Bad PIDF content for presence" }, { PJSIP_SIMPLE_EBADXPIDF, "Bad XPIDF content for presence" }, + { PJSIP_SIMPLE_EBADRPID, "Invalid or bad RPID document"}, /* isComposing errors. */ { PJSIP_SIMPLE_EBADISCOMPOSE, "Bad isComposing indication/XML message" }, diff --git a/pjsip/src/pjsip-simple/presence.c b/pjsip/src/pjsip-simple/presence.c index 88aa8d66..bb5dcd52 100644 --- a/pjsip/src/pjsip-simple/presence.c +++ b/pjsip/src/pjsip-simple/presence.c @@ -390,6 +390,17 @@ PJ_DEF(pj_status_t) pjsip_pres_set_status( pjsip_evsub *sub, pj_strdup(pres->dlg->pool, &pres->status.info[i].contact, &status->info[i].contact); + + /* Duplicate <person> */ + pres->status.info[i].rpid.activity = + status->info[i].rpid.activity; + pj_strdup(pres->dlg->pool, + &pres->status.info[i].rpid.id, + &status->info[i].rpid.id); + pj_strdup(pres->dlg->pool, + &pres->status.info[i].rpid.note, + &status->info[i].rpid.note); + } pres->status.info_cnt = status->info_cnt; diff --git a/pjsip/src/pjsip-simple/presence_body.c b/pjsip/src/pjsip-simple/presence_body.c index c57e427a..9f085d5e 100644 --- a/pjsip/src/pjsip-simple/presence_body.c +++ b/pjsip/src/pjsip-simple/presence_body.c @@ -101,6 +101,11 @@ PJ_DEF(pj_status_t) pjsip_pres_create_pidf( pj_pool_t *pool, status->info[i].basic_open); } + /* Create <person> (RPID) */ + if (status->info_cnt) { + pjrpid_add_element(pidf, pool, 0, &status->info[0].rpid); + } + body = PJ_POOL_ZALLOC_T(pool, pjsip_msg_body); body->data = pidf; body->content_type.type = STR_APPLICATION; @@ -196,6 +201,9 @@ PJ_DEF(pj_status_t) pjsip_pres_parse_pidf( pjsip_rx_data *rdata, pres_status->info_cnt++; } + /* Parse <person> (RPID) */ + pjrpid_get_element(pidf, pool, &pres_status->info[0].rpid); + return PJ_SUCCESS; } diff --git a/pjsip/src/pjsip-simple/rpid.c b/pjsip/src/pjsip-simple/rpid.c new file mode 100644 index 00000000..fde939ba --- /dev/null +++ b/pjsip/src/pjsip-simple/rpid.c @@ -0,0 +1,268 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjsip-simple/rpid.h> +#include <pjsip-simple/errno.h> +#include <pj/assert.h> +#include <pj/guid.h> +#include <pj/string.h> + + +static const pj_str_t DM_NAME = {"xmlns:dm", 8}; +static const pj_str_t DM_VAL = {"urn:ietf:params:xml:ns:pidf:data-model", 38}; +static const pj_str_t RPID_NAME = {"xmlns:rpid", 10}; +static const pj_str_t RPID_VAL = {"urn:ietf:params:xml:ns:pidf:rpid", 32}; + +static const pj_str_t DM_NOTE = {"dm:note", 7}; +static const pj_str_t DM_PERSON = {"dm:person", 9}; +static const pj_str_t ID = {"id", 2}; +static const pj_str_t NOTE = {"note", 4}; +static const pj_str_t RPID_ACTIVITIES = {"rpid:activities", 15}; +static const pj_str_t RPID_AWAY = {"rpid:away", 9}; +static const pj_str_t RPID_BUSY = {"rpid:busy", 9}; +static const pj_str_t RPID_UNKNOWN = {"rpid:unknown", 12}; + + +/* Duplicate RPID element */ +PJ_DEF(void) pjrpid_element_dup(pj_pool_t *pool, pjrpid_element *dst, + const pjrpid_element *src) +{ + pj_memcpy(dst, src, sizeof(pjrpid_element)); + pj_strdup(pool, &dst->id, &src->id); + pj_strdup(pool, &dst->note, &src->note); +} + + +/* Update RPID namespaces. */ +static void update_namespaces(pjpidf_pres *pres, + pj_pool_t *pool) +{ + /* Check if namespace is already present. */ + if (pj_xml_find_attr(pres, &DM_NAME, NULL) != NULL) + return; + + pj_xml_add_attr(pres, pj_xml_attr_new(pool, &DM_NAME, &DM_VAL)); + pj_xml_add_attr(pres, pj_xml_attr_new(pool, &RPID_NAME, &RPID_VAL)); +} + + +/* Comparison function to find node name substring */ +static pj_bool_t substring_match(const pj_xml_node *node, + const char *part_name, + int part_len) +{ + pj_str_t end_name; + + if (part_len < 1) + part_len = pj_ansi_strlen(part_name); + + if (node->name.slen < part_len) + return PJ_FALSE; + + end_name.ptr = node->name.ptr + (node->name.slen - part_len); + end_name.slen = part_len; + + return pj_strnicmp2(&end_name, part_name, part_len)==0; +} + +/* Util to find child node with the specified substring */ +static pj_xml_node *find_node(const pj_xml_node *parent, + const char *part_name) +{ + const pj_xml_node *node = parent->node_head.next, + *head = (pj_xml_node*) &parent->node_head; + int part_len = pj_ansi_strlen(part_name); + + while (node != head) { + if (substring_match(node, part_name, part_len)) + return (pj_xml_node*) node; + + node = node->next; + } + + return NULL; +} + +/* + * Add RPID element into existing PIDF document. + */ +PJ_DEF(pj_status_t) pjrpid_add_element(pjpidf_pres *pres, + pj_pool_t *pool, + unsigned options, + const pjrpid_element *elem) +{ + pj_xml_node *nd_person, *nd_activities, *nd_activity, *nd_note; + pj_xml_attr *attr; + + PJ_ASSERT_RETURN(pres && pool && options==0 && elem, PJ_EINVAL); + + PJ_UNUSED_ARG(options); + + /* Check if we need to add RPID information into the PIDF document. */ + if (elem->id.slen==0 && + elem->activity==PJRPID_ACTIVITY_UNKNOWN && + elem->note.slen==0) + { + /* No RPID information to be added. */ + return PJ_SUCCESS; + } + + /* Add <note> to <tuple> */ + if (elem->note.slen != 0) { + pj_xml_node *nd_tuple; + + nd_tuple = find_node(pres, "tuple"); + + if (nd_tuple) { + nd_note = pj_xml_node_new(pool, &NOTE); + pj_strdup(pool, &nd_note->content, &elem->note); + pj_xml_add_node(nd_tuple, nd_note); + nd_note = NULL; + } + } + + /* Update namespace */ + update_namespaces(pres, pool); + + /* Add <person> */ + nd_person = pj_xml_node_new(pool, &DM_PERSON); + if (elem->id.slen != 0) { + attr = pj_xml_attr_new(pool, &ID, &elem->id); + } else { + pj_str_t person_id; + pj_create_unique_string(pool, &person_id); + attr = pj_xml_attr_new(pool, &ID, &person_id); + } + pj_xml_add_attr(nd_person, attr); + pj_xml_add_node(pres, nd_person); + + /* Add <activities> */ + nd_activities = pj_xml_node_new(pool, &RPID_ACTIVITIES); + pj_xml_add_node(nd_person, nd_activities); + + /* Add the activity */ + switch (elem->activity) { + case PJRPID_ACTIVITY_AWAY: + nd_activity = pj_xml_node_new(pool, &RPID_AWAY); + break; + case PJRPID_ACTIVITY_BUSY: + nd_activity = pj_xml_node_new(pool, &RPID_BUSY); + break; + case PJRPID_ACTIVITY_UNKNOWN: + default: + nd_activity = pj_xml_node_new(pool, &RPID_UNKNOWN); + break; + } + pj_xml_add_node(nd_activities, nd_activity); + + /* Add custom text if required. */ + if (elem->note.slen != 0) { + nd_note = pj_xml_node_new(pool, &DM_NOTE); + pj_strdup(pool, &nd_note->content, &elem->note); + pj_xml_add_node(nd_person, nd_note); + } + + /* Done */ + return PJ_SUCCESS; +} + + +/* Get <note> element from PIDF <tuple> element */ +static pj_status_t get_tuple_note(const pjpidf_pres *pres, + pj_pool_t *pool, + pjrpid_element *elem) +{ + const pj_xml_node *nd_tuple, *nd_note; + + nd_tuple = find_node(pres, "tuple"); + if (!nd_tuple) + return PJSIP_SIMPLE_EBADRPID; + + nd_note = find_node(pres, "note"); + if (nd_note) { + pj_strdup(pool, &elem->note, &nd_note->content); + return PJ_SUCCESS; + } + + return PJSIP_SIMPLE_EBADRPID; +} + +/* + * Get RPID element from PIDF document, if any. + */ +PJ_DEF(pj_status_t) pjrpid_get_element(const pjpidf_pres *pres, + pj_pool_t *pool, + pjrpid_element *elem) +{ + const pj_xml_node *nd_person, *nd_activities, *nd_note = NULL; + const pj_xml_attr *attr; + + /* Reset */ + pj_bzero(elem, sizeof(*elem)); + elem->activity = PJRPID_ACTIVITY_UNKNOWN; + + /* Find <person> */ + nd_person = find_node(pres, "person"); + if (!nd_person) { + /* <person> not found, try to get <note> from <tuple> */ + return get_tuple_note(pres, pool, elem); + } + + /* Get element id attribute */ + attr = pj_xml_find_attr((pj_xml_node*)nd_person, &ID, NULL); + if (attr) + pj_strdup(pool, &elem->id, &attr->value); + + /* Get <activities> */ + nd_activities = find_node(nd_person, "activities"); + if (nd_activities) { + const pj_xml_node *nd_activity; + + /* Try to get <note> from <activities> */ + nd_note = find_node(nd_activities, "note"); + + /* Get the activity */ + nd_activity = nd_activities->node_head.next; + if (nd_activity == nd_note) + nd_activity = nd_activity->next; + + if (nd_activity != (pj_xml_node*) &nd_activities->node_head) { + if (substring_match(nd_activity, "busy", -1)) + elem->activity = PJRPID_ACTIVITY_BUSY; + else if (substring_match(nd_activity, "away", -1)) + elem->activity = PJRPID_ACTIVITY_AWAY; + else + elem->activity = PJRPID_ACTIVITY_UNKNOWN; + + } + } + + /* If <note> is not found, get <note> from <person> */ + if (nd_note == NULL) + nd_note = find_node(nd_person, "note"); + + if (nd_note) { + pj_strdup(pool, &elem->note, &nd_note->content); + } else { + get_tuple_note(pres, pool, elem); + } + + return PJ_SUCCESS; +} + + diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c index b92e3e51..854f8cb4 100644 --- a/pjsip/src/pjsua-lib/pjsua_acc.c +++ b/pjsip/src/pjsua-lib/pjsua_acc.c @@ -430,7 +430,26 @@ PJ_DEF(pj_status_t) pjsua_acc_set_online_status( pjsua_acc_id acc_id, PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP); pjsua_var.acc[acc_id].online_status = is_online; - pjsua_pres_refresh(); + pj_bzero(&pjsua_var.acc[acc_id].rpid, sizeof(pjrpid_element)); + pjsua_pres_update_acc(acc_id, PJ_FALSE); + return PJ_SUCCESS; +} + + +/* + * Set online status with extended information + */ +PJ_DEF(pj_status_t) pjsua_acc_set_online_status2( pjsua_acc_id acc_id, + pj_bool_t is_online, + const pjrpid_element *pr) +{ + PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc), + PJ_EINVAL); + PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP); + + pjsua_var.acc[acc_id].online_status = is_online; + pjrpid_element_dup(pjsua_var.pool, &pjsua_var.acc[acc_id].rpid, pr); + pjsua_pres_update_acc(acc_id, PJ_TRUE); return PJ_SUCCESS; } @@ -689,7 +708,14 @@ PJ_DEF(pj_status_t) pjsua_acc_get_info( pjsua_acc_id acc_id, info->acc_uri = acc_cfg->id; info->has_registration = (acc->cfg.reg_uri.slen > 0); info->online_status = acc->online_status; - + pj_memcpy(&info->rpid, &acc->rpid, sizeof(pjrpid_element)); + if (info->rpid.note.slen) + info->online_status_text = info->rpid.note; + else if (info->online_status) + info->online_status_text = pj_str("Online"); + else + info->online_status_text = pj_str("Offline"); + if (acc->reg_last_err) { info->status = (pjsip_status_code) acc->reg_last_err; pj_strerror(acc->reg_last_err, info->buf_, sizeof(info->buf_)); diff --git a/pjsip/src/pjsua-lib/pjsua_pres.c b/pjsip/src/pjsua-lib/pjsua_pres.c index d5316959..b544af69 100644 --- a/pjsip/src/pjsua-lib/pjsua_pres.c +++ b/pjsip/src/pjsua-lib/pjsua_pres.c @@ -113,7 +113,15 @@ PJ_DEF(pj_status_t) pjsua_buddy_get_info( pjsua_buddy_id buddy_id, info->status_text = pj_str("?"); } else if (pjsua_var.buddy[buddy_id].status.info[0].basic_open) { info->status = PJSUA_BUDDY_STATUS_ONLINE; - info->status_text = pj_str("Online"); + + /* copy RPID information */ + info->rpid = buddy->status.info[0].rpid; + + if (info->rpid.note.slen) + info->status_text = info->rpid.note; + else + info->status_text = pj_str("Online"); + } else { info->status = PJSUA_BUDDY_STATUS_OFFLINE; info->status_text = pj_str("Offline"); @@ -652,6 +660,9 @@ static pj_status_t send_publish(int acc_id, pj_bool_t active) pres_status.info_cnt = 1; pres_status.info[0].basic_open = acc->online_status; pres_status.info[0].id = acc->cfg.pidf_tuple_id; + /* .. including RPID information */ + pj_memcpy(&pres_status.info[0].rpid, &acc->rpid, + sizeof(pjrpid_element)); /* Be careful not to send PIDF with presence entity ID containing * "<" character. @@ -821,8 +832,8 @@ void pjsua_pres_delete_acc(int acc_id) } -/* Refresh subscription (e.g. when our online status has changed) */ -static void refresh_server_subscription(int acc_id) +/* Update server subscription (e.g. when our online status has changed) */ +void pjsua_pres_update_acc(int acc_id, pj_bool_t force) { pjsua_acc *acc = &pjsua_var.acc[acc_id]; pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg; @@ -836,8 +847,12 @@ static void refresh_server_subscription(int acc_id) pjsip_tx_data *tdata; pjsip_pres_get_status(uapres->sub, &pres_status); - if (pres_status.info[0].basic_open != acc->online_status) { + if (force || pres_status.info[0].basic_open != acc->online_status) { + pres_status.info[0].basic_open = acc->online_status; + pj_memcpy(&pres_status.info[0].rpid, &acc->rpid, + sizeof(pjrpid_element)); + pjsip_pres_set_status(uapres->sub, &pres_status); if (pjsip_pres_current_notify(uapres->sub, &tdata)==PJ_SUCCESS) { @@ -855,7 +870,7 @@ static void refresh_server_subscription(int acc_id) * send the first PUBLISH. */ if (acc_cfg->publish_enabled && acc->publish_sess) { - if (acc->publish_state != acc->online_status) { + if (force || acc->publish_state != acc->online_status) { send_publish(acc_id, PJ_TRUE); } } @@ -1251,7 +1266,7 @@ void pjsua_pres_refresh() for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) { if (pjsua_var.acc[i].valid) - refresh_server_subscription(i); + pjsua_pres_update_acc(i, PJ_FALSE); } } |