📄 presence.c
字号:
/* $Id: presence.c 974 2007-02-19 01:13:53Z bennylp $ */
/*
* 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/presence.h>
#include <pjsip-simple/errno.h>
#include <pjsip-simple/evsub_msg.h>
#include <pjsip/sip_module.h>
#include <pjsip/sip_endpoint.h>
#include <pjsip/sip_dialog.h>
#include <pj/assert.h>
#include <pj/guid.h>
#include <pj/log.h>
#include <pj/os.h>
#include <pj/pool.h>
#include <pj/string.h>
#define THIS_FILE "presence.c"
#define PRES_DEFAULT_EXPIRES 600
/*
* Presence module (mod-presence)
*/
static struct pjsip_module mod_presence =
{
NULL, NULL, /* prev, next. */
{ "mod-presence", 12 }, /* Name. */
-1, /* Id */
PJSIP_MOD_PRIORITY_DIALOG_USAGE,/* Priority */
NULL, /* load() */
NULL, /* start() */
NULL, /* stop() */
NULL, /* unload() */
NULL, /* on_rx_request() */
NULL, /* on_rx_response() */
NULL, /* on_tx_request. */
NULL, /* on_tx_response() */
NULL, /* on_tsx_state() */
};
/*
* Presence message body type.
*/
typedef enum content_type
{
CONTENT_TYPE_NONE,
CONTENT_TYPE_PIDF,
CONTENT_TYPE_XPIDF,
} content_type;
/*
* This structure describe a presentity, for both subscriber and notifier.
*/
struct pjsip_pres
{
pjsip_evsub *sub; /**< Event subscribtion record. */
pjsip_dialog *dlg; /**< The dialog. */
content_type content_type; /**< Content-Type. */
pjsip_pres_status status; /**< Presence status. */
pjsip_pres_status tmp_status; /**< Temp, before NOTIFY is answred.*/
pjsip_evsub_user user_cb; /**< The user callback. */
};
typedef struct pjsip_pres pjsip_pres;
/*
* Forward decl for evsub callback.
*/
static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
pjsip_event *event);
static void pres_on_evsub_rx_refresh( 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);
static void pres_on_evsub_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);
static void pres_on_evsub_client_refresh(pjsip_evsub *sub);
static void pres_on_evsub_server_timeout(pjsip_evsub *sub);
/*
* Event subscription callback for presence.
*/
static pjsip_evsub_user pres_user =
{
&pres_on_evsub_state,
&pres_on_evsub_tsx_state,
&pres_on_evsub_rx_refresh,
&pres_on_evsub_rx_notify,
&pres_on_evsub_client_refresh,
&pres_on_evsub_server_timeout,
};
/*
* Some static constants.
*/
const pj_str_t STR_EVENT = { "Event", 5 };
const pj_str_t STR_PRESENCE = { "presence", 8 };
const pj_str_t STR_APPLICATION = { "application", 11 };
const pj_str_t STR_PIDF_XML = { "pidf+xml", 8};
const pj_str_t STR_XPIDF_XML = { "xpidf+xml", 9};
const pj_str_t STR_APP_PIDF_XML = { "application/pidf+xml", 20 };
const pj_str_t STR_APP_XPIDF_XML = { "application/xpidf+xml", 21 };
/*
* Init presence module.
*/
PJ_DEF(pj_status_t) pjsip_pres_init_module( pjsip_endpoint *endpt,
pjsip_module *mod_evsub)
{
pj_status_t status;
pj_str_t accept[2];
/* Check arguments. */
PJ_ASSERT_RETURN(endpt && mod_evsub, PJ_EINVAL);
/* Must have not been registered */
PJ_ASSERT_RETURN(mod_presence.id == -1, PJ_EINVALIDOP);
/* Register to endpoint */
status = pjsip_endpt_register_module(endpt, &mod_presence);
if (status != PJ_SUCCESS)
return status;
accept[0] = STR_APP_PIDF_XML;
accept[1] = STR_APP_XPIDF_XML;
/* Register event package to event module. */
status = pjsip_evsub_register_pkg( &mod_presence, &STR_PRESENCE,
PRES_DEFAULT_EXPIRES,
PJ_ARRAY_SIZE(accept), accept);
if (status != PJ_SUCCESS) {
pjsip_endpt_unregister_module(endpt, &mod_presence);
return status;
}
return PJ_SUCCESS;
}
/*
* Get presence module instance.
*/
PJ_DEF(pjsip_module*) pjsip_pres_instance(void)
{
return &mod_presence;
}
/*
* Create client subscription.
*/
PJ_DEF(pj_status_t) pjsip_pres_create_uac( pjsip_dialog *dlg,
const pjsip_evsub_user *user_cb,
unsigned options,
pjsip_evsub **p_evsub )
{
pj_status_t status;
pjsip_pres *pres;
pjsip_evsub *sub;
PJ_ASSERT_RETURN(dlg && p_evsub, PJ_EINVAL);
pjsip_dlg_inc_lock(dlg);
/* Create event subscription */
status = pjsip_evsub_create_uac( dlg, &pres_user, &STR_PRESENCE,
options, &sub);
if (status != PJ_SUCCESS)
goto on_return;
/* Create presence */
pres = pj_pool_zalloc(dlg->pool, sizeof(pjsip_pres));
pres->dlg = dlg;
pres->sub = sub;
if (user_cb)
pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user));
/* Attach to evsub */
pjsip_evsub_set_mod_data(sub, mod_presence.id, pres);
*p_evsub = sub;
on_return:
pjsip_dlg_dec_lock(dlg);
return status;
}
/*
* Create server subscription.
*/
PJ_DEF(pj_status_t) pjsip_pres_create_uas( pjsip_dialog *dlg,
const pjsip_evsub_user *user_cb,
pjsip_rx_data *rdata,
pjsip_evsub **p_evsub )
{
pjsip_accept_hdr *accept;
pjsip_event_hdr *event;
pjsip_expires_hdr *expires_hdr;
unsigned expires;
content_type content_type = CONTENT_TYPE_NONE;
pjsip_evsub *sub;
pjsip_pres *pres;
pj_status_t status;
/* Check arguments */
PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);
/* Must be request message */
PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
PJSIP_ENOTREQUESTMSG);
/* Check that request is SUBSCRIBE */
PJ_ASSERT_RETURN(pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
&pjsip_subscribe_method)==0,
PJSIP_SIMPLE_ENOTSUBSCRIBE);
/* Check that Event header contains "presence" */
event = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL);
if (!event) {
return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
}
if (pj_stricmp(&event->event_type, &STR_PRESENCE) != 0) {
return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EVENT);
}
/* Check that request contains compatible Accept header. */
accept = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
if (accept) {
unsigned i;
for (i=0; i<accept->count; ++i) {
if (pj_stricmp(&accept->values[i], &STR_APP_PIDF_XML)==0) {
content_type = CONTENT_TYPE_PIDF;
break;
} else
if (pj_stricmp(&accept->values[i], &STR_APP_XPIDF_XML)==0) {
content_type = CONTENT_TYPE_XPIDF;
break;
}
}
if (i==accept->count) {
/* Nothing is acceptable */
return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
}
} else {
/* No Accept header.
* Treat as "application/pidf+xml"
*/
content_type = CONTENT_TYPE_PIDF;
}
/* Check that expires is not too short. */
expires_hdr=pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
if (expires_hdr) {
if (expires_hdr->ivalue < 5) {
return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_INTERVAL_TOO_BRIEF);
}
expires = expires_hdr->ivalue;
if (expires > PRES_DEFAULT_EXPIRES)
expires = PRES_DEFAULT_EXPIRES;
} else {
expires = PRES_DEFAULT_EXPIRES;
}
/* Lock dialog */
pjsip_dlg_inc_lock(dlg);
/* Create server subscription */
status = pjsip_evsub_create_uas( dlg, &pres_user, rdata, 0, &sub);
if (status != PJ_SUCCESS)
goto on_return;
/* Create server presence subscription */
pres = pj_pool_zalloc(dlg->pool, sizeof(pjsip_pres));
pres->dlg = dlg;
pres->sub = sub;
pres->content_type = content_type;
if (user_cb)
pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user));
/* Attach to evsub */
pjsip_evsub_set_mod_data(sub, mod_presence.id, pres);
/* Done: */
*p_evsub = sub;
on_return:
pjsip_dlg_dec_lock(dlg);
return status;
}
/*
* Forcefully terminate presence.
*/
PJ_DEF(pj_status_t) pjsip_pres_terminate( pjsip_evsub *sub,
pj_bool_t notify )
{
return pjsip_evsub_terminate(sub, notify);
}
/*
* Create SUBSCRIBE
*/
PJ_DEF(pj_status_t) pjsip_pres_initiate( pjsip_evsub *sub,
pj_int32_t expires,
pjsip_tx_data **p_tdata)
{
return pjsip_evsub_initiate(sub, &pjsip_subscribe_method, expires,
p_tdata);
}
/*
* Accept incoming subscription.
*/
PJ_DEF(pj_status_t) pjsip_pres_accept( pjsip_evsub *sub,
pjsip_rx_data *rdata,
int st_code,
const pjsip_hdr *hdr_list )
{
return pjsip_evsub_accept( sub, rdata, st_code, hdr_list );
}
/*
* Get presence status.
*/
PJ_DEF(pj_status_t) pjsip_pres_get_status( pjsip_evsub *sub,
pjsip_pres_status *status )
{
pjsip_pres *pres;
PJ_ASSERT_RETURN(sub && status, PJ_EINVAL);
pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
if (pres->tmp_status._is_valid)
pj_memcpy(status, &pres->tmp_status, sizeof(pjsip_pres_status));
else
pj_memcpy(status, &pres->status, sizeof(pjsip_pres_status));
return PJ_SUCCESS;
}
/*
* Set presence status.
*/
PJ_DEF(pj_status_t) pjsip_pres_set_status( pjsip_evsub *sub,
const pjsip_pres_status *status )
{
unsigned i;
pjsip_pres *pres;
PJ_ASSERT_RETURN(sub && status, PJ_EINVAL);
pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
for (i=0; i<status->info_cnt; ++i) {
pres->status.info[i].basic_open = status->info[i].basic_open;
if (status->info[i].id.slen == 0) {
pj_create_unique_string(pres->dlg->pool,
&pres->status.info[i].id);
} else {
pj_strdup(pres->dlg->pool,
&pres->status.info[i].id,
&status->info[i].id);
}
pj_strdup(pres->dlg->pool,
&pres->status.info[i].contact,
&status->info[i].contact);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -