obsolete/pa/offline_winfo.c
f2ee1923
 /* offline presence authorization "requests" e.g. storing
  * watcher information for offline presentities */
 
 #include <stdio.h>
 #include "../../str.h"
 #include "../../dprint.h"
 #include "../../mem/mem.h"
 #include "dlist.h"
 #include "presentity.h"
 #include "watcher.h"
 #include "pdomain.h"
 #include "pa_mod.h"
 #include "../../parser/parse_from.h"
 
 #include <libxml/parser.h>
 #include <libxml/xpath.h>
 
 #include <presence/pidf.h>
 #include <cds/logger.h>
 #include <time.h>
 #include "offline_winfo.h"
 
 #include "winfo_doc.h"
bc9193a5
 #include "message.h"
f2ee1923
 
 /* ----- Helper and internal functions ----- */
 
 #define add_str_len(len,p)	if (p) if (p->s) len += p->len
 #define set_member_str(buf,len,dst,src)	if (src) if (src->s) { \
 			memcpy(buf + len, src->s, src->len); \
 			dst.s = buf + len; \
 			dst.len = src->len; \
 			len += src->len; \
 		}
 
 #define string_val(v,s)	(v).type = DB_STR; \
 	(v).val.str_val=s; \
 	(v).nul=(s.len == 0); 
 
 #define time_val(v,t)	(v).type = DB_DATETIME; \
 	(v).val.time_val=t;\
 	(v).nul=0; 
 
 #define get_str_val(rvi,dst)	do{if(!rvi.nul){dst.s=(char*)rvi.val.string_val;dst.len=strlen(dst.s);}}while(0)
 #define get_blob_val(rvi,dst)	do{if(!rvi.nul){dst=rvi.val.blob_val;}else dst.len=0;}while(0)
 #define get_time_val(rvi,dst)	do{if(!rvi.nul){dst=rvi.val.time_val;}}while(0)
 #define get_int_val(rvi,dst)	do{if(!rvi.nul){dst=rvi.val.int_val;}else dst=0;}while(0)
 
 /* expiration time in secs */
 int offline_winfo_expiration = 259200;
 
626094a5
 /* status of last subscription */
 static watcher_status_t last_subscription_status = WS_PENDING; 
 
 void set_last_subscription_status(watcher_status_t status)
 {
 	last_subscription_status = status;
 }
 
 watcher_status_t get_last_subscription_status()
 {
 	return last_subscription_status;
 }
 
f2ee1923
 static offline_winfo_t *create_winfo(str *uid, 
 		str *wuri, 
 		str *events, 
2df90abe
 		str *domain,
 		str *status)
