/* * 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; }