You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kamailio/modules/presence/presentity.c

1793 lines
41 KiB

/*
* Copyright (C) 2006 Voice Sistem S.R.L.
*
* This file is part of Kamailio, a free SIP server.
*
* Kamailio 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
*
* Kamailio 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/*!
* \file
* \brief Kamailio presence module :: Presentity handling
* \ingroup presence
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "../../lib/srdb1/db.h"
#include "../../hashes.h"
#include "../../dprint.h"
#include "../../mem/shm_mem.h"
#include "../../xavp.h"
#include "../../str.h"
#include "../../data_lump_rpl.h"
#include "presentity.h"
#include "presence.h"
#include "notify.h"
#include "publish.h"
#include "hash.h"
#include "utils_func.h"
/* base priority value (20150101T000000) */
#define PRES_PRIORITY_TBASE 1420070400
xmlNodePtr xmlNodeGetNodeByName(xmlNodePtr node, const char *name,
const char *ns);
static str pu_200_rpl = str_init("OK");
static str pu_412_rpl = str_init("Conditional request failed");
static str str_offline_etag_val = str_init("*#-OFFLINE-#*");
#define ETAG_LEN 128
char* generate_ETag(int publ_count)
{
char* etag= NULL;
int size = 0;
etag = (char*)pkg_malloc(ETAG_LEN*sizeof(char));
if(etag ==NULL)
{
ERR_MEM(PKG_MEM_STR);
}
memset(etag, 0, ETAG_LEN*sizeof(char));
size = snprintf (etag, ETAG_LEN, "%c.%d.%d.%d.%d",prefix, startup_time, pid, counter, publ_count);
if( size <0 )
{
LM_ERR("unsuccessfull snprintf\n ");
pkg_free(etag);
return NULL;
}
if(size >= ETAG_LEN)
{
LM_ERR("buffer size overflown\n");
pkg_free(etag);
return NULL;
}
etag[size] = '\0';
LM_DBG("etag= %s / %d\n ",etag, size);
return etag;
error:
return NULL;
}
int publ_send200ok(struct sip_msg *msg, int lexpire, str etag)
{
char buf[128];
int buf_len= 128, size;
str hdr_append= {0, 0}, hdr_append2= {0, 0} ;
if (msg == NULL)
return 0;
LM_DBG("send 200OK reply\n");
LM_DBG("etag= %s - len= %d\n", etag.s, etag.len);
hdr_append.s = buf;
hdr_append.s[0]='\0';
hdr_append.len = snprintf(hdr_append.s, buf_len, "Expires: %d\r\n",
((lexpire==0)?0:(lexpire-expires_offset)));
if(hdr_append.len < 0)
{
LM_ERR("unsuccessful snprintf\n");
goto error;
}
if(hdr_append.len >= buf_len)
{
LM_ERR("buffer size overflown\n");
goto error;
}
hdr_append.s[hdr_append.len]= '\0';
if (add_lump_rpl( msg, hdr_append.s, hdr_append.len, LUMP_RPL_HDR)==0 )
{
LM_ERR("unable to add lump_rl\n");
goto error;
}
size= sizeof(char)*(20+etag.len) ;
hdr_append2.s = (char *)pkg_malloc(size);
if(hdr_append2.s == NULL)
{
ERR_MEM(PKG_MEM_STR);
}
hdr_append2.s[0]='\0';
hdr_append2.len = snprintf(hdr_append2.s, size, "SIP-ETag: %s\r\n", etag.s);
if(hdr_append2.len < 0)
{
LM_ERR("unsuccessful snprintf\n ");
goto error;
}
if(hdr_append2.len >= size)
{
LM_ERR("buffer size overflown\n");
goto error;
}
hdr_append2.s[hdr_append2.len]= '\0';
if (add_lump_rpl(msg, hdr_append2.s, hdr_append2.len, LUMP_RPL_HDR)==0 )
{
LM_ERR("unable to add lump_rl\n");
goto error;
}
if(slb.freply(msg, 200, &pu_200_rpl) < 0)
{
LM_ERR("sending reply\n");
goto error;
}
pkg_free(hdr_append2.s);
return 0;
error:
if(hdr_append2.s)
pkg_free(hdr_append2.s);
return -1;
}
/**
* get priority value for presence document
*/
unsigned int pres_get_priority(void)
{
sr_xavp_t *vavp = NULL;
str vname = str_init("priority");
if(pres_xavp_cfg.s==NULL || pres_xavp_cfg.len<=0) {
return 0;
}
vavp = xavp_get_child_with_ival(&pres_xavp_cfg, &vname);
if(vavp!=NULL) {
return (unsigned int)vavp->val.v.i;
}
return (unsigned int)(time(NULL) - PRES_PRIORITY_TBASE);
}
/**
* create new presentity record
*/
presentity_t* new_presentity( str* domain,str* user,int expires,
pres_ev_t* event, str* etag, str* sender)
{
presentity_t *presentity= NULL;
int size, init_len;
/* allocating memory for presentity */
size = sizeof(presentity_t)+ domain->len+ user->len+ etag->len +1;
if(sender)
size+= sizeof(str)+ sender->len* sizeof(char);
init_len= size;
presentity = (presentity_t*)pkg_malloc(size);
if(presentity == NULL)
{
ERR_MEM(PKG_MEM_STR);
}
memset(presentity, 0, size);
size= sizeof(presentity_t);
presentity->domain.s = (char*)presentity+ size;
strncpy(presentity->domain.s, domain->s, domain->len);
presentity->domain.len = domain->len;
size+= domain->len;
presentity->user.s = (char*)presentity+size;
strncpy(presentity->user.s, user->s, user->len);
presentity->user.len = user->len;
size+= user->len;
presentity->etag.s = (char*)presentity+ size;
memcpy(presentity->etag.s, etag->s, etag->len);
presentity->etag.s[etag->len]= '\0';
presentity->etag.len = etag->len;
size+= etag->len+1;
if(sender)
{
presentity->sender= (str*)((char*)presentity+ size);
size+= sizeof(str);
presentity->sender->s= (char*)presentity + size;
memcpy(presentity->sender->s, sender->s, sender->len);
presentity->sender->len= sender->len;
size+= sender->len;
}
if(size> init_len)
{
LM_ERR("buffer size overflow init_len= %d, size= %d\n", init_len, size);
goto error;
}
presentity->event= event;
presentity->expires = expires;
presentity->received_time= (int)time(NULL);
presentity->priority = pres_get_priority();
return presentity;
error:
if(presentity)
pkg_free(presentity);
return NULL;
}
xmlNodePtr xmlNodeGetChildByName(xmlNodePtr node, const char *name)
{
xmlNodePtr cur = node->children;
while (cur) {
if (xmlStrcasecmp(cur->name, (unsigned char*)name) == 0)
return cur;
cur = cur->next;
}
return NULL;
}
int check_if_dialog(str body, int *is_dialog, char **dialog_id)
{
xmlDocPtr doc;
xmlNodePtr node;
char *tmp_dialog_id;
*dialog_id = NULL;
*is_dialog = 0;
doc = xmlParseMemory(body.s, body.len);
if(doc== NULL)
{
LM_ERR("failed to parse xml document\n");
return -1;
}
node = doc->children;
node = xmlNodeGetChildByName(node, "dialog");
if(node != NULL)
{
*is_dialog = 1;
tmp_dialog_id = (char *)xmlGetProp(node, (xmlChar *)"id");
if (tmp_dialog_id != NULL)
{
*dialog_id = strdup(tmp_dialog_id);
xmlFree(tmp_dialog_id);
}
}
xmlFreeDoc(doc);
return 0;
}
int parse_dialog_state_from_body(str body, int *is_dialog, char **state)
{
xmlDocPtr doc;
xmlNodePtr node;
xmlNodePtr childNode;
char *tmp_state;
*state = NULL;
*is_dialog = 0;
doc = xmlParseMemory(body.s, body.len);
if(doc== NULL)
{
LM_ERR("failed to parse xml document\n");
return -1;
}
node = doc->children;
node = xmlNodeGetChildByName(node, "dialog");
if(node != NULL)
{
*is_dialog = 1;
childNode = xmlNodeGetChildByName(node, "state");
tmp_state = (char *)xmlNodeGetContent(childNode);
if (tmp_state != NULL)
{
*state = strdup(tmp_state);
xmlFree(tmp_state);
}
}
xmlFreeDoc(doc);
return 0;
}
int delete_presentity_if_dialog_id_exists(presentity_t* presentity, char* dialog_id) {
db_key_t query_cols[13], result_cols[6];
db_op_t query_ops[13];
db_val_t query_vals[13];
int n_query_cols = 0;
int rez_body_col = 0, rez_etag_col = 0, n_result_cols= 0;
db1_res_t *result = NULL;
db_row_t *row = NULL;
db_val_t *row_vals = NULL;
char* db_dialog_id = NULL;
int db_is_dialog = 0;
str tmp_db_body, tmp_db_etag;
int i = 0;
presentity_t old_presentity;
query_cols[n_query_cols] = &str_domain_col;
query_ops[n_query_cols] = OP_EQ;
query_vals[n_query_cols].type = DB1_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val = presentity->domain;
n_query_cols++;
query_cols[n_query_cols] = &str_username_col;
query_ops[n_query_cols] = OP_EQ;
query_vals[n_query_cols].type = DB1_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val = presentity->user;
n_query_cols++;
query_cols[n_query_cols] = &str_event_col;
query_ops[n_query_cols] = OP_EQ;
query_vals[n_query_cols].type = DB1_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val = presentity->event->name;
n_query_cols++;
result_cols[rez_body_col=n_result_cols++] = &str_body_col;
result_cols[rez_etag_col=n_result_cols++] = &str_etag_col;
if (pa_dbf.use_table(pa_db, &presentity_table) < 0)
{
LM_ERR("unsuccessful sql 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, &result) < 0)
{
LM_ERR("unsuccessful sql query\n");
return -2;
}
if(result == NULL)
return -3;
/* no results from query definitely means no dialog exists */
if (result->n <= 0) {
pa_dbf.free_result(pa_db, result);
return 0;
}
// Loop the rows returned from the DB
for (i=0; i < result->n; i++)
{
row = &result->rows[i];
row_vals = ROW_VALUES(row);
tmp_db_body.s = (char*)row_vals[rez_body_col].val.string_val;
tmp_db_body.len = strlen(tmp_db_body.s);
tmp_db_etag.s = (char*)row_vals[rez_etag_col].val.string_val;
tmp_db_etag.len = strlen(tmp_db_etag.s);
if (check_if_dialog(tmp_db_body, &db_is_dialog, &db_dialog_id) == 0)
{
// If ID from DB matches the one we supplied
if (db_dialog_id && !strcmp(db_dialog_id, dialog_id))
{
old_presentity.domain = presentity->domain;
old_presentity.user = presentity->user;
old_presentity.event = presentity->event;
old_presentity.etag = tmp_db_etag;
LM_WARN("Presentity already exists - deleting it\n");
delete_presentity(&old_presentity);
pa_dbf.free_result(pa_db, result);
result = NULL;
free(db_dialog_id);
db_dialog_id = NULL;
return 1;
}
free(db_dialog_id);
db_dialog_id = NULL;
}
}
pa_dbf.free_result(pa_db, result);
result = NULL;
return 0;
}
int get_dialog_state(presentity_t* presentity, char** state)
{
db_key_t query_cols[13], result_cols[6];
db_op_t query_ops[13];
db_val_t query_vals[13];
int n_query_cols = 0;
int rez_body_col = 0, n_result_cols= 0;
db1_res_t *result = NULL;
db_row_t *row = NULL;
db_val_t *row_vals = NULL;
int db_is_dialog = 0;
str tmp_db_body;
int i = 0, parse_state_result = 0;
*state = NULL;
query_cols[n_query_cols] = &str_domain_col;
query_ops[n_query_cols] = OP_EQ;
query_vals[n_query_cols].type = DB1_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val = presentity->domain;
n_query_cols++;
query_cols[n_query_cols] = &str_username_col;
query_ops[n_query_cols] = OP_EQ;
query_vals[n_query_cols].type = DB1_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val = presentity->user;
n_query_cols++;
query_cols[n_query_cols] = &str_event_col;
query_ops[n_query_cols] = OP_EQ;
query_vals[n_query_cols].type = DB1_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val = presentity->event->name;
n_query_cols++;
query_cols[n_query_cols] = &str_etag_col;
query_ops[n_query_cols] = OP_EQ;
query_vals[n_query_cols].type = DB1_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val = presentity->etag;
n_query_cols++;
result_cols[rez_body_col=n_result_cols++] = &str_body_col;
if (pa_dbf.use_table(pa_db, &presentity_table) < 0)
{
LM_ERR("unsuccessful sql 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, &result) < 0)
{
LM_ERR("unsuccessful sql query\n");
return -2;
}
if(result == NULL)
return -3;
/* no results from query definitely means no dialog exists */
if (result->n <= 0) {
pa_dbf.free_result(pa_db, result);
return 0;
}
// Loop the rows returned from the DB
for (i=0; i < result->n; i++)
{
row = &result->rows[i];
row_vals = ROW_VALUES(row);
tmp_db_body.s = (char*)row_vals[rez_body_col].val.string_val;
tmp_db_body.len = strlen(tmp_db_body.s);
parse_state_result = parse_dialog_state_from_body(tmp_db_body,
&db_is_dialog, state);
pa_dbf.free_result(pa_db, result);
result = NULL;
return parse_state_result;
}
pa_dbf.free_result(pa_db, result);
result = NULL;
return 0;
}
int is_dialog_terminated(presentity_t* presentity)
{
char *state = NULL;
int rtn;
get_dialog_state(presentity, &state);
rtn = state && !strcasecmp(state, "terminated");
free(state);
return rtn;
}
int update_presentity(struct sip_msg* msg, presentity_t* presentity, str* body,
int new_t, int* sent_reply, char* sphere)
{
db_key_t query_cols[13], update_keys[9], result_cols[6];
db_op_t query_ops[13];
db_val_t query_vals[13], update_vals[9];
db1_res_t *result= NULL;
int n_query_cols = 0;
int n_update_cols = 0;
char* dot= NULL;
str etag= {0, 0};
str cur_etag= {0, 0};
str* rules_doc= NULL;
str pres_uri= {0, 0};
int rez_body_col, rez_sender_col, n_result_cols= 0;
db_row_t *row = NULL ;
db_val_t *row_vals = NULL;
str old_body, sender;
int is_dialog= 0, bla_update_publish= 1;
int affected_rows = 0;
int ret = -1;
int db_record_exists = 0;
int num_watchers = 0;
char *old_dialog_id = NULL, *dialog_id = NULL;
if (sent_reply) *sent_reply= 0;
if(pres_notifier_processes == 0 && presentity->event->req_auth)
{
/* get rules_document */
if(presentity->event->get_rules_doc(&presentity->user,
&presentity->domain, &rules_doc) < 0)
{
LM_ERR("getting rules doc\n");
goto error;
}
}
if(uandd_to_uri(presentity->user, presentity->domain, &pres_uri)< 0)
{
LM_ERR("constructing uri from user and domain\n");
goto error;
}
query_cols[n_query_cols] = &str_domain_col;
query_ops[n_query_cols] = OP_EQ;
query_vals[n_query_cols].type = DB1_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val = presentity->domain;
n_query_cols++;
query_cols[n_query_cols] = &str_username_col;
query_ops[n_query_cols] = OP_EQ;
query_vals[n_query_cols].type = DB1_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val = presentity->user;
n_query_cols++;
query_cols[n_query_cols] = &str_event_col;
query_ops[n_query_cols] = OP_EQ;
query_vals[n_query_cols].type = DB1_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val = presentity->event->name;
n_query_cols++;
query_cols[n_query_cols] = &str_etag_col;
query_ops[n_query_cols] = OP_EQ;
query_vals[n_query_cols].type = DB1_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val = presentity->etag;
n_query_cols++;
result_cols[rez_body_col= n_result_cols++] = &str_body_col;
result_cols[rez_sender_col= n_result_cols++] = &str_sender_col;
if(new_t)
{
/* insert new record in hash_table */
if ( publ_cache_enabled &&
insert_phtable(&pres_uri, presentity->event->evp->type, sphere)< 0)
{
LM_ERR("inserting record in hash table\n");
goto error;
}
/* insert new record into database */
query_cols[n_query_cols] = &str_sender_col;
query_vals[n_query_cols].type = DB1_STR;
query_vals[n_query_cols].nul = 0;
if(presentity->sender)
{
query_vals[n_query_cols].val.str_val.s = presentity->sender->s;
query_vals[n_query_cols].val.str_val.len = presentity->sender->len;
} else {
query_vals[n_query_cols].val.str_val.s = "";
query_vals[n_query_cols].val.str_val.len = 0;
}
n_query_cols++;
query_cols[n_query_cols] = &str_body_col;
query_vals[n_query_cols].type = DB1_BLOB;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val = *body;
n_query_cols++;
query_cols[n_query_cols] = &str_received_time_col;
query_vals[n_query_cols].type = DB1_INT;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.int_val = presentity->received_time;
n_query_cols++;
query_cols[n_query_cols] = &str_priority_col;
query_vals[n_query_cols].type = DB1_INT;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.int_val = presentity->priority;
n_query_cols++;
if (presentity->expires != -1)
{
/* A real PUBLISH */
query_cols[n_query_cols] = &str_expires_col;
query_vals[n_query_cols].type = DB1_INT;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.int_val = presentity->expires+
(int)time(NULL);
n_query_cols++;
if (pa_dbf.use_table(pa_db, &presentity_table) < 0)
{
LM_ERR("unsuccessful use_table\n");
goto error;
}
if (pa_dbf.start_transaction)
{
if (pa_dbf.start_transaction(pa_db, db_table_lock) < 0)
{
LM_ERR("in start_transaction\n");
goto error;
}
}
check_if_dialog(*body, &is_dialog, &dialog_id);
if ( dialog_id ) {
if (delete_presentity_if_dialog_id_exists(presentity, dialog_id) < 0) {
free(dialog_id);
dialog_id = NULL;
goto error;
}
free(dialog_id);
dialog_id = NULL;
}
LM_DBG("inserting %d cols into table\n",n_query_cols);
if (pa_dbf.insert(pa_db, query_cols, query_vals, n_query_cols) < 0)
{
LM_ERR("inserting new record in database\n");
goto error;
}
}
else
{
/* A hard-state PUBLISH */
query_cols[n_query_cols] = &str_expires_col;
query_vals[n_query_cols].type = DB1_INT;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.int_val = -1;
n_query_cols++;
if (pa_dbf.use_table(pa_db, &presentity_table) < 0)
{
LM_ERR("unsuccessful use_table\n");
goto error;
}
if (pa_dbf.replace == NULL)
{
LM_ERR("replace is required for pidf-manipulation support\n");
goto error;
}
if (pa_dbf.start_transaction)
{
if (pa_dbf.start_transaction(pa_db, db_table_lock) < 0)
{
LM_ERR("in start_transaction\n");
goto error;
}
}
if (pa_dbf.replace(pa_db, query_cols, query_vals, n_query_cols, 4, 0) < 0)
{
LM_ERR("replacing record in database\n");
goto error;
}
}
if( publ_send200ok(msg, presentity->expires, presentity->etag)< 0)
{
LM_ERR("sending 200OK\n");
goto error;
}
if (sent_reply) *sent_reply= 1;
goto send_notify;
}
else
{
if (pa_dbf.use_table(pa_db, &presentity_table) < 0)
{
LM_ERR("unsuccessful sql use table\n");
goto error;
}
if (pa_dbf.start_transaction)
{
if (pa_dbf.start_transaction(pa_db, db_table_lock) < 0)
{
LM_ERR("in start_transaction\n");
goto error;
}
}
if(EVENT_DIALOG_SLA(presentity->event->evp))
{
if (pa_dbf.query (pa_db, query_cols, query_ops, query_vals,
result_cols, n_query_cols, n_result_cols, 0, &result) < 0)
{
LM_ERR("unsuccessful sql query\n");
goto error;
}
if(result== NULL)
goto error;
if (!(result->n > 0))
goto send_412;
db_record_exists= 1;
/* analize if previous body has a dialog */
row = &result->rows[0];
row_vals = ROW_VALUES(row);
old_body.s = (char*)row_vals[rez_body_col].val.string_val;
old_body.len = strlen(old_body.s);
if(check_if_dialog(*body, &is_dialog, &dialog_id)< 0)
{
LM_ERR("failed to check if dialog stored\n");
if(dialog_id) {
free(dialog_id);
dialog_id = NULL;
}
goto error;
}
free(dialog_id);
if(is_dialog== 1) /* if the new body has a dialog - overwrite */
{
goto after_dialog_check;
}
if(check_if_dialog(old_body, &is_dialog, &old_dialog_id)< 0)
{
LM_ERR("failed to check if dialog stored\n");
if(old_dialog_id) {
free(old_dialog_id);
old_dialog_id = NULL;
}
goto error;
}
/* if the old body has no dialog - overwrite */
if(is_dialog==0 ) {
if(old_dialog_id) {
free(old_dialog_id);
old_dialog_id = NULL;
}
goto after_dialog_check;
}
if(old_dialog_id) {
free(old_dialog_id);
old_dialog_id = NULL;
}
sender.s = (char*)row_vals[rez_sender_col].val.string_val;
sender.len= strlen(sender.s);
LM_DBG("old_sender = %.*s\n", sender.len, sender.s );
if(presentity->sender)
{
if(!(presentity->sender->len == sender.len &&
presence_sip_uri_match(presentity->sender, &sender)== 0))
bla_update_publish= 0;
}
after_dialog_check:
pa_dbf.free_result(pa_db, result);
result = NULL;
}
if(presentity->expires <= 0)
{
if (!db_record_exists)
{
if (pa_dbf.query (pa_db, query_cols, query_ops, query_vals,
result_cols, n_query_cols, n_result_cols, 0, &result) < 0)
{
LM_ERR("unsuccessful sql query\n");
goto error;
}
if(result== NULL)
goto error;
if (!(result->n > 0))
goto send_412;
db_record_exists = 1;
pa_dbf.free_result(pa_db, result);
result = NULL;
}
if( publ_send200ok(msg, presentity->expires, presentity->etag)< 0)
{
LM_ERR("sending 200OK reply\n");
goto error;
}
if (sent_reply) *sent_reply= 1;
if (pres_notifier_processes > 0)
{
if ((num_watchers = publ_notify_notifier(pres_uri, presentity->event)) < 0)
{
LM_ERR("updating watcher records\n");
goto error;
}
if (num_watchers > 0)
{
if (mark_presentity_for_delete(presentity) < 0)
{
LM_ERR("Marking presentities\n");
goto error;
}
}
}
else
{
if( publ_notify( presentity, pres_uri, body, &presentity->etag, rules_doc)< 0 )
{
LM_ERR("while sending notify\n");
goto error;
}
}
if (pres_notifier_processes == 0 || num_watchers == 0)
{
if (delete_presentity(presentity) < 0)
{
LM_ERR("Deleting presentity\n");
goto error;
}
LM_DBG("deleted from db %.*s\n", presentity->user.len, presentity->user.s);
}
/* delete from hash table */
if( publ_cache_enabled &&
delete_phtable(&pres_uri, presentity->event->evp->type)< 0)
{
LM_ERR("deleting record from hash table\n");
goto error;
}
goto done;
}
n_update_cols= 0;
/* if event dialog and is_dialog -> if sender not the same as
* old sender do not overwrite */
if( EVENT_DIALOG_SLA(presentity->event->evp) && bla_update_publish==0)
{
LM_DBG("drop Publish for BLA from a different sender that"
" wants to overwrite an existing dialog\n");
LM_DBG("sender = %.*s\n", presentity->sender->len, presentity->sender->s );
if( publ_send200ok(msg, presentity->expires, presentity->etag)< 0)
{
LM_ERR("sending 200OK reply\n");
goto error;
}
if (sent_reply) *sent_reply= 1;
goto done;
}
if(presentity->event->etag_not_new== 0)
{
/* generate another etag */
unsigned int publ_nr;
str str_publ_nr= {0, 0};
dot= presentity->etag.s+ presentity->etag.len;
while(*dot!= '.' && str_publ_nr.len< presentity->etag.len)
{
str_publ_nr.len++;
dot--;
}
if(str_publ_nr.len== presentity->etag.len)
{
LM_ERR("wrong etag\n");
goto error;
}
str_publ_nr.s= dot+1;
str_publ_nr.len--;
if( str2int(&str_publ_nr, &publ_nr)< 0)
{
LM_ERR("converting string to int\n");
goto error;
}
etag.s = generate_ETag(publ_nr+1);
if(etag.s == NULL)
{
LM_ERR("while generating etag\n");
goto error;
}
etag.len=(strlen(etag.s));
cur_etag= etag;
update_keys[n_update_cols] = &str_etag_col;
update_vals[n_update_cols].type = DB1_STR;
update_vals[n_update_cols].nul = 0;
update_vals[n_update_cols].val.str_val = etag;
n_update_cols++;
}
else
cur_etag= presentity->etag;
if (presentity->event->evp->type==EVENT_DIALOG) {
if(is_dialog_terminated(presentity))
{
LM_WARN("Trying to update an already terminated state."
" Skipping update.\n");
/* send 200OK */
if (publ_send200ok(msg, presentity->expires, cur_etag)< 0) {
LM_ERR("sending 200OK reply\n");
goto error;
}
if (sent_reply) *sent_reply= 1;
if(etag.s)
pkg_free(etag.s);
etag.s= NULL;
goto done;
}
}
update_keys[n_update_cols] = &str_expires_col;
update_vals[n_update_cols].type = DB1_INT;
update_vals[n_update_cols].nul = 0;
update_vals[n_update_cols].val.int_val= presentity->expires +
(int)time(NULL);
n_update_cols++;
update_keys[n_update_cols] = &str_received_time_col;
update_vals[n_update_cols].type = DB1_INT;
update_vals[n_update_cols].nul = 0;
update_vals[n_update_cols].val.int_val= presentity->received_time;
n_update_cols++;
update_keys[n_update_cols] = &str_priority_col;
update_vals[n_update_cols].type = DB1_INT;
update_vals[n_update_cols].nul = 0;
update_vals[n_update_cols].val.int_val= presentity->priority;
n_update_cols++;
if(body && body->s)
{
update_keys[n_update_cols] = &str_body_col;
update_vals[n_update_cols].type = DB1_BLOB;
update_vals[n_update_cols].nul = 0;
update_vals[n_update_cols].val.str_val = *body;
n_update_cols++;
/* updated stored sphere */
if(sphere_enable &&
presentity->event->evp->type== EVENT_PRESENCE)
{
if( publ_cache_enabled &&
update_phtable(presentity, pres_uri, *body)< 0)
{
LM_ERR("failed to update sphere for presentity\n");
goto error;
}
}
}
if( presentity->sender)
{
update_keys[n_update_cols] = &str_sender_col;
update_vals[n_update_cols].type = DB1_STR;
update_vals[n_update_cols].nul = 0;
update_vals[n_update_cols].val.str_val = *presentity->sender;
n_update_cols++;
}
/* if there is no support for affected_rows and no previous query has been done, do query */
if (!pa_dbf.affected_rows && !db_record_exists)
{
if (pa_dbf.query (pa_db, query_cols, query_ops, query_vals,
result_cols, n_query_cols, n_result_cols, 0, &result) < 0)
{
LM_ERR("unsuccessful sql query\n");
goto error;
}
if(result== NULL)
goto error;
if (!(result->n > 0))
goto send_412;
db_record_exists = 1;
pa_dbf.free_result(pa_db, result);
result = NULL;
}
if( pa_dbf.update( pa_db,query_cols, query_ops, query_vals,
update_keys, update_vals, n_query_cols, n_update_cols )<0)
{
LM_ERR("updating published info in database\n");
goto error;
}
if (pa_dbf.affected_rows && !db_record_exists)
{
if ((affected_rows = pa_dbf.affected_rows ( pa_db ))<0)
{
LM_ERR("unsuccessful sql affected rows operation");
goto error;
}
LM_DBG ("affected rows after update: %d\n", affected_rows );
}
/*if either affected_rows (if exists) or select query show that there is no line in database*/
if ((pa_dbf.affected_rows && !affected_rows) || (!pa_dbf.affected_rows && !db_record_exists))
goto send_412;
/* send 200OK */
if (publ_send200ok(msg, presentity->expires, cur_etag)< 0)
{
LM_ERR("sending 200OK reply\n");
goto error;
}
if (sent_reply) *sent_reply= 1;
if(etag.s)
pkg_free(etag.s);
etag.s= NULL;
if(!body)
goto done;
}
send_notify:
/* send notify with presence information */
if (pres_notifier_processes > 0)
{
if (publ_notify_notifier(pres_uri, presentity->event) < 0)
{
LM_ERR("updating watcher records\n");
goto error;
}
}
else
{
if( publ_notify( presentity, pres_uri, body, NULL, rules_doc)< 0 )
{
LM_ERR("while sending notify\n");
goto error;
}
}
done:
if(rules_doc)
{
if(rules_doc->s)
pkg_free(rules_doc->s);
pkg_free(rules_doc);
}
if(pres_uri.s)
pkg_free(pres_uri.s);
if (pa_dbf.end_transaction)
{
if (pa_dbf.end_transaction(pa_db) < 0)
{
LM_ERR("in end_transaction\n");
goto error;
}
}
return 0;
send_412:
LM_ERR("No E_Tag match %*s\n", presentity->etag.len, presentity->etag.s);
if (msg != NULL)
{
if (slb.freply(msg, 412, &pu_412_rpl) < 0)
{
LM_ERR("sending '412 Conditional request failed' reply\n");
goto error;
}
}
if (sent_reply) *sent_reply= 1;
ret = 0;
error:
if(result)
pa_dbf.free_result(pa_db, result);
if(etag.s)
pkg_free(etag.s);
if(rules_doc) {
if(rules_doc->s)
pkg_free(rules_doc->s);
pkg_free(rules_doc);
}
if(pres_uri.s) {
pkg_free(pres_uri.s);
pres_uri.s = NULL;
}
if (pa_dbf.abort_transaction) {
if (pa_dbf.abort_transaction(pa_db) < 0) {
LM_ERR("in abort_transaction\n");
}
}
return ret;
}
int pres_htable_restore(void)
{
/* query all records from presentity table and insert records
* in presentity table */
db_key_t result_cols[6];
db1_res_t *result= NULL;
db_row_t *row= NULL ;
db_val_t *row_vals;
int i;
str user, domain, ev_str, uri, body;
int n_result_cols= 0;
int user_col, domain_col, event_col, expires_col, body_col = 0;
int event;
event_t ev;
char* sphere= NULL;
static str query_str;
result_cols[user_col= n_result_cols++]= &str_username_col;
result_cols[domain_col= n_result_cols++]= &str_domain_col;
result_cols[event_col= n_result_cols++]= &str_event_col;
result_cols[expires_col= n_result_cols++]= &str_expires_col;
if(sphere_enable)
result_cols[body_col= n_result_cols++]= &str_body_col;
if (pa_dbf.use_table(pa_db, &presentity_table) < 0)
{
LM_ERR("unsuccessful use table sql operation\n");
goto error;
}
query_str = str_username_col;
if (db_fetch_query(&pa_dbf, pres_fetch_rows, pa_db, 0, 0, 0, result_cols,
0, n_result_cols, &query_str, &result) < 0)
{
LM_ERR("querying presentity\n");
goto error;
}
if(result== NULL)
goto error;
if(result->n<= 0)
{
pa_dbf.free_result(pa_db, result);
return 0;
}
do {
for(i= 0; i< result->n; i++)
{
row = &result->rows[i];
row_vals = ROW_VALUES(row);
sphere= NULL;
user.s= (char*)row_vals[user_col].val.string_val;
user.len= strlen(user.s);
domain.s= (char*)row_vals[domain_col].val.string_val;
domain.len= strlen(domain.s);
ev_str.s= (char*)row_vals[event_col].val.string_val;
ev_str.len= strlen(ev_str.s);
if(event_parser(ev_str.s, ev_str.len, &ev)< 0)
{
LM_ERR("parsing event\n");
free_event_params(ev.params.list, PKG_MEM_TYPE);
goto error;
}
event= ev.type;
free_event_params(ev.params.list, PKG_MEM_TYPE);
if(uandd_to_uri(user, domain, &uri)< 0)
{
LM_ERR("constructing uri\n");
goto error;
}
/* insert in hash_table*/
if(sphere_enable && event== EVENT_PRESENCE )
{
body.s= (char*)row_vals[body_col].val.string_val;
body.len= strlen(body.s);
sphere= extract_sphere(body);
}
if(insert_phtable(&uri, event, sphere)< 0)
{
LM_ERR("inserting record in presentity hash table");
pkg_free(uri.s);
if(sphere)
pkg_free(sphere);
goto error;
}
if(sphere)
pkg_free(sphere);
pkg_free(uri.s);
}
} while((db_fetch_next(&pa_dbf, pres_fetch_rows, pa_db, &result)==1)
&& (RES_ROW_N(result)>0));
pa_dbf.free_result(pa_db, result);
return 0;
error:
if(result)
pa_dbf.free_result(pa_db, result);
return -1;
}
char* extract_sphere(str body)
{
/* check for a rpid sphere element */
xmlDocPtr doc= NULL;
xmlNodePtr node;
char* cont, *sphere= NULL;
doc= xmlParseMemory(body.s, body.len);
if(doc== NULL)
{
LM_ERR("failed to parse xml body\n");
return NULL;
}
node= xmlNodeGetNodeByName(doc->children, "sphere", "rpid");
if(node== NULL)
node= xmlNodeGetNodeByName(doc->children, "sphere", "r");
if(node)
{
LM_DBG("found sphere definition\n");
cont= (char*)xmlNodeGetContent(node);
if(cont== NULL)
{
LM_ERR("failed to extract sphere node content\n");
goto error;
}
sphere= (char*)pkg_malloc((strlen(cont)+ 1)*sizeof(char));
if(sphere== NULL)
{
xmlFree(cont);
ERR_MEM(PKG_MEM_STR);
}
strcpy(sphere, cont);
xmlFree(cont);
}
else
LM_DBG("didn't find sphere definition\n");
error:
xmlFreeDoc(doc);
return sphere;
}
xmlNodePtr xmlNodeGetNodeByName(xmlNodePtr node, const char *name,
const char *ns)
{
xmlNodePtr cur = node;
while (cur) {
xmlNodePtr match = NULL;
if (xmlStrcasecmp(cur->name, (unsigned char*)name) == 0) {
if (!ns || (cur->ns && xmlStrcasecmp(cur->ns->prefix,
(unsigned char*)ns) == 0))
return cur;
}
match = xmlNodeGetNodeByName(cur->children, name, ns);
if (match)
return match;
cur = cur->next;
}
return NULL;
}
char* get_sphere(str* pres_uri)
{
unsigned int hash_code;
char* sphere= NULL;
pres_entry_t* p;
db_key_t query_cols[6];
db_val_t query_vals[6];
db_key_t result_cols[6];
db1_res_t *result = NULL;
db_row_t *row= NULL;
db_val_t *row_vals;
int n_result_cols = 0;
int n_query_cols = 0;
struct sip_uri uri;
str body;
static str query_str;
if(!sphere_enable)
return NULL;
if ( publ_cache_enabled )
{
/* search in hash table*/
hash_code= core_case_hash(pres_uri, NULL, phtable_size);
lock_get(&pres_htable[hash_code].lock);
p= search_phtable(pres_uri, EVENT_PRESENCE, hash_code);
if(p)
{
if(p->sphere)
{
sphere= (char*)pkg_malloc(strlen(p->sphere)* sizeof(char));
if(sphere== NULL)
{
lock_release(&pres_htable[hash_code].lock);
ERR_MEM(PKG_MEM_STR);
}
strcpy(sphere, p->sphere);
}
lock_release(&pres_htable[hash_code].lock);
return sphere;
}
lock_release(&pres_htable[hash_code].lock);
}
if(parse_uri(pres_uri->s, pres_uri->len, &uri)< 0)
{
LM_ERR("failed to parse presentity uri\n");
goto error;
}
query_cols[n_query_cols] = &str_domain_col;
query_vals[n_query_cols].type = DB1_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val = uri.host;
n_query_cols++;
query_cols[n_query_cols] = &str_username_col;
query_vals[n_query_cols].type = DB1_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val = uri.user;
n_query_cols++;
query_cols[n_query_cols] = &str_event_col;
query_vals[n_query_cols].type = DB1_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val.s= "presence";
query_vals[n_query_cols].val.str_val.len= 8;
n_query_cols++;
result_cols[n_result_cols++] = &str_body_col;
if (pa_dbf.use_table(pa_db, &presentity_table) < 0)
{
LM_ERR("in use_table\n");
return NULL;
}
if(pres_retrieve_order==1) {
query_str = pres_retrieve_order_by;
} else {
query_str = str_received_time_col;
}
if (pa_dbf.query (pa_db, query_cols, 0, query_vals,
result_cols, n_query_cols, n_result_cols, &query_str , &result) < 0)
{
LM_ERR("failed to query %.*s table\n", presentity_table.len, presentity_table.s);
if(result)
pa_dbf.free_result(pa_db, result);
return NULL;
}
if(result== NULL)
return NULL;
if (result->n<=0 )
{
LM_DBG("no published record found in database\n");
pa_dbf.free_result(pa_db, result);
return NULL;
}
row = &result->rows[result->n-1];
row_vals = ROW_VALUES(row);
if(row_vals[0].val.string_val== NULL)
{
LM_ERR("NULL notify body record\n");
goto error;
}
body.s= (char*)row_vals[0].val.string_val;
body.len= strlen(body.s);
if(body.len== 0)
{
LM_ERR("Empty notify body record\n");
goto error;
}
sphere= extract_sphere(body);
pa_dbf.free_result(pa_db, result);
return sphere;
error:
if(result)
pa_dbf.free_result(pa_db, result);
return NULL;
}
int mark_presentity_for_delete(presentity_t *pres)
{
db_key_t query_cols[4], result_cols[1], update_cols[3];
db_val_t query_vals[4], update_vals[3], *value;
db_row_t *row;
db1_res_t *result = NULL;
int n_query_cols = 0, n_update_cols = 0;
int ret = -1;
str *cur_body = NULL, *new_body = NULL;
db_query_f query_fn = pa_dbf.query_lock ? pa_dbf.query_lock : pa_dbf.query;
if (pres->event->agg_nbody == NULL)
{
/* Nothing clever to do here... just delete */
if (delete_presentity(pres) < 0)
{
LM_ERR("deleting presentity\n");
goto error;
}
goto done;
}
if (pa_dbf.use_table(pa_db, &presentity_table) < 0)
{
LM_ERR("unsuccessful use table sql operation\n");
goto error;
}
query_cols[n_query_cols] = &str_username_col;
query_vals[n_query_cols].type = DB1_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val = pres->user;
n_query_cols++;
query_cols[n_query_cols] = &str_domain_col;
query_vals[n_query_cols].type = DB1_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val = pres->domain;
n_query_cols++;
query_cols[n_query_cols] = &str_event_col;
query_vals[n_query_cols].type = DB1_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val = pres->event->name;
n_query_cols++;
query_cols[n_query_cols] = &str_etag_col;
query_vals[n_query_cols].type = DB1_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val = pres->etag;
n_query_cols++;
result_cols[0] = &str_body_col;
if (pa_dbf.start_transaction)
{
if (pa_dbf.start_transaction(pa_db, db_table_lock) < 0)
{
LM_ERR("in start_transaction\n");
goto error;
}
}
if (query_fn(pa_db, query_cols, 0, query_vals, result_cols,
n_query_cols, 1, 0, &result) < 0)
{
LM_ERR("query failed\n");
goto error;
}
if (result == NULL)
{
LM_ERR("bad result\n");
goto error;
}
if (RES_ROW_N(result) <= 0)
{
/* Can happen when the timer and notifier processes clash */
LM_INFO("Found 0 presentities - expected 1\n");
goto done;
}
if (RES_ROW_N(result) > 1)
{
/* More that one is prevented by DB constraint - but handle
* it anyway */
LM_ERR("Found %d presentities - expected 1\n", RES_ROW_N(result));
if (delete_presentity(pres) < 0)
{
LM_ERR("deleting presentity\n");
goto error;
}
/* Want the calling function to continue properly so do not
* return an error */
goto done;
}
row = RES_ROWS(result);
value = ROW_VALUES(row);
if ((cur_body = (str *) pkg_malloc(sizeof(str))) == NULL)
{
LM_ERR("allocating pkg memory\n");
goto error;
}
cur_body->s = (char *) value->val.string_val;
cur_body->len = strlen (cur_body->s);
if ((new_body = pres->event->agg_nbody(&pres->user, &pres->domain,
&cur_body, 1, 0)) == NULL)
{
LM_ERR("preparing body\n");
goto error;
}
update_cols[n_update_cols] = &str_etag_col;
update_vals[n_update_cols].type = DB1_STR;
update_vals[n_update_cols].nul = 0;
update_vals[n_update_cols].val.str_val = str_offline_etag_val;
n_update_cols++;
update_cols[n_update_cols] = &str_expires_col;
update_vals[n_update_cols].type = DB1_INT;
update_vals[n_update_cols].nul = 0;
update_vals[n_update_cols].val.int_val = (int)time(NULL);
n_update_cols++;
update_cols[n_update_cols] = &str_body_col;
update_vals[n_update_cols].type = DB1_STR;
update_vals[n_update_cols].nul = 0;
update_vals[n_update_cols].val.str_val.s = new_body->s;
update_vals[n_update_cols].val.str_val.len = new_body->len;
n_update_cols++;
if (pa_dbf.update(pa_db, query_cols, 0, query_vals, update_cols,
update_vals, n_query_cols, n_update_cols) < 0)
{
LM_ERR("unsuccessful sql update operation");
goto error;
}
if (pa_dbf.end_transaction)
{
if (pa_dbf.end_transaction(pa_db) < 0)
{
LM_ERR("in end_transaction\n");
goto error;
}
}
if (pa_dbf.affected_rows)
ret = pa_dbf.affected_rows(pa_db);
else
done:
ret = 0;
error:
free_notify_body(new_body, pres->event);
if (cur_body) pkg_free(cur_body);
if (result) pa_dbf.free_result(pa_db, result);
if (pa_dbf.abort_transaction)
{
if (pa_dbf.abort_transaction(pa_db) < 0)
LM_ERR("in abort_transaction\n");
}
return ret;
}
int delete_presentity(presentity_t *pres)
{
db_key_t query_cols[4];
db_val_t query_vals[4];
int n_query_cols = 0;
if (pa_dbf.use_table(pa_db, &presentity_table) < 0)
{
LM_ERR("unsuccessful use table sql operation\n");
goto error;
}
query_cols[n_query_cols] = &str_username_col;
query_vals[n_query_cols].type = DB1_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val = pres->user;
n_query_cols++;
query_cols[n_query_cols] = &str_domain_col;
query_vals[n_query_cols].type = DB1_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val = pres->domain;
n_query_cols++;
query_cols[n_query_cols] = &str_event_col;
query_vals[n_query_cols].type = DB1_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val = pres->event->name;
n_query_cols++;
query_cols[n_query_cols] = &str_etag_col;
query_vals[n_query_cols].type = DB1_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val = pres->etag;
n_query_cols++;
if(pa_dbf.delete(pa_db, query_cols, 0, query_vals, n_query_cols) < 0)
{
LM_ERR("unsuccessful sql delete operation");
goto error;
}
if (pa_dbf.affected_rows)
return pa_dbf.affected_rows(pa_db);
else
return 0;
error:
return -1;
}
int delete_offline_presentities(str *pres_uri, pres_ev_t *event)
{
db_key_t query_cols[4];
db_val_t query_vals[4];
int n_query_cols = 0;
struct sip_uri uri;
if (parse_uri(pres_uri->s, pres_uri->len, &uri) < 0)
{
LM_ERR("failed to parse presentity uri\n");
goto error;
}
query_cols[n_query_cols] = &str_username_col;
query_vals[n_query_cols].type = DB1_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val = uri.user;
n_query_cols++;
query_cols[n_query_cols] = &str_domain_col;
query_vals[n_query_cols].type = DB1_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val = uri.host;
n_query_cols++;
query_cols[n_query_cols] = &str_event_col;
query_vals[n_query_cols].type = DB1_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val = event->name;
n_query_cols++;
query_cols[n_query_cols] = &str_etag_col;
query_vals[n_query_cols].type = DB1_STR;
query_vals[n_query_cols].nul = 0;
query_vals[n_query_cols].val.str_val = str_offline_etag_val;
n_query_cols++;
if (pa_dbf.use_table(pa_db, &presentity_table) < 0)
{
LM_ERR("unsuccessful use table sql operation\n");
goto error;
}
if (pa_dbf.delete(pa_db, query_cols, 0, query_vals, n_query_cols) < 0)
{
LM_ERR("unsuccessful sql delete operation");
goto error;
}
if (pa_dbf.affected_rows)
return pa_dbf.affected_rows(pa_db);
else
return 0;
error:
return -1;
}