kvpair_t* load_kvpairs(conflate_handle_t *handle, const char *filename) { char* errmsg = NULL; sqlite3 *db = NULL; kvpair_t* pairs = NULL; if (sqlite3_open(filename, &db) != SQLITE_OK) { goto finished; } if (sqlite3_exec(db, LOAD_KVPAIRS, &append_kvpair_from_db, &pairs, &errmsg) != SQLITE_OK) { goto finished; } finished: if (sqlite3_errcode(db) != SQLITE_OK) { CONFLATE_LOG(handle, LOG_LVL_ERROR, "DB error %d: %s", sqlite3_errcode(db), sqlite3_errmsg(db)); if (errmsg) { CONFLATE_LOG(handle, LOG_LVL_ERROR, " %s", errmsg); sqlite3_free(errmsg); } } sqlite3_close(db); return pairs; }
static enum conflate_mgmt_cb_result process_serverlist(void *opaque, conflate_handle_t *handle, const char *cmd, bool direct, kvpair_t *conf, conflate_form_result *r) { /* If we have "config_is_private" set to "yes" we should only process this if it's direct (i.e. ignore pubsub) */ if (!direct) { char *priv = conflate_get_private(handle, "config_is_private", handle->conf->save_path); if (priv && strcmp(priv, "yes") == 0) { CONFLATE_LOG(handle, INFO, "Currently using a private config, ignoring update."); return RV_OK; } free(priv); } CONFLATE_LOG(handle, INFO, "Processing a serverlist"); /* Persist the config lists */ if (!save_kvpairs(handle, conf, handle->conf->save_path)) { CONFLATE_LOG(handle, ERROR, "Can not save config to %s", handle->conf->save_path); } /* Send the config to the callback */ handle->conf->new_config(handle->conf->userdata, conf); return RV_OK; }
char *conflate_get_private(conflate_handle_t *handle, const char *k, const char *filename) { char *errmsg = NULL; sqlite3 *db = NULL; sqlite3_stmt *get = NULL; char *rv = NULL; bool done = false; int steps_run = 0, err = 0; if (sqlite3_open(filename, &db) != SQLITE_OK) { goto finished; } if (sqlite3_prepare_v2(db, SEL_PRIV, strlen(SEL_PRIV), &get, NULL) != SQLITE_OK) { goto finished; } sqlite3_bind_text(get, 1, k, strlen(k), SQLITE_TRANSIENT); while (!done) { switch(sqlite3_step(get)) { case SQLITE_BUSY: CONFLATE_LOG(handle, LOG_LVL_INFO, "DB was busy, retrying...\n"); break; case SQLITE_ROW: assert(rv == NULL); rv = safe_strdup((char*)sqlite3_column_text(get, 0)); CONFLATE_LOG(handle, LOG_LVL_DEBUG, "Retrieved value for ``%s'': ``%s''", k, rv); break; case SQLITE_DONE: done = true; break; } ++steps_run; assert(steps_run < MAX_STEPS); } finished: err = sqlite3_errcode(db); if (err != SQLITE_OK && err != SQLITE_DONE) { CONFLATE_LOG(handle, LOG_LVL_ERROR, "DB error %d: %s", sqlite3_errcode(db), sqlite3_errmsg(db)); if (errmsg) { CONFLATE_LOG(handle, LOG_LVL_ERROR, " %s", errmsg); sqlite3_free(errmsg); } } if (get) { sqlite3_finalize(get); } sqlite3_close(db); return rv; }
void* run_conflate(void *arg) { conflate_handle_t* handle = (conflate_handle_t*)arg; /* Before connecting and all that, load the stored config */ kvpair_t* conf = load_kvpairs(handle, handle->conf->save_path); if (conf) { handle->conf->new_config(handle->conf->userdata, conf); free_kvpair(conf); } xmpp_log_t strophe_logger = { &conflate_strophe_logger, handle }; /* Run forever */ for (;;) { handle->ctx = xmpp_ctx_new(NULL, &strophe_logger); assert(handle->ctx); handle->conn = xmpp_conn_new(handle->ctx); assert(handle->conn); /* Use the stored jid if there is one */ char *db_jid = conflate_get_private(handle, STORED_JID_KEY, handle->conf->save_path); if (db_jid) { CONFLATE_LOG(handle, LOG_LVL_DEBUG, "Using jid from db: %s", db_jid); xmpp_conn_set_jid(handle->conn, db_jid); free(db_jid); } else { CONFLATE_LOG(handle, LOG_LVL_DEBUG, "Using provided jid: %s", handle->conf->jid); xmpp_conn_set_jid(handle->conn, handle->conf->jid); } xmpp_conn_set_pass(handle->conn, handle->conf->pass); xmpp_connect_client(handle->conn, handle->conf->host, 0, conn_handler, handle); xmpp_run(handle->ctx); CONFLATE_LOG(handle, LOG_LVL_INFO, "xmpp_run exited"); xmpp_conn_release(handle->conn); xmpp_ctx_free(handle->ctx); sleep(5); CONFLATE_LOG(handle, LOG_LVL_INFO, "reconnecting"); } CONFLATE_LOG(handle, LOG_LVL_FATAL, "Exited an infinite loop."); return NULL; }
static void conn_handler(xmpp_conn_t * const conn, const xmpp_conn_event_t status, const int error, xmpp_stream_error_t * const stream_error, void * const userdata) { conflate_handle_t *handle = (conflate_handle_t *)userdata; if (status == XMPP_CONN_CONNECT) { xmpp_stanza_t* pres = NULL, *priority = NULL, *pri_text = NULL; CONFLATE_LOG(handle, LOG_LVL_INFO, "Connected."); xmpp_handler_add(conn, version_handler, "jabber:iq:version", "iq", NULL, handle); xmpp_handler_add(conn, command_handler, "http://jabber.org/protocol/commands", "iq", NULL, handle); xmpp_handler_add(conn, disco_items_handler, "http://jabber.org/protocol/disco#items", "iq", NULL, handle); xmpp_handler_add(conn, message_handler, NULL, "message", NULL, handle); xmpp_timed_handler_add(conn, keepalive_handler, 60000, handle); xmpp_timed_handler_add(conn, alarmqueue_handler, 10000, handle); /* Send initial <presence/> so that we appear online to contacts */ pres = xmpp_stanza_new(handle->ctx); assert(pres); xmpp_stanza_set_name(pres, "presence"); priority = xmpp_stanza_new(handle->ctx); assert(priority); xmpp_stanza_set_name(priority, "priority"); add_and_release(pres, priority); pri_text = xmpp_stanza_new(handle->ctx); assert(pri_text); xmpp_stanza_set_text(pri_text, "5"); add_and_release(priority, pri_text); xmpp_send(conn, pres); xmpp_stanza_release(pres); /* Store the bound jid */ if (!conflate_save_private(handle, STORED_JID_KEY, xmpp_conn_get_bound_jid(conn), handle->conf->save_path)) { CONFLATE_LOG(handle, LOG_LVL_WARN, "Failed to save the bound jid"); } } else { CONFLATE_LOG(handle, LOG_LVL_INFO, "disconnected."); xmpp_stop(handle->ctx); } }
static int command_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata) { xmpp_stanza_t *reply = NULL, *req = NULL; char *cmd = NULL; /* Figure out what the command is */ req = xmpp_stanza_get_child_by_name(stanza, "command"); assert(req); assert(strcmp(xmpp_stanza_get_name(req), "command") == 0); cmd = xmpp_stanza_get_attribute(req, "node"); assert(cmd); CONFLATE_LOG(((conflate_handle_t *)userdata), LOG_LVL_INFO, "Command: %s", cmd); reply = command_dispatch(conn, stanza, userdata, cmd, req, true); if (reply) { xmpp_send(conn, reply); xmpp_stanza_release(reply); } return 1; }
static xmpp_stanza_t* n_handler(const char *cmd, xmpp_stanza_t* cmd_stanza, xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata, bool direct, conflate_mgmt_cb_t cb) { conflate_handle_t *handle = (conflate_handle_t*) userdata; xmpp_ctx_t *ctx = handle->ctx; conflate_form_result result = { .conn = conn, .ctx = ctx, .reply = NULL, .cmd_res = NULL, .container = NULL }; kvpair_t *form = NULL; assert(cb); result.reply = create_reply(ctx, stanza); result.cmd_res = create_cmd_response(ctx, cmd_stanza); add_and_release(result.reply, result.cmd_res); xmpp_stanza_t *x = xmpp_stanza_get_child_by_name(cmd_stanza, "x"); if (x) { xmpp_stanza_t *fields = xmpp_stanza_get_child_by_name(x, "field"); if (fields) { form = grok_form(fields); } } enum conflate_mgmt_cb_result rv = cb(handle->conf->userdata, handle, cmd, direct, form, &result); CONFLATE_LOG(handle, LOG_LVL_DEBUG, "Result of %s: %s", cmd, cb_name(rv)); switch (rv) { case RV_ERROR: add_cmd_error(ctx, result.reply, "500", "urn:ietf:params:xml:ns:xmpp-stanzas", "internal-server-error", NULL, NULL); break; case RV_BADARG: add_cmd_error(ctx, result.reply, "400", "urn:ietf:params:xml:ns:xmpp-stanzas", "bad-request", "http://jabber.org/protocol/commands", "bad-payload"); break; case RV_OK: /* Things are good, use the built form */ break; } free_kvpair(form); return result.reply; }
static int run_mod_steps(conflate_handle_t *handle, sqlite3 *db, sqlite3_stmt *statement) { int steps_run = 0, rc = 0; while ((rc = sqlite3_step(statement)) != SQLITE_DONE) { steps_run++; assert(steps_run < MAX_STEPS); CONFLATE_LOG(handle, LOG_LVL_DEBUG, "statement step result: %d", rc); } return sqlite3_changes(db); }
bool conflate_delete_private(conflate_handle_t *handle, const char *k, const char *filename) { bool rv = false; int err = 0, deleted = 0; sqlite3 *db = NULL; sqlite3_stmt *del = NULL; if ((err = open_and_initialize_db(handle, filename, &db)) != SQLITE_OK) { goto finished; } assert(db != NULL); if ((err = sqlite3_prepare_v2(db, DEL_PRIV, strlen(DEL_PRIV), &del, NULL)) != SQLITE_OK) { goto finished; } sqlite3_bind_text(del, 1, k, strlen(k), SQLITE_TRANSIENT); deleted = run_mod_steps(handle, db, del); CONFLATE_LOG(handle, LOG_LVL_DEBUG, "Removed %d records", deleted); rv = deleted >= 0; finished: err = sqlite3_errcode(db); if (err != SQLITE_OK && err != SQLITE_DONE) { CONFLATE_LOG(handle, LOG_LVL_ERROR, "DB error %d: %s", sqlite3_errcode(db), sqlite3_errmsg(db)); } if (del) { sqlite3_finalize(del); } sqlite3_close(db); return rv; }
static bool db_do(conflate_handle_t *handle, sqlite3 *db, const char* query) { char* errmsg = NULL; bool rv = true; if (sqlite3_exec(db, query, NULL, NULL, &errmsg) != SQLITE_OK) { CONFLATE_LOG(handle, LOG_LVL_ERROR, "DB Error: %s\n%s", errmsg, query); sqlite3_free(errmsg); rv = false; } return rv; }
static bool maybe_create_table(conflate_handle_t *handle, sqlite3 *db, int flag, int flags, const char* query) { assert(db); assert(query); bool rv = true; char* table_name = get_table_name(flag); if (flag & flags) { CONFLATE_LOG(handle, LOG_LVL_DEBUG, "Table %s already exists", table_name); } else { if (db_do(handle, db, query)) { CONFLATE_LOG(handle, LOG_LVL_INFO, "Created table: %s", table_name); } else { CONFLATE_LOG(handle, LOG_LVL_WARN, "DB error creating table: %s", table_name); rv = false; } } return rv; }
static int message_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata) { xmpp_stanza_t* event = NULL, *items = NULL, *item = NULL, *command = NULL, *reply = NULL; conflate_handle_t *handle = (conflate_handle_t*)userdata; CONFLATE_LOG(handle, LOG_LVL_DEBUG, "Got a message from %s", xmpp_stanza_get_attribute(stanza, "from")); event = xmpp_stanza_get_child_by_name(stanza, "event"); assert(event); items = xmpp_stanza_get_child_by_name(event, "items"); assert(items); item = xmpp_stanza_get_child_by_name(items, "item"); if (item) { command = xmpp_stanza_get_child_by_name(item, "command"); assert(command); CONFLATE_LOG(handle, LOG_LVL_INFO, "Pubsub comand: %s", xmpp_stanza_get_attribute(command, "command")); reply = command_dispatch(conn, stanza, userdata, xmpp_stanza_get_attribute(command, "command"), command, false); if (reply) { xmpp_stanza_release(reply); } } else { CONFLATE_LOG(handle, LOG_LVL_INFO, "Received pubsub event with no items."); } return 1; }
static void conflate_strophe_logger(void *const userdata, const xmpp_log_level_t level, const char *const area, const char *const msg) { enum conflate_log_level lvl = LOG_LVL_ERROR; switch(level) { case XMPP_LEVEL_DEBUG: lvl = LOG_LVL_DEBUG; break; case XMPP_LEVEL_INFO: lvl = LOG_LVL_INFO; break; case XMPP_LEVEL_WARN: lvl = LOG_LVL_WARN; break; case XMPP_LEVEL_ERROR: lvl = LOG_LVL_ERROR; break; } conflate_handle_t *handle = (conflate_handle_t*)userdata; CONFLATE_LOG(handle, lvl, "%s", msg); }
static int open_and_initialize_db(conflate_handle_t *handle, const char *filename, sqlite3 **db) { int err = 0; if ((err = sqlite3_open(filename, &*db)) != SQLITE_OK) { return err; } if (!initialize_db(handle, *db)) { CONFLATE_LOG(handle, LOG_LVL_ERROR, "Error initializing tables"); return SQLITE_ERROR; } return SQLITE_OK; }
static int set_table_mask(void* arg, int n, char **vals, char **cols) { (void)cols; struct table_mask_userdata *udata = (struct table_mask_userdata*)arg; assert(n == 1); if (strcmp(vals[0], "keys") == 0) { udata->found |= HAS_KEYS; } else if(strcmp(vals[0], "vals") == 0) { udata->found |= HAS_VALUES; } else if(strcmp(vals[0], "private") == 0) { udata->found |= HAS_PRIVATE; } else { CONFLATE_LOG(udata->handle, LOG_LVL_WARN, "Unknown table: %s", vals[0]); } return SQLITE_OK; }
bool conflate_save_private(conflate_handle_t *handle, const char *k, const char *v, const char *filename) { bool rv = false; int err = 0; sqlite3 *db = NULL; sqlite3_stmt *ins = NULL; if ((err = open_and_initialize_db(handle, filename, &db)) != SQLITE_OK) { goto finished; } assert(db != NULL); if ((err = sqlite3_prepare_v2(db, INS_PRIV, strlen(INS_PRIV), &ins, NULL)) != SQLITE_OK) { goto finished; } sqlite3_bind_text(ins, 1, k, strlen(k), SQLITE_TRANSIENT); sqlite3_bind_text(ins, 2, v, strlen(v), SQLITE_TRANSIENT); rv = run_mod_steps(handle, db, ins) == 1; finished: err = sqlite3_errcode(db); if (err != SQLITE_OK && err != SQLITE_DONE) { CONFLATE_LOG(handle, LOG_LVL_ERROR, "DB error %d: %s", sqlite3_errcode(db), sqlite3_errmsg(db)); } if (ins) { sqlite3_finalize(ins); } sqlite3_close(db); return rv; }
static bool initialize_db(conflate_handle_t *handle, sqlite3 *db) { bool rv = true; char *errmsg = NULL; char* query = "SELECT name FROM sqlite_master " "WHERE type='table' " "ORDER BY name;"; assert(db); struct table_mask_userdata udata = { 0, handle }; if (sqlite3_exec(db, query, set_table_mask, &udata, &errmsg) != SQLITE_OK) { CONFLATE_LOG(handle, LOG_LVL_ERROR, "DB error: %s", errmsg); sqlite3_free(errmsg); } else { rv &= maybe_create_table(handle, db, HAS_KEYS, udata.found, CREATE_KEYS); rv &= maybe_create_table(handle, db, HAS_VALUES, udata.found, CREATE_VALUES); rv &= maybe_create_table(handle, db, HAS_PRIVATE, udata.found, CREATE_PRIVATE); } return rv; }
bool save_kvpairs(conflate_handle_t *handle, kvpair_t* kvpair, const char *filename) { bool rv = false; sqlite3 *db = NULL; sqlite3_stmt *ins_keys = NULL, *ins_vals = NULL; if ( open_and_initialize_db(handle, filename, &db) != SQLITE_OK) { goto finished; } assert(db != NULL); if (!db_do(handle, db, "begin transaction")) { goto finished; } /* Cleanup the existing stuff */ if (!db_do(handle, db, "delete from keys")) { goto finished; } if (!db_do(handle, db, "delete from vals")) { goto finished; } /* Add the keys */ if (sqlite3_prepare_v2(db, INS_KEYS, strlen(INS_KEYS), &ins_keys, NULL) != SQLITE_OK) { goto finished; } /* Add the values */ if (sqlite3_prepare_v2(db, INS_VALS, strlen(INS_VALS), &ins_vals, NULL) != SQLITE_OK) { goto finished; } /* OK, Add them all in */ while (kvpair) { int j = 0; sqlite_int64 key_id; sqlite3_clear_bindings(ins_keys); sqlite3_clear_bindings(ins_vals); sqlite3_bind_text(ins_keys, 1, kvpair->key, strlen(kvpair->key), SQLITE_TRANSIENT); if (run_mod_steps(handle, db, ins_keys) != 1) { goto finished; } key_id = sqlite3_last_insert_rowid(db); for (j = 0; kvpair->values[j]; j++) { char* val = kvpair->values[j]; sqlite3_bind_int64(ins_vals, 1, key_id); sqlite3_bind_text(ins_vals, 2, val, strlen(val), SQLITE_TRANSIENT); if (run_mod_steps(handle, db, ins_vals) != 1) { goto finished; } sqlite3_reset(ins_vals); } sqlite3_reset(ins_keys); kvpair = kvpair -> next; } if (!db_do(handle, db, "commit")) { goto finished; } rv = true; finished: if (sqlite3_errcode(db) != SQLITE_OK) { CONFLATE_LOG(handle, LOG_LVL_ERROR, "DB error %d: %s", sqlite3_errcode(db), sqlite3_errmsg(db)); } if (ins_keys) { sqlite3_finalize(ins_keys); } if (ins_vals) { sqlite3_finalize(ins_vals); } sqlite3_close(db); return rv; }