"user INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,"
"language INTEGER NOT NULL REFERENCES languages(id) ON DELETE CASCADE,"
"value INTEGER,"
+ "modified INTEGER,"
"CONSTRAINT tid UNIQUE (token, user, language) ON CONFLICT REPLACE"
");"
"CREATE UNIQUE INDEX IF NOT EXISTS un ON users(name);"
RSPAMD_STAT_BACKEND_INC_LEARNS,
RSPAMD_STAT_BACKEND_DEC_LEARNS,
RSPAMD_STAT_BACKEND_GET_LEARNS,
+ RSPAMD_STAT_BACKEND_GET_LANGUAGE,
+ RSPAMD_STAT_BACKEND_GET_USER,
+ RSPAMD_STAT_BACKEND_INSERT_LANGUAGE,
+ RSPAMD_STAT_BACKEND_INSERT_USER,
RSPAMD_STAT_BACKEND_MAX
};
},
{
.idx = RSPAMD_STAT_BACKEND_SET_TOKEN,
- .sql = "INSERT OR REPLACE INTO tokens (token, user, language, value) "
- "VALUES (?1, ?2, ?3, ?4);",
+ .sql = "INSERT OR REPLACE INTO tokens (token, user, language, value, modified) "
+ "VALUES (?1, ?2, ?3, ?4, strftime('%s','now'));",
.stmt = NULL,
.args = "IIII",
.result = SQLITE_DONE,
.args = "",
.result = SQLITE_ROW,
.ret = "I"
+ },
+ {
+ .idx = RSPAMD_STAT_BACKEND_GET_LANGUAGE,
+ .sql = "SELECT id FROM languages WHERE name=?1;",
+ .stmt = NULL,
+ .args = "T",
+ .result = SQLITE_ROW,
+ .ret = "I"
+ },
+ {
+ .idx = RSPAMD_STAT_BACKEND_GET_USER,
+ .sql = "SELECT id FROM users WHERE name=?1;",
+ .stmt = NULL,
+ .args = "T",
+ .result = SQLITE_ROW,
+ .ret = "I"
+ },
+ {
+ .idx = RSPAMD_STAT_BACKEND_INSERT_USER,
+ .sql = "INSERT INTO users (name, learns) VALUES (?1, 0);",
+ .stmt = NULL,
+ .args = "T",
+ .result = SQLITE_ROW,
+ .ret = "L"
+ },
+ {
+ .idx = RSPAMD_STAT_BACKEND_INSERT_LANGUAGE,
+ .sql = "INSERT INTO languages (name, learns) VALUES (?1, 0);",
+ .stmt = NULL,
+ .args = "T",
+ .result = SQLITE_ROW,
+ .ret = "L"
}
};
return g_quark_from_static_string ("sqlite3-stat-backend");
}
+static gint64
+rspamd_sqlite3_get_user (struct rspamd_stat_sqlite3_db *db,
+ struct rspamd_task *task, gboolean learn)
+{
+ gint64 id = 0; /* Default user is 0 */
+ gint rc;
+ const gchar *user = NULL;
+ const InternetAddress *ia;
+
+ if (task->deliver_to != NULL) {
+ /* Use deliver-to value if presented */
+ user = task->deliver_to;
+ }
+ if (task->user != NULL) {
+ /* Use user value if presented */
+ user = task->user;
+ }
+ else if (task->rcpt_envelope != NULL) {
+ /* Check envelope recipients */
+ if (internet_address_list_length (task->rcpt_envelope) == 1) {
+ /* XXX: we support now merely single recipient statistics */
+ ia = internet_address_list_get_address (task->rcpt_envelope, 0);
+
+ if (ia != NULL) {
+ user = internet_address_mailbox_get_addr (INTERNET_ADDRESS_MAILBOX (ia));
+ }
+ }
+ }
+
+ /* XXX: We ignore now mime recipients as they could be easily forged */
+ if (user != NULL) {
+ rc = rspamd_sqlite3_run_prstmt (db->sqlite, db->prstmt,
+ RSPAMD_STAT_BACKEND_GET_USER, user, &id);
+
+ if (rc != SQLITE_OK && learn) {
+ /* We need to insert a new user */
+ if (!db->in_transaction) {
+ rspamd_sqlite3_run_prstmt (db->sqlite, db->prstmt,
+ RSPAMD_STAT_BACKEND_TRANSACTION_START_IM);
+ db->in_transaction = TRUE;
+ }
+
+ rc = rspamd_sqlite3_run_prstmt (db->sqlite, db->prstmt,
+ RSPAMD_STAT_BACKEND_INSERT_USER, user, &id);
+ }
+ }
+
+ return id;
+}
+
static struct rspamd_stat_sqlite3_db *
rspamd_sqlite3_opendb (const gchar *path, const ucl_object_t *opts,
gboolean create, GError **err)
rt->ctx = ctx;
rt->db = bk;
rt->task = task;
+ rt->user_id = -1;
}
return rt;
bk->in_transaction = TRUE;
}
+ if (rt->user_id == -1) {
+ rt->user_id = rspamd_sqlite3_get_user (bk, task, FALSE);
+ }
+
memcpy (&idx, tok->data, sizeof (idx));
/* TODO: language and user support */
bk->in_transaction = TRUE;
}
+ if (rt->user_id == -1) {
+ rt->user_id = rspamd_sqlite3_get_user (bk, task, TRUE);
+ }
+
iv = res->value;
memcpy (&idx, tok->data, sizeof (idx));