f2ee1923
 {
 	int len = 0;
 	offline_winfo_t *info;
 	
 	add_str_len(len, uid);
 	add_str_len(len, wuri);
 	add_str_len(len, events);
 	add_str_len(len, domain);
2df90abe
 	add_str_len(len, status);
f2ee1923
 	len += sizeof(offline_winfo_t);
 	
2a385f88
 	info = (offline_winfo_t*)mem_alloc(len);
f2ee1923
 	if (info) {
 		memset(info, 0, len);
 		len = 0;
 		set_member_str(info->buffer, len, info->uid, uid);
 		set_member_str(info->buffer, len, info->watcher, wuri);
 		set_member_str(info->buffer, len, info->events, events);
 		set_member_str(info->buffer, len, info->domain, domain);
2df90abe
 		set_member_str(info->buffer, len, info->status, status);
f2ee1923
 		info->created = time(NULL);
 		info->expires = info->created + offline_winfo_expiration;
 		info->index = -1;
 	}
 
 	return info;
 }
 
 static inline void free_winfo(offline_winfo_t *info)
 {
2a385f88
 	if (info) mem_free(info);
f2ee1923
 }
 
 static inline void free_winfos(offline_winfo_t *info)
 {
 	offline_winfo_t *n;
 	
 	while (info) {
 		n = info->next;
 		free_winfo(info);
 		info = n;
 	}
 }
 
 static int db_store_winfo(offline_winfo_t *info)
 {
 	/* ignore duplicit records (should be stored only once!) */
 	db_key_t cols[20];
 	db_val_t vals[20];
 	int n = -1;
 	
 	if (!pa_db) {
 		ERR("database not initialized: set parameter \'use_offline_winfo\' to 1\n");
 		return -1;
 	}
 	
 	if (pa_dbf.use_table(pa_db, offline_winfo_table) < 0) {
 		LOG(L_ERR, "db_add_watcher: Error in use_table\n");
 		return -1;
 	}
 	
6a611e22
 	cols[++n] = col_uid;
f2ee1923
 	string_val(vals[n], info->uid);
 	
6a611e22
 	cols[++n] = col_watcher;
f2ee1923
 	string_val(vals[n], info->watcher);
 	
6a611e22
 	cols[++n] = col_events;
f2ee1923
 	string_val(vals[n], info->events);
 	
6a611e22
 	cols[++n] = col_domain;
f2ee1923
 	string_val(vals[n], info->domain);
 	
6a611e22
 	cols[++n] = col_status;
2df90abe
 	string_val(vals[n], info->status);
 	
6a611e22
 	cols[++n] = col_created_on;
f2ee1923
 	time_val(vals[n], info->created);
 	
6a611e22
 	cols[++n] = col_expires_on;
f2ee1923
 	time_val(vals[n], info->expires);
 
 	/* index ("dbid") is created automaticaly ! */
 	
 	/* insert new record into database */
 	if (pa_dbf.insert(pa_db, cols, vals, n + 1) < 0) {
 		return -1;
 	}
 
 	return 0;
 }
 
 static int get_watcher_uri(struct sip_msg* _m, str* uri)
 {
 	struct sip_uri puri;
 	int res = 0;
 	
 	uri->s = get_from(_m)->uri.s;
 	uri->len = get_from(_m)->uri.len;
 
 	if (parse_uri(uri->s, uri->len, &puri) < 0) {
 		LOG(L_ERR, "Error while parsing URI\n");
 		return -1;
 	}
2df90abe
 #if 0	
f2ee1923
 	uri->s = puri.user.s;
 	if ((!uri->s) || (puri.user.len < 1)) {
 		uri->s = puri.host.s;
 		uri->len = puri.host.len;
 		res = 1; /* it is uri without username ! */
 	}
2df90abe
 #endif
f2ee1923
 	uri->len = puri.host.s + puri.host.len - uri->s;
 	return res;
 }
 
 static int get_events(struct sip_msg* _m, str* events)
 {
 	char *c;
 	str_clear(events);
 	
 	if (parse_headers(_m, HDR_EVENT_F, 0) == -1) {
 		ERR("Error while parsing headers\n");
 		return -1;
 	}
 	if (_m->event) {
 		/* parse_event(_m->event); */
 		*events = _m->event->body;
 		c = str_strchr(events, ';');
 		if (c) events->len = c - events->s;
 	}
 
 	return 0;
 }
 
 int remove_expired_winfos()
 {
6a611e22
 	db_key_t keys[] = { col_expires_on };
f2ee1923
 	db_val_t vals[1] = { 
 		{ DB_DATETIME, 0, { .time_val = time(NULL) } }
 	};
 	db_op_t ops[] = { OP_LEQ };
 	int res = 0;
 
 	if (!pa_db) {
 		ERR("database not initialized: set parameter \'use_offline_winfo\' to 1\n");
 		return -1;
 	}
 	
 	if (pa_dbf.use_table(pa_db, offline_winfo_table) < 0) {
 		LOG(L_ERR, "db_add_watcher: Error in use_table\n");
 		return -1;
 	}
 
 	res = pa_dbf.delete(pa_db, keys, ops, vals, 1);
 	if (res < 0 )
 		DBG("ERROR cleaning expired offline winfo\n");
 	return res;
 }
 
 int db_remove_winfos(offline_winfo_t *info)
 {
6a611e22
 	db_key_t keys[] = { col_dbid };
f2ee1923
 	db_val_t vals[1];
e21f87f6
 	db_op_t ops[] = { OP_EQ };
f2ee1923
 	int res = 0;
 
 	if (!pa_db) {
 		ERR("database not initialized: set parameter \'use_offline_winfo\' to 1\n");
 		return -1;
 	}
 	
 	if (pa_dbf.use_table(pa_db, offline_winfo_table) < 0) {
 		LOG(L_ERR, "Error in use_table\n");
 		return -1;
 	}
 
 	while (info) {
 		vals[0].type = DB_INT;
 		vals[0].nul = 0;
 		vals[0].val.int_val = info->index;
 
 		res = pa_dbf.delete(pa_db, keys, ops, vals, 1);
 		if (res < 0 )
 			DBG("ERROR cleaning expired offline winfo\n");
 
 		info = info->next;
 	}
 	return res;
 }
 
 static void send_winfo_cb(struct cell* t, int type, struct tmcb_params* params)
 {
 	offline_winfo_t *info = NULL;
 
 	if (!params) {
 		ERR("BUG: empty arg\n");
 		return;
 	}
 
 	if (params->param) info = (offline_winfo_t *)*(params->param);
 	if (!info) {
 		ERR("BUG: empty arg\n");
 		return;
 	}
 
 	if ((params->code >= 200) && (params->code < 300)) {
 		/* delete infos from DB */
 		db_remove_winfos(info);
 	}
 	else 
 		ERR("%d response on winfo NOTIFY\n", params->code);
 
 	
 	/* delete infos from memory */
 	free_winfos(info);
 }
 
 static int send_winfo(presentity_t *p, offline_winfo_t *info)
 {
 	watcher_t *w;
 	
 	if (!p) {
 		ERR("BUG: trying to send offline winfo to empty presentity\n");
 		return -1;
 	}
 	
5d58dfb2
 	w = p->first_winfo_watcher;
f2ee1923
 	while (w) {
 		if (w->status == WS_ACTIVE) {
 			if (send_winfo_notify_offline(p, w, info, send_winfo_cb, info) == 0) 
 				return 0;
 		}
 		w = w->next;
 	}
 	
 	return -1; /* impossible to send it */
 }
 
 int db_load_winfo(str *uid, str *events, str *domain, offline_winfo_t **infos)
 {
 	int i, r = 0;
 	db_res_t *res = NULL;
 	db_key_t result_cols[] = { 
6a611e22
 		col_watcher, col_created_on, col_expires_on, col_dbid, col_status
f2ee1923
 	};
6a611e22
 	db_key_t keys[] = { col_uid, col_events };
f2ee1923
 	db_op_t ops[] = { OP_EQ, OP_EQ };
 	db_val_t k_vals[] = { 
 		{ DB_STR, 0, { .str_val = *uid } }
 	};
 	offline_winfo_t *info = NULL;
 	offline_winfo_t *last = NULL;
 
 	*infos = NULL;
 	if (pa_dbf.use_table(pa_db, offline_winfo_table) < 0) {
 		ERR("Error in use_table\n");
 		return -1;
 	}
 
 	if (pa_dbf.query (pa_db, keys, ops, k_vals,
 			result_cols, 1, sizeof(result_cols) / sizeof(db_key_t), 
 			0, &res) < 0) {
 		ERR("Error while querying stored winfos\n");
 		r = -1;
 		res = NULL;
 	}
 	if (res) {
 		for (i = 0; i < res->n; i++) {
 			db_row_t *row = &res->rows[i];
 			db_val_t *row_vals = ROW_VALUES(row);
 			str watcher = STR_NULL;
2df90abe
 			str status = STR_NULL;
f2ee1923
 			time_t created_on = 0;
 			time_t expires_on = 0;
 			int index = 0;
 
 			get_str_val(row_vals[0], watcher);
 			get_time_val(row_vals[1], created_on);
 			get_time_val(row_vals[2], expires_on);
 			get_int_val(row_vals[3], index);
2df90abe
 			get_str_val(row_vals[4], status);
f2ee1923
 			
2df90abe
 			info = create_winfo(uid, &watcher, events, domain, &status);
f2ee1923
 			if (!info) { 
 				r = -1; 
 				break; 
 			}
 			info->created = created_on;
 			info->expires = expires_on;
 			info->index = index;
 			
 			if (last) last->next = info;
 			else *infos = info;
 			last = info;
 		}
 
 		pa_dbf.free_result(pa_db, res);
 	}
 	if ((*infos) && (r != 0)) {
 		free_winfos(*infos);
 		*infos = NULL;
 	}
 	
 	return r;
 }
 
 /* ----- Handler functions ----- */
