Implement ticket #336: custom presence status in NOTIFY/PUBLISH, supporting subset of RPID elements
git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@1424 74dad513-b988-da41-8d7b-12977e46ad98remotes/origin/turn
parent
fe04fb5a0f
commit
4461c7d500
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,148 @@
|
||||
/* $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
|
||||
*/
|
||||
#ifndef __PJSIP_SIMPLE_RPID_H__
|
||||
#define __PJSIP_SIMPLE_RPID_H__
|
||||
|
||||
/**
|
||||
* @file rpid.h
|
||||
* @brief RPID: Rich Presence Extensions to the PIDF (RFC 4480)
|
||||
*/
|
||||
#include <pjsip-simple/types.h>
|
||||
#include <pjsip-simple/pidf.h>
|
||||
|
||||
PJ_BEGIN_DECL
|
||||
|
||||
|
||||
/**
|
||||
* @defgroup PJSIP_SIMPLE_RPID RPID/Rich Presence Extensions to PIDF (RFC 4480)
|
||||
* @ingroup PJSIP_SIMPLE
|
||||
* @brief RPID/Rich Presence Extensions to PIDF (RFC 4480)
|
||||
* @{
|
||||
*
|
||||
* This file provides tools for managing subset of RPID elements into
|
||||
* PIDF document.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This enumeration describes subset of standard activities as
|
||||
* described by RFC 4880, RPID: Rich Presence Extensions to the
|
||||
* Presence Information Data Format (PIDF).
|
||||
*/
|
||||
typedef enum pjrpid_activity
|
||||
{
|
||||
/** Activity is unknown. The activity would then be conceived
|
||||
* in the "note" field.
|
||||
*/
|
||||
PJRPID_ACTIVITY_UNKNOWN,
|
||||
|
||||
/** The person is away */
|
||||
PJRPID_ACTIVITY_AWAY,
|
||||
|
||||
/** The person is busy */
|
||||
PJRPID_ACTIVITY_BUSY
|
||||
|
||||
} pjrpid_activity;
|
||||
|
||||
|
||||
/**
|
||||
* This enumeration describes types of RPID element.
|
||||
*/
|
||||
typedef enum pjrpid_element_type
|
||||
{
|
||||
/** RPID <person> element */
|
||||
PJRPID_ELEMENT_TYPE_PERSON
|
||||
|
||||
} pjrpid_element_type;
|
||||
|
||||
|
||||
/**
|
||||
* This structure describes person information in RPID document.
|
||||
*/
|
||||
typedef struct pjrpid_element
|
||||
{
|
||||
/** Element type. */
|
||||
pjrpid_element_type type;
|
||||
|
||||
/** Optional id to set on the element. */
|
||||
pj_str_t id;
|
||||
|
||||
/** Activity type. */
|
||||
pjrpid_activity activity;
|
||||
|
||||
/** Optional text describing the person/element. */
|
||||
pj_str_t note;
|
||||
|
||||
} pjrpid_element;
|
||||
|
||||
|
||||
/**
|
||||
* Duplicate RPID element.
|
||||
*
|
||||
* @param pool Pool.
|
||||
* @param dst Destination structure.
|
||||
* @param src Source structure.
|
||||
*/
|
||||
PJ_DECL(void) pjrpid_element_dup(pj_pool_t *pool, pjrpid_element *dst,
|
||||
const pjrpid_element *src);
|
||||
|
||||
|
||||
/**
|
||||
* Add RPID element information into existing PIDF document. This will also
|
||||
* add the appropriate XML namespace attributes into the presence's XML
|
||||
* node, if the attributes are not already present, and also a <note> element
|
||||
* to the first <tuple> element of the PIDF document.
|
||||
*
|
||||
* @param pres The PIDF presence document.
|
||||
* @param pool Pool.
|
||||
* @param options Currently unused, and must be zero.
|
||||
* @param elem RPID element information to be added into the PIDF
|
||||
* document.
|
||||
*
|
||||
* @return PJ_SUCCESS on success.
|
||||
*/
|
||||
PJ_DECL(pj_status_t) pjrpid_add_element(pjpidf_pres *pres,
|
||||
pj_pool_t *pool,
|
||||
unsigned options,
|
||||
const pjrpid_element *elem);
|
||||
|
||||
/**
|
||||
* Get RPID element information from PIDF document, if any.
|
||||
*
|
||||
* @param pres The PIDF document containing RPID elements.
|
||||
* @param pool Pool to duplicate the information.
|
||||
* @param elem Structure to receive the element information.
|
||||
*
|
||||
* @return PJ_SUCCESS if the document does contain RPID element
|
||||
* and the information has been parsed successfully.
|
||||
*/
|
||||
PJ_DECL(pj_status_t) pjrpid_get_element(const pjpidf_pres *pres,
|
||||
pj_pool_t *pool,
|
||||
pjrpid_element *elem);
|
||||
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
PJ_END_DECL
|
||||
|
||||
|
||||
#endif /* __PJSIP_SIMPLE_RPID_H__ */
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in new issue