/*
* Presence Agent, watcher structure and related functions
*
* $Id: watcher.c,v 1.17.2.1 2005/06/06 16:27:32 andrei Exp $
*
* Copyright (C) 2001-2003 FhG Fokus
*
* This file is part of ser, a free SIP server.
*
* ser 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
*
* For a license to use the ser software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* ser 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 "paerrno.h"
#include "../../db/db.h"
#include "../../dprint.h"
#include "../../parser/parse_event.h"
#include "../../mem/shm_mem.h"
#include "../../trim.h"
#include "../../ut.h"
#include "pa_mod.h"
#include "common.h"
#include "watcher.h"
#include "presentity.h"
char *doctype_name[] = {
[DOC_XPIDF] = "DOC_XPIDF",
[DOC_LPIDF] = "DOC_LPIDF",
[DOC_PIDF] = "DOC_PIDF",
[DOC_WINFO] = "DOC_WINFO",
[DOC_XCAP_CHANGE] = "DOC_XCAP_CHANGE",
[DOC_LOCATION] = "DOC_LOCATION"
};
char *event_package_name[] = {
[EVENT_OTHER] = "unknown",
[EVENT_PRESENCE] = "presence",
[EVENT_PRESENCE_WINFO] = "presence.winfo",
[EVENT_XCAP_CHANGE] = "xcap-change",
[EVENT_LOCATION] = "location",
NULL
};
static str watcher_status_names[] = {
[WS_PENDING] = { "pending", 7 },
[WS_ACTIVE] = { "active", 6 },
[WS_WAITING] = { "waiting", 7 },
[WS_TERMINATED] = { "terminated", 10 },
{ 0, 0 }
};
static str watcher_event_names[] = {
[WE_SUBSCRIBE] = { "subscribe", 9 },
[WE_APPROVED] = { "approved", 8 },
[WE_DEACTIVATED] = { "deactivated", 11 },
[WE_PROBATION] = { "probation", 9 },
[WE_REJECTED] = { "rejected", 8 },
[WE_TIMEOUT] = { "timeout", 7 },
[WE_GIVEUP] = { "giveup", 6 },
[WE_NORESOURCE] = { "noresource", 10 },
{ 0, 0 }
};
int event_package_from_string(str *epname)
{
int i;
for (i = 0; event_package_name[i]; i++) {
if (strcasecmp(epname->s, event_package_name[i]) == 0) {
return i;
}
}
return 0;
}
watcher_status_t watcher_status_from_string(str *wsname)
{
int i;
for (i = 0; watcher_status_names[i].len; i++) {
if (str_strcasecmp(wsname, &watcher_status_names[i]) == 0) {
return i;
}
}
return 0;
}
watcher_event_t watcher_event_from_string(str *wename)
{
int i;
for (i = 0; watcher_event_names[i].len; i++) {
if (str_strcasecmp(wename, &watcher_event_names[i]) == 0) {
return i;
}
}
return 0;
}
#define S_ID_LEN 64
/* be sure s!=NULL */
/* compute a hash value for a string */
unsigned int compute_hash(unsigned int _h, char* s, int len)
{
#define h_inc h+=v^(v>>3);
char* p;
register unsigned v;
register unsigned h = _h;
for(p=s; p<=(s+len-4); p+=4)
{
v=(*p<<24)+(p[1]<<16)+(p[2]<<8)+p[3];
h_inc;
}
v=0;
for(;p<(s+len); p++)
{
v<<=8;
v+=*p;
}
h_inc;
return h;
}
static char hbuf[2048];
static int watcher_assign_statement_id(presentity_t *presentity, watcher_t *watcher)
{
unsigned int h = 0;
char *dn = doctype_name[watcher->accept];
if (1) {
int len = 0;
strncpy(hbuf+len, presentity->uri.s, presentity->uri.len);
len += presentity->uri.len;
strncpy(hbuf+len, dn, strlen(dn));
len += strlen(dn);
strncpy(hbuf+len, watcher->uri.s, watcher->uri.len);
len += watcher->uri.len;
h = compute_hash(0, hbuf, len);
} else {
h = compute_hash(0, presentity->uri.s, presentity->uri.len);
h = compute_hash(h, dn, strlen(dn));
h = compute_hash(h, watcher->uri.s, watcher->uri.len);
}
watcher->s_id.len = sprintf(watcher->s_id.s, "SID%08x", h);
return 0;
}
/*
* Create a new watcher structure but do not write to database
*/
int new_watcher_no_wb(presentity_t *_p, str* _uri, time_t _e, int event_package, doctype_t _a, dlg_t* _dlg,
str *_dn, watcher_t** _w)
{
watcher_t* watcher;
/* Check parameters */
if (!_uri && !_dlg && !_w) {
LOG(L_ERR, "new_watcher(): Invalid parameter value\n");
return -1;
}
/* Allocate memory buffer for watcher_t structure and uri string */
watcher = (watcher_t*)shm_malloc(sizeof(watcher_t) + _uri->len + _dn->len + S_ID_LEN);
if (!watcher) {
paerrno = PA_NO_MEMORY;
LOG(L_ERR, "new_watcher(): No memory left\n");
return -1;
}
memset(watcher, 0, sizeof(watcher_t));
/* Copy uri string */
watcher->uri.s = (char*)watcher + S_ID_LEN + sizeof(watcher_t);
watcher->uri.len = _uri->len;
memcpy(watcher->uri.s, _uri->s, _uri->len);
/* Copy display_name string */
watcher->display_name.s = (char*)watcher + S_ID_LEN + sizeof(watcher_t) + _uri->len;
watcher->display_name.len = _dn->len;
memcpy(watcher->display_name.s, _dn->s, _dn->len);
watcher->s_id.s = (char*)watcher + sizeof(watcher_t);
watcher->s_id.len = 0;
watcher->event_package = event_package;
watcher->expires = _e; /* Expires value */
watcher->accept = _a; /* Accepted document type */
watcher->dialog = _dlg; /* Dialog handle */
watcher->event = WE_SUBSCRIBE;
*_w = watcher;
return 0;
}
/*
* Create a new watcher structure
*/
int new_watcher(presentity_t *_p, str* _uri, time_t _e, int event_package, doctype_t _a, dlg_t* _dlg,
str *_dn, watcher_t** _w)
{
int rc;
watcher_t* watcher;
/* Check parameters */
if (!_uri && !_dlg && !_w) {
LOG(L_ERR, "new_watcher(): Invalid parameter value\n");
return -1;
}
rc = new_watcher_no_wb(_p, _uri, _e, event_package, _a, _dlg, _dn, _w);
if (rc < 0) {
return rc;
} else {
watcher = *_w;
}
if (use_db) {
db_key_t query_cols[11];
db_op_t query_ops[11];
db_val_t query_vals[11];
db_key_t result_cols[5];
db_res_t *res;
int n_query_cols = 0;
int n_result_cols = 0;
int s_id_col, status_col, display_name_col, event_col;
char *package = event_package_name[watcher->event_package];
LOG(L_ERR, "new_watcher starting\n");
query_cols[n_query_cols] = "r_uri";
query_ops[n_query_cols] = OP_EQ;
query_vals[n_query_cols].type = DB_STR;
query_vals[n_query_cols].nul = n_query_cols;
query_vals[n_query_cols].val.str_val.s = _p->uri.s;
query_vals[n_query_cols].val.str_val.len = _p->uri.len;
n_query_cols++;
LOG(L_ERR, "new_watcher: _p->uri=%.*s\n", _p->uri.len, _p->uri.s);
query_cols[n_query_cols] = "w_uri";
query_ops[n_query_cols] = OP_EQ;
query_vals[n_query_cols].type = DB_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val.s = watcher->uri.s;
query_vals[n_query_cols].val.str_val.len = watcher->uri.len;
n_query_cols++;
LOG(L_ERR, "db_new_watcher: watcher->uri=%.*s\n", watcher->uri.len, watcher->uri.s);
query_cols[n_query_cols] = "package";
query_ops[n_query_cols] = OP_EQ;
query_vals[n_query_cols].type = DB_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val.s = package;
query_vals[n_query_cols].val.str_val.len = strlen(package);
n_query_cols++;
LOG(L_ERR, "new_watcher: watcher->package=%s\n", package);
result_cols[s_id_col = n_result_cols++] = "s_id";
result_cols[status_col = n_result_cols++] = "status";
result_cols[event_col = n_result_cols++] = "event";
result_cols[display_name_col = n_result_cols++] = "display_name";
if (pa_dbf.use_table(pa_db, watcherinfo_table) < 0) {
LOG(L_ERR, "new_watcher: Error in use_table\n");
return -1;
}
if (pa_dbf.query (pa_db, query_cols, query_ops, query_vals,
result_cols, n_query_cols, n_result_cols, 0, &res) < 0) {
LOG(L_ERR, "new_watcher: Error while querying tuple\n");
return -1;
}
LOG(L_INFO, "new_watcher: getting values: res=%p res->n=%d\n",
res, (res ? res->n : 0));
if (res && res->n > 0) {
/* fill in tuple structure from database query result */
db_row_t *row = &res->rows[0];
db_val_t *row_vals = ROW_VALUES(row);
str s_id = { 0, 0 };
str status = { 0, 0 };
str event_str = { 0, 0 };
watcher_event_t watcher_event = WE_SUBSCRIBE;
if (!row_vals[s_id_col].nul) {
s_id.s = (char*)row_vals[s_id_col].val.string_val;
s_id.len = strlen(s_id.s);
}
if (!row_vals[status_col].nul) {
status.s = (char*)row_vals[status_col].val.string_val;
status.len = strlen(status.s);
}
if (!row_vals[event_col].nul) {
event_str.s = (char*)row_vals[event_col].val.string_val;
event_str.len = strlen(event_str.s);
watcher_event = watcher_event_from_string(&event_str);
}
watcher->event = watcher_event;
LOG(L_ERR, "new_watcher: status=%.*s\n", status.len, status.s);
if (status.len) {
watcher->status = watcher_status_from_string(&status);
} else {
watcher->status = WS_ACTIVE;
}
if (s_id.s) {
strncpy(watcher->s_id.s, s_id.s, S_ID_LEN);
watcher->s_id.len = strlen(s_id.s);
}
} else {
watcher_assign_statement_id(_p, watcher);
query_cols[n_query_cols] = "s_id";
query_vals[n_query_cols].type = DB_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val.s = watcher->s_id.s;
query_vals[n_query_cols].val.str_val.len = watcher->s_id.len;
n_query_cols++;
query_cols[n_query_cols] = "status";
query_vals[n_query_cols].type = DB_STR;
query_vals[n_query_cols].nul = 0;
if (new_watcher_pending) {
query_vals[n_query_cols].val.str_val.s = "pending";
query_vals[n_query_cols].val.str_val.len = strlen("pending");
} else {
query_vals[n_query_cols].val.str_val.s = "active";
query_vals[n_query_cols].val.str_val.len = strlen("active");
}
n_query_cols++;
query_cols[n_query_cols] = "event";
query_vals[n_query_cols].type = DB_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val = watcher_event_names[watcher->event];
n_query_cols++;
query_cols[n_query_cols] = "display_name";
query_vals[n_query_cols].type = DB_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val.s = watcher->display_name.s;
query_vals[n_query_cols].val.str_val.len = watcher->display_name.len;
n_query_cols++;
query_cols[n_query_cols] = "accepts";
query_vals[n_query_cols].type = DB_INT;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.int_val = watcher->accept;
n_query_cols++;
query_cols[n_query_cols] = "expires";
query_vals[n_query_cols].type = DB_INT;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.int_val = watcher->expires;
n_query_cols++;
/* insert new record into database */
LOG(L_INFO, "new_watcher: inserting %d cols into table\n", n_query_cols);
if (pa_dbf.insert(pa_db, query_cols, query_vals, n_query_cols)
< 0) {
LOG(L_ERR, "new_watcher: Error while inserting tuple\n");
return -1;
}
}
if (res)
pa_dbf.free_result(pa_db, res);
}
return 0;
}
/*
* Read watcherinfo table from database for presentity _p
*/
int db_read_watcherinfo(presentity_t *_p)
{
if (use_db) {
db_key_t query_cols[5];
db_op_t query_ops[5];
db_val_t query_vals[5];
db_key_t result_cols[9];
db_res_t *res;
int n_query_cols = 1;
int n_result_cols = 0;
int w_uri_col, s_id_col, event_package_col, status_col, watcher_event_col, display_name_col, accepts_col, expires_col;
// LOG(L_ERR, "db_read_watcherinfo starting\n");
query_cols[0] = "r_uri";
query_ops[0] = OP_EQ;
query_vals[0].type = DB_STRING;
query_vals[0].nul = 0;
query_vals[0].val.string_val = _p->uri.s;
LOG(L_ERR, "db_read_watcherinfo: _p->uri='%s'\n", _p->uri.s);
result_cols[w_uri_col = n_result_cols++] = "w_uri";
result_cols[s_id_col = n_result_cols++] = "s_id";
result_cols[event_package_col = n_result_cols++] = "package";
result_cols[status_col = n_result_cols++] = "status";
result_cols[display_name_col = n_result_cols++] = "display_name";
result_cols[accepts_col = n_result_cols++] = "accepts";
result_cols[expires_col = n_result_cols++] = "expires";
result_cols[watcher_event_col = n_result_cols++] = "event";
if (pa_dbf.use_table(pa_db, watcherinfo_table) < 0) {
LOG(L_ERR, "db_read_watcherinfo: Error in use_table\n");
return -1;
}
if (pa_dbf.query (pa_db, query_cols, query_ops, query_vals,
result_cols, n_query_cols, n_result_cols, 0, &res) < 0) {
LOG(L_ERR, "db_read_watcherinfo(): Error while querying watcherinfo\n");
return -1;
}
if (res && res->n > 0) {
/* fill in tuple structure from database query result */
int i;
for (i = 0; i < res->n; i++) {
db_row_t *row = &res->rows[i];
db_val_t *row_vals = ROW_VALUES(row);
str w_uri = { 0, 0 };
str s_id = { 0, 0 };
str event_package_str = { 0, 0 };
int event_package = EVENT_PRESENCE;
str watcher_event_str = { 0, 0 };
watcher_event_t watcher_event = WE_SUBSCRIBE;
int accepts = row_vals[accepts_col].val.int_val;
int expires = row_vals[expires_col].val.int_val;
str status = { 0, 0 };
str display_name = { 0, 0 };
watcher_t *watcher = NULL;
if (!row_vals[w_uri_col].nul) {
w_uri.s = (char*)row_vals[w_uri_col].val.string_val;
w_uri.len = strlen(w_uri.s);
}
if (!row_vals[s_id_col].nul) {
s_id.s = (char*)row_vals[s_id_col].val.string_val;
s_id.len = strlen(s_id.s);
}
if (!row_vals[event_package_col].nul) {
event_package_str.s = (char*)
row_vals[event_package_col].val.string_val;
event_package_str.len = strlen(event_package_str.s);
event_package = event_package_from_string(&event_package_str);
}
if (!row_vals[status_col].nul) {
status.s = (char*)row_vals[status_col].val.string_val;
status.len = strlen(status.s);
}
if (!row_vals[watcher_event_col].nul) {
watcher_event_str.s = (char*)
row_vals[watcher_event_col].val.string_val;
watcher_event_str.len = strlen(watcher_event_str.s);
watcher_event = watcher_event_from_string(&watcher_event_str);
}
if (!row_vals[display_name_col].nul) {
display_name.s = (char*)row_vals[display_name_col].val.string_val;
display_name.len = strlen(display_name.s);
}
if (find_watcher(_p, &w_uri, event_package, &watcher) != 0) {
new_watcher_no_wb(_p, &w_uri, expires, event_package, accepts, NULL, &display_name, &watcher);
}
if (watcher) {
watcher_status_t ws = watcher_status_from_string(&status);
if (watcher->status != ws)
watcher->flags |= WFLAG_SUBSCRIPTION_CHANGED;
watcher->status = ws;
watcher->event = watcher_event;
if (s_id.s) {
strncpy(watcher->s_id.s, s_id.s, S_ID_LEN);
watcher->s_id.len = strlen(s_id.s);
}
}
}
}
pa_dbf.free_result(pa_db, res);
LOG(L_ERR, "db_read_watcherinfo: _p->uri='%s' done\n", _p->uri.s);
}
return 0;
}
/*
* Release a watcher structure
*/
void free_watcher(watcher_t* _w)
{
tmb.free_dlg(_w->dialog);
shm_free(_w);
}
/*
* Print contact, for debugging purposes only
*/
void print_watcher(FILE* _f, watcher_t* _w)
{
fprintf(_f, "---Watcher---\n");
fprintf(_f, "uri : '%.*s'\n", _w->uri.len, ZSW(_w->uri.s));
fprintf(_f, "expires: %d\n", (int)(_w->expires - time(0)));
fprintf(_f, "accept : %s\n", doctype_name[_w->accept]);
fprintf(_f, "next : %p\n", _w->next);
tmb.print_dlg(_f, _w->dialog);
fprintf(_f, "---/Watcher---\n");
}
/*
* Update a watcher structure
*/
int update_watcher(watcher_t* _w, time_t _e)
{
_w->expires = _e;
return 0;
}
#define CRLF "\r\n"
#define CRLF_L (sizeof(CRLF) - 1)
#define PUBLIC_ID "//IETF//DTD RFCxxxx PIDF 1.0//EN"
#define PUBLIC_ID_L (sizeof(PUBLIC_ID) - 1)
#define XML_VERSION ""
#define XML_VERSION_L (sizeof(XML_VERSION) - 1)
#define WATCHERINFO_STAG ""
#define WATCHERINFO_STAG_L (sizeof(WATCHERINFO_STAG) - 1)
#define WATCHERINFO_ETAG ""
#define WATCHERINFO_ETAG_L (sizeof(WATCHERINFO_ETAG) - 1)
#define WATCHERLIST_START " "
#define WATCHERLIST_ETAG_L (sizeof(WATCHERLIST_ETAG) - 1)
#define WATCHER_START " "
#define URI_START_L (sizeof(URI_START) - 1)
#define WATCHER_ETAG ""
#define WATCHER_ETAG_L (sizeof(WATCHER_ETAG) - 1)
#define PACKAGE_START "\" package=\""
#define PACKAGE_START_L (sizeof(PACKAGE_START) - 1)
#define PACKAGE_END "\">"
#define PACKAGE_END_L (sizeof(PACKAGE_END) - 1)
void escape_str(str *unescaped)
{
int i;
char *s = unescaped->s;
for (i = 0; i < unescaped->len; i++) {
if (s[i] == '<' || s[i] == '>') {
s[i] = ' ';
}
}
}
/*
* Add a watcher information
*/
int winfo_add_watcher(str* _b, int _l, watcher_t *watcher)
{
str strs[20];
int n_strs = 0;
int i;
int len = 0;
int status = watcher->status;
#define add_string(_s, _l) ((strs[n_strs].s = (_s)), (strs[n_strs].len = (_l)), (len += _l), n_strs++)
#define add_str(_s) (strs[n_strs].s = (_s.s), strs[n_strs].len = (_s.len), len += _s.len, n_strs++)
#define add_pstr(_s) (strs[n_strs].s = (_s->s), strs[n_strs].len = (_s->len), len += _s->len, n_strs++)
add_string(WATCHER_START, WATCHER_START_L);
add_string(STATUS_START, STATUS_START_L);
add_str(watcher_status_names[status]);
add_string(EVENT_START, EVENT_START_L);
add_str(watcher_event_names[watcher->event]);
add_string(SID_START, SID_START_L);
add_str(watcher->s_id);
if (watcher->display_name.len > 0) {
add_string(DISPLAY_NAME_START, DISPLAY_NAME_START_L);
escape_str(&watcher->display_name);
add_str(watcher->display_name);
}
add_string(URI_START, URI_START_L);
add_str(watcher->uri);
add_string(WATCHER_ETAG, WATCHER_ETAG_L);
add_string(CRLF, CRLF_L);
if (_l < len) {
paerrno = PA_SMALL_BUFFER;
LOG(L_ERR, "winfo_add_watcher(): Buffer too small\n");
return -1;
}
for (i = 0; i < n_strs; i++)
str_append(_b, strs[i].s, strs[i].len);
return 0;
}
/*
* Create start of winfo document
*/
int start_winfo_doc(str* _b, int _l)
{
str strs[10];
int n_strs = 0;
int i;
int len = 0;
if ((XML_VERSION_L +
CRLF_L
) > _l) {
paerrno = PA_SMALL_BUFFER;
LOG(L_ERR, "start_pidf_doc(): Buffer too small\n");
return -1;
}
add_string(XML_VERSION, XML_VERSION_L);
add_string(CRLF, CRLF_L);
add_string(WATCHERINFO_STAG, WATCHERINFO_STAG_L);
add_string(CRLF, CRLF_L);
if (_l < len) {
paerrno = PA_SMALL_BUFFER;
LOG(L_ERR, "winfo_add_resource(): Buffer too small\n");
return -1;
}
for (i = 0; i < n_strs; i++)
str_append(_b, strs[i].s, strs[i].len);
return 0;
}
/*
* Start a resource in a winfo document
*/
int winfo_start_resource(str* _b, int _l, str* _uri, watcher_t *watcher)
{
str strs[10];
int n_strs = 0;
int i;
int len = 0;
add_string(WATCHERLIST_START, WATCHERLIST_START_L);
add_pstr(_uri);
add_string(PACKAGE_START, PACKAGE_START_L);
add_string(event_package_name[watcher->event_package], strlen(event_package_name[watcher->event_package]));
add_string(PACKAGE_END, PACKAGE_END_L);
add_string(CRLF, CRLF_L);
if (_l < len) {
paerrno = PA_SMALL_BUFFER;
LOG(L_ERR, "winfo_add_resource(): Buffer too small\n");
return -1;
}
for (i = 0; i < n_strs; i++)
str_append(_b, strs[i].s, strs[i].len);
return 0;
}
/*
* End a resource in a winfo document
*/
int winfo_end_resource(str *_b, int _l)
{
str strs[10];
int n_strs = 0;
int i;
int len = 0;
add_string(WATCHERLIST_ETAG, WATCHERLIST_ETAG_L);
add_string(CRLF, CRLF_L);
if (_l < len) {
paerrno = PA_SMALL_BUFFER;
LOG(L_ERR, "winfo_add_resource(): Buffer too small\n");
return -1;
}
for (i = 0; i < n_strs; i++)
str_append(_b, strs[i].s, strs[i].len);
return 0;
}
/*
* End a winfo document
*/
int end_winfo_doc(str* _b, int _l)
{
if (_l < (WATCHERINFO_ETAG_L + CRLF_L)) {
paerrno = PA_SMALL_BUFFER;
LOG(L_ERR, "end_pidf_doc(): Buffer too small\n");
return -1;
}
str_append(_b, WATCHERINFO_ETAG CRLF, WATCHERINFO_ETAG_L + CRLF_L);
return 0;
}