626094a5
 #if 0
 /* not used due to problems with lost AVP after sending NOTIFY */
2df90abe
 static int get_status(str *dst)
 {
 	avp_t *avp;
 	int_str name, val;
 	struct search_state s;
 	str avp_subscription_status = STR_STATIC_INIT("subscription_status");
 
 	/* if (!dst) return -1; */
 	
 	name.s = avp_subscription_status;
626094a5
 	avp = search_first_avp(AVP_CLASS_USER | 
 			AVP_TRACK_FROM | AVP_NAME_STR | AVP_VAL_STR, name, &val, 0);
2df90abe
 	if (avp) {
 		/* don't use default - use value from AVP */
626094a5
 		TRACE("subscription status = %.*s\n", FMT_STR(val.s));
2df90abe
 		*dst = val.s;
 		return 0;
 	} 
 	else {
 		/* leave default value!! */
 		/* TRACE("left default subscription status\n"); */
626094a5
 		TRACE("subscription status AVP not found\n");
2df90abe
 	}
 	
 	return 1;
 }
626094a5
 #endif
 
 static void get_status_str(str *dst)
 {
 	str s;
 	
 	switch(get_last_subscription_status()) {
2e472c6b
 		case WS_ACTIVE:
626094a5
 			s = watcher_status_names[WS_ACTIVE];
 			break;
 		case WS_REJECTED:
 		case WS_PENDING_TERMINATED:
 		case WS_TERMINATED:
 			s = watcher_status_names[WS_TERMINATED];
 			break;
 		case WS_PENDING: 
 			s = watcher_status_names[WS_PENDING];
 			break;
6a611e22
 		default: str_clear(&s); /* throw out gcc complains */
626094a5
 	}
 	if (dst) *dst = s;
 }
