/* * Presence Agent, watcher structure and related functions * * $Id$ * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "paerrno.h" #include "../../lib/srdb2/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 "watcher.h" #include "presentity.h" #include "auth.h" #include "ptime.h" str watcher_status_names[] = { [WS_PENDING] = STR_STATIC_INIT("pending"), [WS_ACTIVE] = STR_STATIC_INIT("active"), [WS_REJECTED] = STR_STATIC_INIT("rejected"), [WS_TERMINATED] = STR_STATIC_INIT("terminated"), [WS_PENDING_TERMINATED] = STR_STATIC_INIT("terminated"), STR_NULL }; str watcher_event_names[] = { [WE_SUBSCRIBE] = STR_STATIC_INIT("subscribe"), [WE_APPROVED] = STR_STATIC_INIT("approved"), [WE_DEACTIVATED] = STR_STATIC_INIT("deactivated"), [WE_PROBATION] = STR_STATIC_INIT("probation"), [WE_REJECTED] = STR_STATIC_INIT("rejected"), [WE_TIMEOUT] = STR_STATIC_INIT("timeout"), [WE_GIVEUP] = STR_STATIC_INIT("giveup"), [WE_NORESOURCE] = STR_STATIC_INIT("noresource"), STR_NULL }; const char *event_package2str(int et) /* FIXME: change working with this to enum ?*/ { /* added due to incorrect package handling */ switch (et) { case EVENT_PRESENCE: return "presence"; case EVENT_PRESENCE_WINFO: return "presence.winfo"; /*case EVENT_XCAP_CHANGE: return ...; */ default: return "unknown"; } } int str2event_package(const char *epname) { /* work only with packages supported by PA! */ if (strcmp(epname, "presence") == 0) return EVENT_PRESENCE; if (strcmp(epname, "presence.winfo") == 0) return EVENT_PRESENCE_WINFO; return -1; /* unsupported */ } /*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_nocase_equals(wsname, &watcher_status_names[i]) == 0) { return i; } } return 0; } static watcher_event_t watcher_event_from_string(str *wename) { int i; for (i = 0; watcher_event_names[i].len; i++) { if (str_nocase_equals(wename, &watcher_event_names[i]) == 0) { return i; } } return 0; } /* 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; } /* * Create a new watcher structure but do not write to database */ int new_watcher_no_wb(str* _uri, time_t _e, int event_package, int doc_type, dlg_t* _dlg, str *_dn, str *server_contact, str *id, watcher_t** _w) { watcher_t* watcher; int size; dbid_t dbid; str sid; /* Check parameters */ if (!_uri && !_dlg && !_w) { LOG(L_ERR, "new_watcher(): Invalid parameter value\n"); return -1; } if (!id) { /* generate new database/watcher id (needed for winfo documents!) */ generate_dbid(dbid); sid.s = dbid_strptr(dbid); sid.len = dbid_strlen(dbid); id = &sid; } /* Allocate memory buffer for watcher_t structure and uri string */ size = sizeof(watcher_t) + id->len + _uri->len + _dn->len + server_contact->len; watcher = (watcher_t*)mem_alloc(size); if (!watcher) { paerrno = PA_NO_MEMORY; ERR("No memory left (%d bytes)\n", size); return -1; } memset(watcher, 0, sizeof(watcher_t)); /* Copy ID string */ watcher->id.s = (char*)watcher + sizeof(watcher_t); str_cpy(&watcher->id, id); /* Copy uri string */ watcher->uri.s = after_str_ptr(&watcher->id); str_cpy(&watcher->uri, _uri); /* Copy display_name string */ watcher->display_name.s = after_str_ptr(&watcher->uri); str_cpy(&watcher->display_name, _dn); /* Copy server_contact string */ watcher->server_contact.s = after_str_ptr(&watcher->display_name); str_cpy(&watcher->server_contact, server_contact); watcher->document_index = 0; watcher->event_package = event_package; watcher->expires = _e; /* Expires value */ watcher->preferred_mimetype = doc_type; /* Accepted document type */ watcher->dialog = _dlg; /* Dialog handle */ watcher->event = WE_SUBSCRIBE; watcher->status = WS_PENDING; *_w = watcher; return 0; } static int set_watcher_db_data(presentity_t *_p, watcher_t *watcher, db_key_t *cols, db_val_t *vals, int *col_cnt, str *dialog_str /* destination for dialog string -> must be freed after ! */ ) { int n_cols = 0; char *package = (char*)event_package2str(watcher->event_package); str dialog; /* serialized dialog */ str_clear(dialog_str); if (dlg_func.dlg2str(watcher->dialog, &dialog) != 0) { LOG(L_ERR, "Error while serializing dialog\n"); return -1; } cols[n_cols] = col_w_uri; vals[n_cols].type = DB_STR; vals[n_cols].nul = 0; vals[n_cols].val.str_val = watcher->uri; n_cols++; cols[n_cols] = col_package; vals[n_cols].type = DB_STR; vals[n_cols].nul = 0; vals[n_cols].val.str_val.s = package; vals[n_cols].val.str_val.len = strlen(package); n_cols++; cols[n_cols] = col_s_id; vals[n_cols].type = DB_STR; vals[n_cols].nul = 0; vals[n_cols].val.str_val = watcher->id; n_cols++; cols[n_cols] = col_status; vals[n_cols].type = DB_STR; vals[n_cols].nul = 0; vals[n_cols].val.str_val = watcher_status_names[watcher->status]; n_cols++; cols[n_cols] = col_event; vals[n_cols].type = DB_STR; vals[n_cols].nul = 0; vals[n_cols].val.str_val = watcher_event_names[watcher->event]; n_cols++; cols[n_cols] = col_display_name; vals[n_cols].type = DB_STR; vals[n_cols].nul = 0; vals[n_cols].val.str_val.s = watcher->display_name.s; vals[n_cols].val.str_val.len = watcher->display_name.len; n_cols++; cols[n_cols] = col_accepts; vals[n_cols].type = DB_INT; vals[n_cols].nul = 0; vals[n_cols].val.int_val = watcher->preferred_mimetype; n_cols++; cols[n_cols] = col_expires; vals[n_cols].type = DB_DATETIME; vals[n_cols].nul = 0; vals[n_cols].val.time_val = watcher->expires; n_cols++; cols[n_cols] = col_dialog; vals[n_cols].type = DB_BLOB; vals[n_cols].nul = 0; vals[n_cols].val.blob_val = dialog; n_cols++; cols[n_cols] = col_server_contact; vals[n_cols].type = DB_STR; vals[n_cols].nul = 0; vals[n_cols].val.str_val = watcher->server_contact; n_cols++; cols[n_cols] = col_pres_id; vals[n_cols].type = DB_STR; vals[n_cols].nul = 0; vals[n_cols].val.str_val = _p->pres_id; n_cols++; cols[n_cols] = col_doc_index; vals[n_cols].type = DB_INT; vals[n_cols].nul = 0; vals[n_cols].val.int_val = watcher->document_index; n_cols++; *col_cnt = n_cols; if (dialog_str) *dialog_str = dialog; return 0; } static int db_add_watcher(presentity_t *_p, watcher_t *watcher) { str_t tmp; db_key_t query_cols[20]; db_val_t query_vals[20]; int n_query_cols = 0; if (!use_db) return 0; str_clear(&tmp); if (pa_dbf.use_table(pa_db, watcherinfo_table) < 0) { LOG(L_ERR, "db_add_watcher: Error in use_table\n"); return -1; } if (set_watcher_db_data(_p, watcher, query_cols, query_vals, &n_query_cols, &tmp) != 0) { return -1; } /* insert new record into database */ if (pa_dbf.insert(pa_db, query_cols, query_vals, n_query_cols) < 0) { LOG(L_ERR, "db_add_watcher: Error while inserting watcher\n"); str_free_content(&tmp); return -1; } str_free_content(&tmp); return 0; } int db_update_watcher(presentity_t *_p, watcher_t *watcher) { str tmp; db_key_t query_cols[20]; db_val_t query_vals[20]; int n_query_cols = 0; db_key_t keys[] = { col_s_id }; db_op_t ops[] = { OP_EQ }; db_val_t k_vals[] = { { DB_STR, 0, { .str_val = watcher->id } } }; if (!use_db) return 0; str_clear(&tmp); if (pa_dbf.use_table(pa_db, watcherinfo_table) < 0) { LOG(L_ERR, "db_update_watcher: Error in use_table\n"); return -1; } if (set_watcher_db_data(_p, watcher, query_cols, query_vals, &n_query_cols, &tmp) != 0) { return -1; } if (pa_dbf.update(pa_db, keys, ops, k_vals, query_cols, query_vals, 1, n_query_cols) < 0) { LOG(L_ERR, "Error while updating watcher in DB\n"); str_free_content(&tmp); return -1; } str_free_content(&tmp); return 0; } static inline int db_remove_watcher(struct presentity *_p, watcher_t *w) { db_key_t keys[] = { col_s_id }; db_op_t ops[] = { OP_EQ }; db_val_t k_vals[] = { { DB_STR, 0, { .str_val = w->id } } }; if (!use_db) return 0; if (pa_dbf.use_table(pa_db, watcherinfo_table) < 0) { ERR("Error in use_table\n"); return -1; } if (pa_dbf.delete(pa_db, keys, ops, k_vals, 1) < 0) { ERR("Error while deleting watcher from DB\n"); return -1; } return 0; } /* * Read watcherinfo table from database for presentity _p */ int db_read_watcherinfo(presentity_t *_p, db_con_t* db) { db_key_t query_cols[5]; db_op_t query_ops[5]; db_val_t query_vals[5]; str dialog = STR_NULL; dlg_t *dlg = NULL; db_key_t result_cols[11]; db_res_t *res; int r = 0; 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, dialog_col, server_contact_col, doc_index_col; if (!use_db) return 0; /* LOG(L_ERR, "db_read_watcherinfo starting\n");*/ query_cols[0] = col_pres_id; query_ops[0] = OP_EQ; query_vals[0].type = DB_STR; query_vals[0].nul = 0; query_vals[0].val.str_val = _p->pres_id; result_cols[w_uri_col = n_result_cols++] = col_w_uri; result_cols[s_id_col = n_result_cols++] = col_s_id; result_cols[event_package_col = n_result_cols++] = col_package; result_cols[status_col = n_result_cols++] = col_status; result_cols[display_name_col = n_result_cols++] = col_display_name; result_cols[accepts_col = n_result_cols++] = col_accepts; result_cols[expires_col = n_result_cols++] = col_expires; result_cols[watcher_event_col = n_result_cols++] = col_event; result_cols[dialog_col = n_result_cols++] = col_dialog; result_cols[server_contact_col = n_result_cols++] = col_server_contact; result_cols[doc_index_col = n_result_cols++] = col_doc_index; if (pa_dbf.use_table(db, watcherinfo_table) < 0) { ERR("Error in use_table\n"); return -1; } if (pa_dbf.query (db, query_cols, query_ops, query_vals, result_cols, n_query_cols, n_result_cols, 0, &res) < 0) { ERR("Error while querying watcherinfo\n"); return -1; } if (res && (res->n > 0)) { /* fill in tuple structure from database query result */ int i; dlg = NULL; 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 = STR_NULL; str s_id = STR_NULL; char *event_package_str = NULL; int event_package = EVENT_PRESENCE; str watcher_event_str = STR_NULL; watcher_event_t watcher_event = WE_SUBSCRIBE; int accepts = row_vals[accepts_col].val.int_val; time_t expires = row_vals[expires_col].val.time_val; int doc_index = row_vals[doc_index_col].val.int_val; str status = STR_NULL; str display_name = STR_NULL; str server_contact = STR_NULL; 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 = (char *)row_vals[event_package_col].val.string_val; event_package = str2event_package(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 (!row_vals[dialog_col].nul) { dialog = row_vals[dialog_col].val.blob_val; dlg = (dlg_t*)mem_alloc(sizeof(*dlg)); if (!dlg) { LOG(L_ERR, "db_read_watcher: Can't allocate dialog\n"); r = -1; } else if (dlg_func.str2dlg(&dialog, dlg) != 0) { LOG(L_ERR, "db_read_watcher: Error while deserializing dialog\n"); r = -1; } } if (!row_vals[server_contact_col].nul) { server_contact.s = (char *)row_vals[server_contact_col].val.string_val; server_contact.len = strlen(server_contact.s); } DBG("creating watcher\n"); if (new_watcher_no_wb(&w_uri, expires, event_package, accepts, dlg, &display_name, &server_contact, &s_id, &watcher) == 0) { watcher->status = watcher_status_from_string(&status); watcher->event = watcher_event; watcher->document_index = doc_index; r = append_watcher(_p, watcher, 0); if (r < 0) { ERR("Error while adding watcher\n"); free_watcher(watcher); } } else r = -1; } } pa_dbf.free_result(db, res); return r; } /* * Release a watcher structure */ void free_watcher(watcher_t* _w) { tmb.free_dlg(_w->dialog); mem_free(_w); } /* * Update a watcher structure */ int update_watcher(struct presentity *p, watcher_t* _w, time_t _e, struct sip_msg *m) { watcher_status_t old = _w->status; /* old status of subscription */ tmb.dlg_request_uas(_w->dialog, m, IS_TARGET_REFRESH); _w->expires = _e; _w->flags |= WFLAG_SUBSCRIPTION_CHANGED; /* actualize watcher's status according to time */ if (_w->expires <= act_time) { /* ERR("Updated watcher to expire: %.*s\n", _w->uri.len, _w->uri.s); */ _w->expires = 0; set_watcher_terminated_status(_w); } if ((old != _w->status) && (_w->event_package == EVENT_PRESENCE)) { /* changed only when presence watcher changes status */ /* FIXME: it could be changed when expires time changes too, * but we don't send expiration in watcherinf notify thus * it is worthless */ p->flags |= PFLAG_WATCHERINFO_CHANGED; } if (use_db) return db_update_watcher(p, _w); else return 0; } int is_watcher_terminated(watcher_t *w) { if (!w) return -1; if ((w->status == WS_TERMINATED) || (w->status == WS_REJECTED) || (w->status == WS_PENDING_TERMINATED)) return 1; return 0; } int is_watcher_authorized(watcher_t *w) { if (!w) return 0; switch (w->status) { case WS_PENDING: ; case WS_PENDING_TERMINATED: ; case WS_REJECTED: return 0; case WS_ACTIVE: ; case WS_TERMINATED: return 1; } return 0; } void set_watcher_terminated_status(watcher_t *w) { if (!w) return; switch (w->status) { case WS_REJECTED: break; case WS_PENDING: ; w->status = WS_PENDING_TERMINATED; break; case WS_PENDING_TERMINATED: break; case WS_TERMINATED: break; case WS_ACTIVE: ; w->status = WS_TERMINATED; break; } } int append_watcher(presentity_t *_p, watcher_t *_w, int add_to_db) { if (add_to_db && use_db) { if (db_add_watcher(_p, _w) != 0) { ERR("Error while adding watcher\n"); paerrno = PA_INTERNAL_ERROR; return -5; } } if (_w->event_package == EVENT_PRESENCE_WINFO) { DOUBLE_LINKED_LIST_ADD(_p->first_winfo_watcher, _p->last_winfo_watcher, _w); } else { DOUBLE_LINKED_LIST_ADD(_p->first_watcher, _p->last_watcher, _w); /* changed only when presence watcher added */ _p->flags |= PFLAG_WATCHERINFO_CHANGED; DEBUG("setting PFLAG_WATCHERINFO_CHANGED\n"); } return 0; } void remove_watcher(presentity_t* _p, watcher_t *w) { if (!w) return; if (use_db) db_remove_watcher(_p, w); if (w->event_package == EVENT_PRESENCE_WINFO) DOUBLE_LINKED_LIST_REMOVE(_p->first_winfo_watcher, _p->last_winfo_watcher, w); else { DOUBLE_LINKED_LIST_REMOVE(_p->first_watcher, _p->last_watcher, w); _p->flags |= PFLAG_WATCHERINFO_CHANGED; } } /* returns 0 if equal dialog IDs */ static int cmp_dlg_ids(dlg_id_t *a, dlg_id_t *b) { if (!a) { if (!b) return -1; else return 0; } if (!b) return 1; if (str_case_equals(&a->call_id, &b->call_id) != 0) return 1; if (str_case_equals(&a->rem_tag, &b->rem_tag) != 0) return 1; /* case sensitive ? */ if (str_case_equals(&a->loc_tag, &b->loc_tag) != 0) return 1; /* case sensitive ? */ return 0; } /* Find a watcher in the list via dialog identifier */ int find_watcher_dlg(struct presentity* _p, dlg_id_t *dlg_id, int _et, watcher_t** _w) { watcher_t* watcher; /* first look for watchers */ if (!dlg_id) return -1; if (_et != EVENT_PRESENCE_WINFO) watcher = _p->first_watcher; else watcher = _p->first_winfo_watcher; while(watcher) { if (watcher->dialog) { if ((cmp_dlg_ids(&watcher->dialog->id, dlg_id) == 0) && (watcher->event_package == _et)) { *_w = watcher; return 0; } } watcher = watcher->next; } return 1; }