2df90abe
 
f2ee1923
 int store_offline_winfo(struct sip_msg* _m, char* _domain, char* _table)
 {
 	str uid = STR_NULL;
 	str wuri = STR_NULL;
 	str events = STR_NULL;
 	str domain = STR_NULL;
2df90abe
 	str status = STR_NULL;
f2ee1923
 	int res = -1;
 	offline_winfo_t *info;
 
5d58dfb2
 	if (get_presentity_uid(&uid, _m) < 0) {
f2ee1923
 		ERR("Error while extracting presentity UID\n");
 		return 0; /* ??? impossible to return -1 or 1 */
 	}
 	get_watcher_uri(_m, &wuri);
 	get_events(_m, &events);
 	if (_domain) {
 		domain.s = _domain;
 		domain.len = strlen(_domain);
 	}
2df90abe
 
626094a5
 	/* get stored subscription status value */
 	get_status_str(&status);
2df90abe
 	/* TRACE("subscription status is: %.*s\n", FMT_STR(status)); */
 
 	info = create_winfo(&uid, &wuri, &events, &domain, &status);
f2ee1923
 	/* store it into database or use internal data structures too? */
 	/* better to use only database because of lower memory usage - this 
 	 * information could be stored for very long time ! */
 	
 	db_store_winfo(info);
 	
 	free_winfo(info); /* don't hold this information in memory ! */
 
 	return res;
 }
 
 /* send offline winfo as regular watcher info NOTIFY if possible 
  * (for presentity got from get_to) */
 int dump_offline_winfo(struct sip_msg* _m, char* _domain, char* _events)
 {
 	struct pdomain* d;
 	struct presentity *p;
 	str uid = STR_NULL;
 	int res = -1;
 	str events;
 	offline_winfo_t *info;
 
 	d = (struct pdomain*)_domain;
 
5d58dfb2
 	if (get_presentity_uid(&uid, _m) < 0) {
f2ee1923
 		ERR("Error while extracting presentity UID\n");
 		return -1;
 	}
 	
 	if (_events) {
 		events.s = _events;
 		events.len = strlen(_events);
 	}
 
 	if (db_load_winfo(&uid, &events, d->name, &info) != 0) {
 		return -1;
 	}
 
 	if (!info) {
 		return 1; /* nothing to do */
 	}
 
 	lock_pdomain(d);
 
 	if (find_presentity_uid(d, &uid, &p) == 0) {
 		/* presentity found */
 		if (send_winfo(p, info) == 0) res = 1;
 		else res = -1;
 	}
 
 	unlock_pdomain(d);
 
 	return res;
 }
 
 void offline_winfo_timer(unsigned int ticks, void* param)
 {
 	remove_expired_winfos();
 }
 
626094a5
 int check_subscription_status(struct sip_msg* _m, char* _status, char* _x)
 {
 	watcher_status_t status = (watcher_status_t)_status;
 	if (status == get_last_subscription_status()) return 1;
 	else return -1;
 }
 
 /* convert char* parameter to watcher_status_t */
 int check_subscription_status_fix(void **param, int param_no)
 {
 	watcher_status_t status = WS_PENDING;
 	char *s;
 	str ss;
 
 	if (param_no == 1) {
 		s = (char*)*param;
 		if (!s) {
 			ERR("status not given!\n");
 			return -1;
 		}
 		
3f4b99d7
 		/* TRACE("status name is %s\n", (char*)*param); */
626094a5
 		
 		ss.s = s;
 		ss.len = strlen(s);
 		
 		status = watcher_status_from_string(&ss);
 		*param = (void*)status;
 	}
 	return 0;
 }