Page MenuHomePhorge

No OneTemporary

Authored By
Unknown
Size
64 KB
Referenced Files
None
Subscribers
None
diff --git a/imap/dav_db.c b/imap/dav_db.c
index be5327592..10ce2e185 100644
--- a/imap/dav_db.c
+++ b/imap/dav_db.c
@@ -1,491 +1,590 @@
/* dav_db.c -- implementation of per-user DAV database
*
* Copyright (c) 1994-2012 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any legal
* details, please contact
* Carnegie Mellon University
* Center for Technology Transfer and Enterprise Creation
* 4615 Forbes Avenue
* Suite 302
* Pittsburgh, PA 15213
* (412) 268-7393, fax: (412) 268-7395
* innovation@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include <config.h>
#include <stdlib.h>
#include <syslog.h>
#include <string.h>
#include <errno.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "assert.h"
#include "caldav_alarm.h"
#include "cyrusdb.h"
#include "dav_db.h"
#include "global.h"
#include "sieve_db.h"
#include "user.h"
#include "util.h"
#include "xmalloc.h"
/* generated headers are not necessarily in current directory */
#include "imap/imap_err.h"
#define CMD_CREATE_CAL \
"CREATE TABLE IF NOT EXISTS ical_objs (" \
" rowid INTEGER PRIMARY KEY," \
" creationdate INTEGER," \
" mailbox TEXT NOT NULL," \
" resource TEXT NOT NULL," \
" imap_uid INTEGER," \
" modseq INTEGER," \
" createdmodseq INTEGER," \
" lock_token TEXT," \
" lock_owner TEXT," \
" lock_ownerid TEXT," \
" lock_expire INTEGER," \
" comp_type INTEGER," \
" ical_uid TEXT," \
" organizer TEXT," \
" dtstart TEXT," \
" dtend TEXT," \
" comp_flags INTEGER," \
" sched_tag TEXT," \
" alive INTEGER," \
" UNIQUE( mailbox, imap_uid )," \
" UNIQUE( mailbox, resource ) );" \
"CREATE INDEX IF NOT EXISTS idx_ical_uid ON ical_objs ( ical_uid );"
#define CMD_CREATE_CARD \
"CREATE TABLE IF NOT EXISTS vcard_objs (" \
" rowid INTEGER PRIMARY KEY," \
" creationdate INTEGER," \
" mailbox TEXT NOT NULL," \
" resource TEXT NOT NULL," \
" imap_uid INTEGER," \
" modseq INTEGER," \
" createdmodseq INTEGER," \
" lock_token TEXT," \
" lock_owner TEXT," \
" lock_ownerid TEXT," \
" lock_expire INTEGER," \
" version INTEGER," \
" vcard_uid TEXT," \
" kind INTEGER," \
" fullname TEXT," \
" name TEXT," \
" nickname TEXT," \
" alive INTEGER," \
" UNIQUE( mailbox, imap_uid )," \
" UNIQUE( mailbox, resource ) );" \
"CREATE INDEX IF NOT EXISTS idx_vcard_fn ON vcard_objs ( fullname );" \
"CREATE INDEX IF NOT EXISTS idx_vcard_uid ON vcard_objs ( vcard_uid );"
#define CMD_CREATE_EM \
"CREATE TABLE IF NOT EXISTS vcard_emails (" \
" rowid INTEGER PRIMARY KEY," \
" objid INTEGER," \
" pos INTEGER NOT NULL," /* for sorting */ \
" email TEXT NOT NULL COLLATE NOCASE," \
" ispref INTEGER NOT NULL DEFAULT 0," \
" ispinned INTEGER NOT NULL DEFAULT 0," \
" FOREIGN KEY (objid) REFERENCES vcard_objs (rowid) ON DELETE CASCADE );" \
"CREATE INDEX IF NOT EXISTS idx_vcard_email ON vcard_emails ( email COLLATE NOCASE );"
#define CMD_CREATE_GR \
"CREATE TABLE IF NOT EXISTS vcard_groups (" \
" rowid INTEGER PRIMARY KEY," \
" objid INTEGER," \
" pos INTEGER NOT NULL," /* for sorting */ \
" member_uid TEXT NOT NULL," \
" otheruser TEXT NOT NULL DEFAULT \"\"," \
" FOREIGN KEY (objid) REFERENCES vcard_objs (rowid) ON DELETE CASCADE );"
#define CMD_CREATE_OBJS \
"CREATE TABLE IF NOT EXISTS dav_objs (" \
" rowid INTEGER PRIMARY KEY," \
" creationdate INTEGER," \
" mailbox TEXT NOT NULL," \
" resource TEXT NOT NULL," \
" imap_uid INTEGER," \
" modseq INTEGER," \
" createdmodseq INTEGER," \
" lock_token TEXT," \
" lock_owner TEXT," \
" lock_ownerid TEXT," \
" lock_expire INTEGER," \
" filename TEXT," \
" type TEXT," \
" subtype TEXT," \
" res_uid TEXT," \
" ref_count INTEGER," \
" alive INTEGER," \
" UNIQUE( mailbox, imap_uid )," \
" UNIQUE( mailbox, resource ) );" \
#define CMD_CREATE_CALCACHE \
"CREATE TABLE IF NOT EXISTS ical_jmapcache (" \
" rowid INTEGER NOT NULL," \
" userid TEXT NOT NULL," \
" jmapversion INTEGER NOT NULL," \
" jmapdata TEXT NOT NULL," \
" PRIMARY KEY (rowid, userid)" \
" FOREIGN KEY (rowid) REFERENCES ical_objs (rowid) ON DELETE CASCADE );"
#define CMD_CREATE_CARDCACHE \
"CREATE TABLE IF NOT EXISTS vcard_jmapcache (" \
" rowid INTEGER NOT NULL PRIMARY KEY," \
" jmapversion INTEGER NOT NULL," \
" jmapdata TEXT NOT NULL," \
" FOREIGN KEY (rowid) REFERENCES vcard_objs (rowid) ON DELETE CASCADE );"
#define CMD_CREATE_SIEVE \
"CREATE TABLE IF NOT EXISTS sieve_scripts (" \
" rowid INTEGER PRIMARY KEY," \
" creationdate INTEGER," \
" lastupdated INTEGER," \
" mailbox TEXT NOT NULL," \
" imap_uid INTEGER," \
" modseq INTEGER," \
" createdmodseq INTEGER," \
" id TEXT NOT NULL," \
" name TEXT NOT NULL," \
" contentid TEXT NOT NULL," \
" isactive INTEGER," \
" alive INTEGER," \
" UNIQUE( mailbox, imap_uid )," \
" UNIQUE( id ) );" \
"CREATE INDEX IF NOT EXISTS idx_sieve_name ON sieve_scripts ( name );"
#define CMD_CREATE CMD_CREATE_CAL CMD_CREATE_CARD CMD_CREATE_EM CMD_CREATE_GR \
CMD_CREATE_OBJS CMD_CREATE_CALCACHE CMD_CREATE_CARDCACHE \
CMD_CREATE_SIEVE
/* leaves these unused columns around, but that's life. A dav_reconstruct
* will fix them */
#define CMD_DBUPGRADEv2 \
"ALTER TABLE ical_objs ADD COLUMN comp_flags INTEGER;" \
"UPDATE ical_objs SET comp_flags = recurring + 2 * transp;"
#define CMD_DBUPGRADEv3 \
"ALTER TABLE ical_objs ADD COLUMN modseq INTEGER;" \
"UPDATE ical_objs SET modseq = 1;" \
"ALTER TABLE vcard_objs ADD COLUMN modseq INTEGER;" \
"UPDATE vcard_objs SET modseq = 1;"
#define CMD_DBUPGRADEv4 \
"ALTER TABLE ical_objs ADD COLUMN alive INTEGER;" \
"UPDATE ical_objs SET alive = 1;" \
"ALTER TABLE vcard_objs ADD COLUMN alive INTEGER;" \
"UPDATE vcard_objs SET alive = 1;"
#define CMD_DBUPGRADEv5 \
"ALTER TABLE vcard_emails ADD COLUMN ispref INTEGER NOT NULL DEFAULT 0;" \
"ALTER TABLE vcard_groups ADD COLUMN otheruser TEXT NOT NULL DEFAULT \"\";"
#define CMD_DBUPGRADEv6 CMD_CREATE_OBJS
#define CMD_DBUPGRADEv7 \
"ALTER TABLE ical_objs ADD COLUMN createdmodseq INTEGER;" \
"UPDATE ical_objs SET createdmodseq = 0;" \
"ALTER TABLE vcard_objs ADD COLUMN createdmodseq INTEGER;" \
"UPDATE vcard_objs SET createdmodseq = 0;" \
"ALTER TABLE dav_objs ADD COLUMN createdmodseq INTEGER;" \
"UPDATE dav_objs SET createdmodseq = 0;"
#define CMD_DBUPGRADEv8 \
"ALTER TABLE vcard_emails ADD COLUMN ispinned INTEGER NOT NULL DEFAULT 0;"
#define CMD_DBUPGRADEv9 CMD_CREATE_CALCACHE CMD_CREATE_CARDCACHE
#define CMD_DBUPGRADEv10 \
"CREATE UNIQUE INDEX IF NOT EXISTS idx_ical_imapuid ON ical_objs ( mailbox, imap_uid );" \
"CREATE UNIQUE INDEX IF NOT EXISTS idx_vcard_imapuid ON vcard_objs ( mailbox, imap_uid );" \
"CREATE UNIQUE INDEX IF NOT EXISTS idx_object_imapuid ON dav_objs ( mailbox, imap_uid );" \
"DROP INDEX IF EXISTS idx_res_uid;"
#define CMD_DBUPGRADEv14 CMD_CREATE_SIEVE
+static int sievedb_upgrade(sqldb_t *db);
+
struct sqldb_upgrade davdb_upgrade[] = {
{ 2, CMD_DBUPGRADEv2, NULL },
{ 3, CMD_DBUPGRADEv3, NULL },
{ 4, CMD_DBUPGRADEv4, NULL },
{ 5, CMD_DBUPGRADEv5, NULL },
{ 6, CMD_DBUPGRADEv6, NULL },
{ 7, CMD_DBUPGRADEv7, NULL },
{ 8, CMD_DBUPGRADEv8, NULL },
{ 9, CMD_DBUPGRADEv9, NULL },
{ 10, CMD_DBUPGRADEv10, NULL },
/* Don't upgrade to version 11. We only jump to 11 on CREATE */
/* Don't upgrade to version 12. This was an intermediate Sieve DB version */
/* Don't upgrade to version 13. This was an intermediate Sieve DB version */
- { 14, CMD_DBUPGRADEv14,
-#ifdef USE_SIEVE
- &sievedb_upgrade
-#else
- NULL
-#endif
- },
+ { 14, CMD_DBUPGRADEv14, &sievedb_upgrade },
{ 0, NULL, NULL }
};
#define DB_VERSION 14
static sqldb_t *reconstruct_db;
/* Create filename corresponding to DAV DB for mailbox */
EXPORTED void dav_getpath(struct buf *fname, struct mailbox *mailbox)
{
char *userid = mboxname_to_userid(mailbox_name(mailbox));
if (userid) dav_getpath_byuserid(fname, userid);
else buf_setcstr(fname, mailbox_meta_fname(mailbox, META_DAV));
free(userid);
}
/* Create filename corresponding to DAV DB for userid */
EXPORTED void dav_getpath_byuserid(struct buf *fname, const char *userid)
{
char *path = user_hash_meta(userid, FNAME_DAVSUFFIX);
buf_setcstr(fname, path);
free(path);
}
EXPORTED sqldb_t *dav_open_userid(const char *userid)
{
if (reconstruct_db) return reconstruct_db;
sqldb_t *db = NULL;
struct buf fname = BUF_INITIALIZER;
dav_getpath_byuserid(&fname, userid);
db = sqldb_open(buf_cstring(&fname), CMD_CREATE, DB_VERSION, davdb_upgrade,
config_getduration(IMAPOPT_DAV_LOCK_TIMEOUT, 's') * 1000);
buf_free(&fname);
return db;
}
EXPORTED sqldb_t *dav_open_mailbox(struct mailbox *mailbox)
{
if (reconstruct_db) return reconstruct_db;
sqldb_t *db = NULL;
struct buf fname = BUF_INITIALIZER;
dav_getpath(&fname, mailbox);
db = sqldb_open(buf_cstring(&fname), CMD_CREATE, DB_VERSION, davdb_upgrade,
config_getduration(IMAPOPT_DAV_LOCK_TIMEOUT, 's') * 1000);
buf_free(&fname);
return db;
}
EXPORTED int dav_attach_userid(sqldb_t *db, const char *userid)
{
assert (!reconstruct_db);
struct buf fname = BUF_INITIALIZER;
dav_getpath_byuserid(&fname, userid);
int r = sqldb_attach(db, buf_cstring(&fname));
buf_free(&fname);
return r;
}
EXPORTED int dav_attach_mailbox(sqldb_t *db, struct mailbox *mailbox)
{
assert (!reconstruct_db);
struct buf fname = BUF_INITIALIZER;
dav_getpath(&fname, mailbox);
int r = sqldb_attach(db, buf_cstring(&fname));
buf_free(&fname);
return r;
}
EXPORTED int dav_close(sqldb_t **dbp)
{
if (reconstruct_db) return 0;
return sqldb_close(dbp);
}
/*
* mboxlist_usermboxtree() callback function to create DAV DB entries for a mailbox
*/
static int _dav_reconstruct_mb(const mbentry_t *mbentry,
void *rock
#ifndef WITH_JMAP
__attribute__((unused))
#endif
)
{
#ifdef WITH_JMAP
const char *userid = (const char *) rock;
struct buf attrib = BUF_INITIALIZER;
#endif
int (*addproc)(struct mailbox *) = NULL;
int r = 0;
signals_poll();
switch (mbtype_isa(mbentry->mbtype)) {
#ifdef WITH_DAV
case MBTYPE_CALENDAR:
case MBTYPE_COLLECTION:
case MBTYPE_ADDRESSBOOK:
addproc = &mailbox_add_dav;
break;
#endif
#ifdef USE_SIEVE
case MBTYPE_SIEVE:
addproc = &mailbox_add_sieve;
break;
#endif
#ifdef WITH_JMAP
case MBTYPE_JMAPSUBMIT:
addproc = &mailbox_add_email_alarms;
break;
case MBTYPE_EMAIL:
r = annotatemore_lookup(mbentry->name, "/specialuse", userid, &attrib);
if (!r && buf_len(&attrib)) {
strarray_t *specialuse =
strarray_split(buf_cstring(&attrib), NULL, 0);
if (strarray_find(specialuse, "\\Snoozed", 0) >= 0) {
addproc = &mailbox_add_email_alarms;
}
strarray_free(specialuse);
}
buf_free(&attrib);
break;
#endif
}
if (addproc) {
struct mailbox *mailbox = NULL;
/* Open/lock header */
r = mailbox_open_irl(mbentry->name, &mailbox);
if (!r) r = addproc(mailbox);
mailbox_close(&mailbox);
}
return r;
}
static void run_audit_tool(const char *tool, const char *userid, const char *srcdb, const char *dstdb)
{
pid_t pid = fork();
if (pid < 0)
return;
if (pid == 0) {
/* child */
execl(tool, tool, "-C", config_filename, "-u", userid, srcdb, dstdb, (void *)NULL);
exit(-1);
}
int status;
while (waitpid(pid, &status, 0) < 0);
}
EXPORTED int dav_reconstruct_user(const char *userid, const char *audit_tool)
{
syslog(LOG_NOTICE, "dav_reconstruct_user: %s", userid);
struct buf fname = BUF_INITIALIZER;
dav_getpath_byuserid(&fname, userid);
struct buf newfname = BUF_INITIALIZER;
dav_getpath_byuserid(&newfname, userid);
buf_printf(&newfname, ".NEW");
struct mboxlock *namespacelock = user_namespacelock(userid);
int r = IMAP_IOERROR;
reconstruct_db = sqldb_open(buf_cstring(&newfname), CMD_CREATE, DB_VERSION, davdb_upgrade,
config_getduration(IMAPOPT_DAV_LOCK_TIMEOUT, 's') * 1000);
if (reconstruct_db) {
r = sqldb_begin(reconstruct_db, "reconstruct");
#ifdef WITH_DAV
// make all the alarm updates to go this database too
if (!r) r = caldav_alarm_set_reconstruct(reconstruct_db);
#endif
// reconstruct everything
if (!r) r = mboxlist_usermboxtree(userid, NULL,
_dav_reconstruct_mb, (void *) userid, 0);
#ifdef WITH_DAV
// make sure all the alarms are resolved
if (!r) r = caldav_alarm_process(0, NULL, /*dryrun*/1);
// commit events over to ther alarm database if we're keeping them
if (!r && !audit_tool) r = caldav_alarm_commit_reconstruct(userid);
else caldav_alarm_rollback_reconstruct();
#endif
// and commit to this DB
if (r) sqldb_rollback(reconstruct_db, "reconstruct");
else sqldb_commit(reconstruct_db, "reconstruct");
sqldb_close(&reconstruct_db);
}
/* this actually works before close according to the internets */
if (r) {
syslog(LOG_ERR, "dav_reconstruct_user: %s FAILED %s", userid, error_message(r));
if (audit_tool) {
printf("Not auditing %s, reconstruct failed %s\n", userid, error_message(r));
}
unlink(buf_cstring(&newfname));
}
else {
syslog(LOG_NOTICE, "dav_reconstruct_user: %s SUCCEEDED", userid);
if (audit_tool) {
run_audit_tool(audit_tool, userid, buf_cstring(&fname), buf_cstring(&newfname));
unlink(buf_cstring(&newfname));
}
else {
rename(buf_cstring(&newfname), buf_cstring(&fname));
}
}
mboxname_release(&namespacelock);
buf_free(&newfname);
buf_free(&fname);
return 0;
}
+
+
+struct sievedb_upgrade_rock {
+ char *mboxname;
+ strarray_t *sha1;
+};
+
+static int sievedb_upgrade_cb(sqlite3_stmt *stmt, void *rock)
+{
+ struct sievedb_upgrade_rock *srock = (struct sievedb_upgrade_rock *) rock;
+
+ if (!srock->mboxname) {
+ srock->mboxname = xstrdup((const char *) sqlite3_column_text(stmt, 0));
+ }
+
+ if (srock->sha1) {
+ const char *content = (const char *) sqlite3_column_text(stmt, 1);
+ unsigned rowid = sqlite3_column_int(stmt, 2);
+ struct message_guid uuid;
+
+ /* Generate SHA1 from content */
+ message_guid_generate(&uuid, content, strlen(content));
+
+ /* Add SHA1 to our array using rowid as the index */
+ strarray_set(srock->sha1, rowid, message_guid_encode(&uuid));
+ }
+
+ return 0;
+}
+
+#define CMD_GET_v12_ROWS \
+ "SELECT mailbox, content, rowid FROM sieve_scripts;"
+
+#define CMD_ALTER_v12_TABLE \
+ "ALTER TABLE sieve_scripts RENAME COLUMN content TO contentid;"
+
+#define CMD_UPDATE_v13_ROW \
+ "UPDATE sieve_scripts SET contentid = :contentid WHERE rowid = :rowid;"
+
+#define CMD_GET_v13_ROW1 \
+ "SELECT mailbox FROM sieve_scripts LIMIT 1;"
+
+#define CMD_UPDATE_v13_TABLE \
+ "UPDATE sieve_scripts SET mailbox = :mailbox;"
+
+
+/* Upgrade v12/v13 sieve_script table to v14 */
+static int sievedb_upgrade(sqldb_t *db)
+{
+ struct sievedb_upgrade_rock srock = { NULL, NULL };
+ struct sqldb_bindval bval[] = {
+ { ":rowid", SQLITE_INTEGER, { .i = 0 } },
+ { ":contentid", SQLITE_TEXT, { .s = NULL } },
+ { ":mailbox", SQLITE_TEXT, { .s = NULL } },
+ { NULL, SQLITE_NULL, { .s = NULL } } };
+ strarray_t sha1 = STRARRAY_INITIALIZER;
+ mbentry_t *mbentry = NULL;
+ int rowid;
+ int r = 0;
+
+ if (db->version == 12) {
+ /* Create an array of SHA1 for the content in each record */
+ srock.sha1 = &sha1;
+ r = sqldb_exec(db, CMD_GET_v12_ROWS, NULL, &sievedb_upgrade_cb, &srock);
+ if (r) goto done;
+
+ /* Rename 'content' -> 'contentid' */
+ r = sqldb_exec(db, CMD_ALTER_v12_TABLE, NULL, NULL, NULL);
+ if (r) goto done;
+
+ /* Rewrite 'contentid' columns with actual ids (SHA1) */
+ for (rowid = 1; rowid < strarray_size(&sha1); rowid++) {
+ bval[0].val.i = rowid;
+ bval[1].val.s = strarray_nth(&sha1, rowid);
+
+ r = sqldb_exec(db, CMD_UPDATE_v13_ROW, bval, NULL, NULL);
+ if (r) goto done;
+ }
+ }
+ else if (db->version == 13) {
+ /* Fetch mailbox name from first record */
+ r = sqldb_exec(db, CMD_GET_v13_ROW1, NULL, &sievedb_upgrade_cb, &srock);
+ if (r) goto done;
+ }
+
+ /* This will only be set if we are upgrading from v12 or v13
+ AND there are records in the table */
+ if (!srock.mboxname) goto done;
+
+ r = mboxlist_lookup_allow_all(srock.mboxname, &mbentry, NULL);
+ if (r) goto done;
+
+ /* Rewrite 'mailbox' columns with mboxid rather than mboxname */
+ bval[2].val.s = mbentry->uniqueid;
+ r = sqldb_exec(db, CMD_UPDATE_v13_TABLE, bval, NULL, NULL);
+
+ done:
+ mboxlist_entry_free(&mbentry);
+ strarray_fini(&sha1);
+ free(srock.mboxname);
+
+ return r;
+}
diff --git a/imap/sieve_db.c b/imap/sieve_db.c
index 0e6ffcbc6..6088b8425 100644
--- a/imap/sieve_db.c
+++ b/imap/sieve_db.c
@@ -1,1170 +1,1079 @@
/* sieve_db.c -- implementation of per-user Sieve database
*
* Copyright (c) 1994-2020 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any legal
* details, please contact
* Carnegie Mellon University
* Center for Technology Transfer and Enterprise Creation
* 4615 Forbes Avenue
* Suite 302
* Pittsburgh, PA 15213
* (412) 268-7393, fax: (412) 268-7395
* innovation@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include <config.h>
#include <syslog.h>
#include <string.h>
#include "append.h"
#include "cyrusdb.h"
#include "dav_db.h"
#include <errno.h>
#include "global.h"
#include "jmap_api.h"
#include "libconfig.h"
#include "mboxlist.h"
#include "sieve_db.h"
#include "sievedir.h"
#include "times.h"
#include "user.h"
#include "util.h"
#include "xmalloc.h"
#include "sieve/bytecode.h"
#include "sieve/bc_parse.h"
#include "sieve/script.h"
#include "sieve/sieve_interface.h"
/* generated headers are not necessarily in current directory */
#include "imap/imap_err.h"
enum {
STMT_SELRSRC,
STMT_SELUID,
STMT_SELMBOX,
STMT_INSERT,
STMT_UPDATE,
STMT_DELETE,
STMT_DELMBOX,
STMT_BEGIN,
STMT_COMMIT,
STMT_ROLLBACK
};
#define NUM_STMT 10
struct sieve_db {
sqldb_t *db; /* DB handle */
struct buf mailbox; /* buffers for copies of column text */
struct buf id;
struct buf name;
struct buf contentid;
};
static int sieve_initialized = 0;
static void done_cb(void *rock __attribute__((unused))) {
sievedb_done();
}
static void init_internal() {
if (!sieve_initialized) {
sievedb_init();
cyrus_modules_add(done_cb, NULL);
}
}
EXPORTED int sievedb_init(void)
{
int r = sqldb_init();
if (!r) sieve_initialized = 1;
return r;
}
EXPORTED int sievedb_done(void)
{
int r = sqldb_done();
if (!r) sieve_initialized = 0;
return r;
}
/* Open Sieve DB corresponding to userid */
EXPORTED struct sieve_db *sievedb_open_userid(const char *userid)
{
struct sieve_db *sievedb = NULL;
init_internal();
sqldb_t *db = dav_open_userid(userid);
if (!db) return NULL;
sievedb = xzmalloc(sizeof(struct sieve_db));
sievedb->db = db;
return sievedb;
}
/* Open Sieve DB corresponding to mailbox */
EXPORTED struct sieve_db *sievedb_open_mailbox(struct mailbox *mailbox)
{
struct sieve_db *sievedb = NULL;
char *userid = mboxname_to_userid(mailbox_name(mailbox));
init_internal();
if (userid) {
sievedb = sievedb_open_userid(userid);
free(userid);
return sievedb;
}
sqldb_t *db = dav_open_mailbox(mailbox);
if (!db) return NULL;
sievedb = xzmalloc(sizeof(struct sieve_db));
sievedb->db = db;
return sievedb;
}
/* Close Sieve DB */
EXPORTED int sievedb_close(struct sieve_db *sievedb)
{
int r = 0;
if (!sievedb) return 0;
buf_free(&sievedb->mailbox);
buf_free(&sievedb->id);
buf_free(&sievedb->name);
buf_free(&sievedb->contentid);
r = dav_close(&sievedb->db);
free(sievedb);
return r;
}
EXPORTED int sievedb_begin(struct sieve_db *sievedb)
{
return sqldb_begin(sievedb->db, "sieve");
}
EXPORTED int sievedb_commit(struct sieve_db *sievedb)
{
return sqldb_commit(sievedb->db, "sieve");
}
EXPORTED int sievedb_abort(struct sieve_db *sievedb)
{
return sqldb_rollback(sievedb->db, "sieve");
}
struct read_rock {
struct sieve_db *db;
struct sieve_data *sdata;
int tombstones;
sieve_cb_t *cb;
void *rock;
};
static const char *column_text_to_buf(const char *text, struct buf *buf)
{
if (text) {
buf_setcstr(buf, text);
text = buf_cstring(buf);
}
return text;
}
static int read_cb(sqlite3_stmt *stmt, void *rock)
{
struct read_rock *rrock = (struct read_rock *) rock;
struct sieve_db *db = rrock->db;
struct sieve_data *sdata = rrock->sdata;
int r = 0;
memset(sdata, 0, sizeof(struct sieve_data));
sdata->modseq = sqlite3_column_int64(stmt, 5);
sdata->createdmodseq = sqlite3_column_int64(stmt, 6);
sdata->alive = sqlite3_column_int(stmt, 11);
if (!rrock->tombstones && !sdata->alive)
return 0;
sdata->rowid = sqlite3_column_int(stmt, 0);
sdata->creationdate = sqlite3_column_int(stmt, 1);
sdata->lastupdated = sqlite3_column_int(stmt, 2);
sdata->imap_uid = sqlite3_column_int(stmt, 4);
sdata->isactive = sqlite3_column_int(stmt, 10);
if (rrock->cb) {
/* We can use the column data directly for the callback */
sdata->mailbox = (const char *) sqlite3_column_text(stmt, 3);
sdata->id = (const char *) sqlite3_column_text(stmt, 7);
sdata->name = (const char *) sqlite3_column_text(stmt, 8);
sdata->contentid = (const char *) sqlite3_column_text(stmt, 9);
r = rrock->cb(rrock->rock, sdata);
}
else {
/* For single row SELECTs like sieve_read(),
* we need to make a copy of the column data before
* it gets flushed by sqlite3_step() or sqlite3_reset() */
sdata->mailbox =
column_text_to_buf((const char *) sqlite3_column_text(stmt, 3),
&db->mailbox);
sdata->id =
column_text_to_buf((const char *) sqlite3_column_text(stmt, 7),
&db->id);
sdata->name =
column_text_to_buf((const char *) sqlite3_column_text(stmt, 8),
&db->name);
sdata->contentid =
column_text_to_buf((const char *) sqlite3_column_text(stmt, 9),
&db->contentid);
}
return r;
}
#define CMD_GETFIELDS \
"SELECT rowid, creationdate, lastupdated, mailbox, imap_uid," \
" modseq, createdmodseq, id, name, contentid, isactive, alive" \
" FROM sieve_scripts"
#define CMD_SELNAME CMD_GETFIELDS \
" WHERE name = :name;"
EXPORTED int sievedb_lookup_name(struct sieve_db *sievedb, const char *name,
struct sieve_data **result, int tombstones)
{
struct sqldb_bindval bval[] = {
{ ":name", SQLITE_TEXT, { .s = name } },
{ NULL, SQLITE_NULL, { .s = NULL } } };
static struct sieve_data sdata;
struct read_rock rrock = { sievedb, &sdata, tombstones, NULL, NULL };
int r;
*result = memset(&sdata, 0, sizeof(struct sieve_data));
r = sqldb_exec(sievedb->db, CMD_SELNAME, bval, &read_cb, &rrock);
if (!r && !sdata.rowid) r = CYRUSDB_NOTFOUND;
return r;
}
#define CMD_SELID CMD_GETFIELDS \
" WHERE id = :id;"
EXPORTED int sievedb_lookup_id(struct sieve_db *sievedb, const char *id,
struct sieve_data **result, int tombstones)
{
struct sqldb_bindval bval[] = {
{ ":id", SQLITE_TEXT, { .s = id } },
{ NULL, SQLITE_NULL, { .s = NULL } } };
static struct sieve_data sdata;
struct read_rock rrock = { sievedb, &sdata, tombstones, NULL, NULL };
int r;
*result = memset(&sdata, 0, sizeof(struct sieve_data));
r = sqldb_exec(sievedb->db, CMD_SELID, bval, &read_cb, &rrock);
if (!r && !sdata.rowid) r = CYRUSDB_NOTFOUND;
return r;
}
#define CMD_SELIMAPUID CMD_GETFIELDS \
" WHERE imap_uid = :imap_uid;"
EXPORTED int sievedb_lookup_imapuid(struct sieve_db *sievedb, int imap_uid,
struct sieve_data **result, int tombstones)
{
struct sqldb_bindval bval[] = {
{ ":imap_uid", SQLITE_INTEGER, { .i = imap_uid } },
{ NULL, SQLITE_NULL, { .s = NULL } } };
static struct sieve_data sdata;
struct read_rock rrock = { sievedb, &sdata, tombstones, NULL, NULL };
int r;
*result = memset(&sdata, 0, sizeof(struct sieve_data));
r = sqldb_exec(sievedb->db, CMD_SELIMAPUID, bval, &read_cb, &rrock);
if (!r && !sdata.rowid) r = CYRUSDB_NOTFOUND;
sdata.imap_uid = imap_uid;
return r;
}
#define CMD_SELACTIVE CMD_GETFIELDS " WHERE isactive = 1 AND alive = 1;"
EXPORTED int sievedb_lookup_active(struct sieve_db *sievedb,
struct sieve_data **result)
{
static struct sieve_data sdata;
struct read_rock rrock = { sievedb, &sdata, 0, NULL, NULL };
int r;
*result = memset(&sdata, 0, sizeof(struct sieve_data));
r = sqldb_exec(sievedb->db, CMD_SELACTIVE, NULL, &read_cb, &rrock);
if (!r && !sdata.rowid) r = CYRUSDB_NOTFOUND;
return r;
}
#define CMD_SELMBOX CMD_GETFIELDS \
" WHERE alive = 1;"
EXPORTED int sievedb_foreach(struct sieve_db *sievedb,
int (*cb)(void *rock, struct sieve_data *data),
void *rock)
{
struct sieve_data sdata;
struct read_rock rrock = { sievedb, &sdata, 0, cb, rock };
return sqldb_exec(sievedb->db, CMD_SELMBOX, NULL, &read_cb, &rrock);
}
#define CMD_INSERT \
"INSERT INTO sieve_scripts (" \
" creationdate, lastupdated, mailbox, imap_uid," \
" modseq, createdmodseq, id, name, contentid, isactive, alive )" \
" VALUES (" \
" :creationdate, :lastupdated, :mailbox, :imap_uid," \
" :modseq, :createdmodseq, :id, :name, :contentid, :isactive, :alive );"
#define CMD_UPDATE \
"UPDATE sieve_scripts SET" \
" lastupdated = :lastupdated," \
" imap_uid = :imap_uid," \
" modseq = :modseq," \
" name = :name," \
" contentid = :contentid," \
" isactive = :isactive," \
" alive = :alive" \
" WHERE rowid = :rowid;"
EXPORTED int sievedb_write(struct sieve_db *sievedb, struct sieve_data *sdata)
{
struct sqldb_bindval bval[] = {
{ ":lastupdated", SQLITE_INTEGER, { .i = sdata->lastupdated } },
{ ":imap_uid", SQLITE_INTEGER, { .i = sdata->imap_uid } },
{ ":modseq", SQLITE_INTEGER, { .i = sdata->modseq } },
{ ":name", SQLITE_TEXT, { .s = sdata->name } },
{ ":contentid", SQLITE_TEXT, { .s = sdata->contentid } },
{ ":isactive", SQLITE_INTEGER, { .i = sdata->isactive } },
{ ":alive", SQLITE_INTEGER, { .i = sdata->alive } },
{ NULL, SQLITE_NULL, { .s = NULL } },
{ NULL, SQLITE_NULL, { .s = NULL } },
{ NULL, SQLITE_NULL, { .s = NULL } },
{ NULL, SQLITE_NULL, { .s = NULL } },
{ NULL, SQLITE_NULL, { .s = NULL } } };
const char *cmd;
int r;
if (sdata->rowid) {
cmd = CMD_UPDATE;
bval[7].name = ":rowid";
bval[7].type = SQLITE_INTEGER;
bval[7].val.i = sdata->rowid;
}
else {
cmd = CMD_INSERT;
bval[7].name = ":creationdate";
bval[7].type = SQLITE_INTEGER;
bval[7].val.i = sdata->creationdate;
bval[8].name = ":createdmodseq";
bval[8].type = SQLITE_INTEGER;
bval[8].val.i = sdata->createdmodseq;
bval[9].name = ":id";
bval[9].type = SQLITE_TEXT;
bval[9].val.s = sdata->id;
bval[10].name = ":mailbox";
bval[10].type = SQLITE_TEXT;
bval[10].val.s = sdata->mailbox;
}
r = sqldb_exec(sievedb->db, cmd, bval, NULL, NULL);
return r;
}
#define CMD_DELETE "DELETE FROM sieve_scripts WHERE rowid = :rowid;"
EXPORTED int sievedb_delete(struct sieve_db *sievedb, unsigned rowid)
{
struct sqldb_bindval bval[] = {
{ ":rowid", SQLITE_INTEGER, { .i = rowid } },
{ NULL, SQLITE_NULL, { .s = NULL } } };
int r;
r = sqldb_exec(sievedb->db, CMD_DELETE, bval, NULL, NULL);
return r;
}
#define CMD_DELMBOX "DELETE FROM sieve_scripts;"
HIDDEN int sievedb_delmbox(struct sieve_db *sievedb)
{
int r;
r = sqldb_exec(sievedb->db, CMD_DELMBOX, NULL, NULL, NULL);
return r;
}
EXPORTED int sievedb_get_updates(struct sieve_db *sievedb,
modseq_t oldmodseq, int limit,
int (*cb)(void *rock, struct sieve_data *sdata),
void *rock)
{
struct sqldb_bindval bval[] = {
{ ":modseq", SQLITE_INTEGER, { .i = oldmodseq } },
/* SQLite interprets a negative limit as unbounded. */
{ ":limit", SQLITE_INTEGER, { .i = limit > 0 ? limit : -1 } },
{ NULL, SQLITE_NULL, { .s = NULL } }
};
static struct sieve_data sdata;
struct read_rock rrock = { sievedb, &sdata, 1 /* tombstones */, cb, rock };
struct buf sqlbuf = BUF_INITIALIZER;
int r;
buf_setcstr(&sqlbuf, CMD_GETFIELDS " WHERE");
if (!oldmodseq) buf_appendcstr(&sqlbuf, " alive = 1 AND");
buf_appendcstr(&sqlbuf, " modseq > :modseq ORDER BY modseq LIMIT :limit;");
r = sqldb_exec(sievedb->db, buf_cstring(&sqlbuf), bval, &read_cb, &rrock);
buf_free(&sqlbuf);
return r;
}
static int count_cb(sqlite3_stmt *stmt, void *rock)
{
int *count = (int *) rock;
*count = sqlite3_column_int(stmt, 0);
return 0;
}
#define CMD_COUNTREC "SELECT COUNT(name) FROM sieve_scripts WHERE alive = 1;"
EXPORTED int sievedb_count(struct sieve_db *sievedb, int *count)
{
int r;
*count = 0;
r = sqldb_exec(sievedb->db, CMD_COUNTREC, NULL, &count_cb, count);
return r;
}
static int lock_and_execute(struct mailbox *mailbox,
struct sieve_data *sdata,
void *rock,
int (*proc)(struct mailbox *mailbox,
struct sieve_data *sdata,
void *rock))
{
int r, unlock = 0;
if (!mailbox_index_islocked(mailbox, 1/*write*/)) {
r = mailbox_lock_index(mailbox, LOCK_EXCLUSIVE);
if (r) {
syslog(LOG_ERR, "locking mailbox %s failed: %s",
mailbox_name(mailbox), error_message(r));
return r;
}
unlock = 1;
}
r = proc(mailbox, sdata, rock);
if (unlock) mailbox_unlock_index(mailbox, NULL);
return r;
}
static int remove_uid(struct mailbox *mailbox, uint32_t uid)
{
struct index_record record;
int r;
r = mailbox_find_index_record(mailbox, uid, &record);
if (!r) {
record.internal_flags |= FLAG_INTERNAL_EXPUNGED;
r = mailbox_rewrite_index_record(mailbox, &record);
}
if (r) {
syslog(LOG_ERR, "expunging record (%s:%u) failed: %s",
mailbox_name(mailbox), uid, error_message(r));
}
return r;
}
static int store_script(struct mailbox *mailbox, struct sieve_data *sdata,
void *rock)
{
const struct buf *databuf = (const struct buf *) rock;
strarray_t flags = STRARRAY_INITIALIZER;
struct auth_state *authstate = NULL;
struct buf buf = BUF_INITIALIZER;
uint32_t old_uid = sdata->imap_uid;
const char *data = buf_base(databuf);
size_t datalen = buf_len(databuf);
struct stagemsg *stage;
struct appendstate as;
time_t now = time(0);
FILE *f = NULL;
char *mimehdr;
int r = 0;
init_internal();
/* Prepare to stage the message */
if (!(f = append_newstage(mailbox_name(mailbox), now, 0, &stage))) {
syslog(LOG_ERR, "append_newstage(%s) failed", mailbox_name(mailbox));
return CYRUSDB_IOERROR;
}
/* Create RFC 5322 header for script */
char *userid = mboxname_to_userid(mailbox_name(mailbox));
if (strchr(userid, '@')) {
buf_printf(&buf, "<%s>", userid);
}
else {
buf_printf(&buf, "<%s@%s>", userid, config_servername);
}
mimehdr = charset_encode_mimeheader(buf_cstring(&buf), buf_len(&buf), 0);
fprintf(f, "From: %s\r\n", mimehdr);
free(mimehdr);
mimehdr = charset_encode_mimeheader(sdata->name, 0, 0);
fprintf(f, "Subject: %s\r\n", mimehdr);
free(mimehdr);
char datestr[80];
time_to_rfc5322(now, datestr, sizeof(datestr));
fprintf(f, "Date: %s\r\n", datestr);
/* Use SHA1(script) as contentid */
struct message_guid uuid;
message_guid_generate(&uuid, data, datalen);
sdata->contentid = message_guid_encode(&uuid);
/* Use scriptid@servername as Message-ID */
fprintf(f, "Message-ID: <%s@%s>\r\n", sdata->contentid, config_servername);
fprintf(f, "Content-Type: application/sieve; charset=utf-8\r\n");
fprintf(f, "Content-Length: %lu\r\n", datalen);
fprintf(f, "Content-Disposition: attachment;\r\n\tfilename=\"%s%s\"\r\n",
sdata->id ? sdata->id : makeuuid(), SIEVE_EXTENSION);
fputs("MIME-Version: 1.0\r\n", f);
fputs("\r\n", f);
/* Write the script data to the file */
fwrite(data, datalen, 1, f);
fclose(f);
if (sdata->isactive) {
/* Flag script as active */
strarray_append(&flags, "\\Flagged");
/* Need authstate in order to set flags */
authstate = auth_newstate(userid);
}
if ((r = append_setup_mbox(&as, mailbox, userid, authstate,
0, NULL, NULL, 0, 0))) {
syslog(LOG_ERR, "append_setup(%s) failed: %s",
mailbox_name(mailbox), error_message(r));
}
else {
struct body *body = NULL;
r = append_fromstage(&as, &body, stage, now,
sdata->createdmodseq, &flags, 0, NULL);
if (body) {
message_free_body(body);
free(body);
}
if (r) {
syslog(LOG_ERR, "append_fromstage() failed: %s", error_message(r));
append_abort(&as);
}
else {
/* Commit the append to the sieve mailbox */
r = append_commit(&as);
if (r) {
syslog(LOG_ERR, "append_commit() failed: %s", error_message(r));
}
else if (old_uid) {
/* Now that we have the replacement script in place
expunge the old one. */
r = remove_uid(mailbox, old_uid);
}
}
}
append_removestage(stage);
auth_freestate(authstate);
strarray_fini(&flags);
buf_free(&buf);
free(userid);
return r;
}
EXPORTED int sieve_script_store(struct mailbox *mailbox,
struct sieve_data *sdata,
const struct buf *content)
{
return lock_and_execute(mailbox, sdata, (void *) content, &store_script);
}
static int activate_script(struct mailbox *mailbox, struct sieve_data *sdata,
void *rock __attribute__((unused)))
{
struct index_record record;
int activate = (sdata != NULL);
int r;
init_internal();
if (activate) {
if (sdata->isactive) return 0;
r = mailbox_find_index_record(mailbox, sdata->imap_uid, &record);
if (r) {
syslog(LOG_ERR, "fetching record (%s:%u) failed: %s",
mailbox_name(mailbox), sdata->imap_uid, error_message(r));
return r;
}
}
struct sieve_db *sievedb = sievedb_open_mailbox(mailbox);
if (!sievedb) {
syslog(LOG_ERR, "opening sieve_db for %s failed", mailbox_name(mailbox));
return CYRUSDB_IOERROR;
}
struct sieve_data *mydata = NULL;
r = sievedb_lookup_active(sievedb, &mydata);
if (r == CYRUSDB_NOTFOUND) {
/* No active script to deactivate */
r = 0;
}
else if (!r) {
struct index_record oldactive;
r = mailbox_find_index_record(mailbox, mydata->imap_uid, &oldactive);
if (r) {
syslog(LOG_ERR, "fetching record (%s:%u) failed: %s",
mailbox_name(mailbox), mydata->imap_uid, error_message(r));
}
else {
oldactive.system_flags &= ~FLAG_FLAGGED;
r = mailbox_rewrite_index_record(mailbox, &oldactive);
if (r) {
syslog(LOG_ERR, "unflagging record (%s:%u) failed: %s",
mailbox_name(mailbox), oldactive.uid, error_message(r));
}
}
}
sievedb_close(sievedb);
if (!r && activate) {
record.system_flags |= FLAG_FLAGGED;
r = mailbox_rewrite_index_record(mailbox, &record);
if (r) {
syslog(LOG_ERR, "flagging record (%s:%u) failed: %s",
mailbox_name(mailbox), record.uid, error_message(r));
}
}
return r;
}
EXPORTED int sieve_script_activate(struct mailbox *mailbox,
struct sieve_data *sdata)
{
return lock_and_execute(mailbox, sdata, NULL, &activate_script);
}
static int remove_script(struct mailbox *mailbox, struct sieve_data *sdata,
void *rock __attribute__((unused)))
{
init_internal();
return remove_uid(mailbox, sdata->imap_uid);
}
EXPORTED int sieve_script_remove(struct mailbox *mailbox,
struct sieve_data *sdata)
{
return lock_and_execute(mailbox, sdata, NULL, &remove_script);
}
EXPORTED int sieve_script_rename(struct mailbox *mailbox,
struct sieve_data *sdata,
const char *newname)
{
struct buf content = BUF_INITIALIZER;
int r;
r = sieve_script_fetch(mailbox, sdata, &content);
if (!r) {
sdata->name = newname;
r = sieve_script_store(mailbox, sdata, &content);
}
buf_free(&content);
return r;
}
EXPORTED int sieve_script_fetch(struct mailbox *mailbox,
const struct sieve_data *sdata,
struct buf *content)
{
struct index_record record;
int r;
r = mailbox_find_index_record(mailbox, sdata->imap_uid, &record);
if (!r) {
/* Load message containing the resource */
message_t *m = message_new_from_record(mailbox, &record);
r = message_get_field(m, "rawbody", MESSAGE_RAW, content);
message_unref(&m);
}
if (r) {
syslog(LOG_ERR, "fetching message (%s:%u) failed: %s",
mailbox_name(mailbox), sdata->imap_uid, error_message(r));
}
return r;
}
struct migrate_rock {
struct mailbox *mailbox;
char *active;
};
static int migrate_cb(const char *sievedir,
const char *fname, struct stat *sbuf,
const char *link_target __attribute__((unused)),
void *rock)
{
struct migrate_rock *mrock = (struct migrate_rock *) rock;
struct buf *content = sievedir_get_script(sievedir, fname);
if (content) {
struct sieve_data sdata;
char *myname = xstrndup(fname, strlen(fname) - SCRIPT_SUFFIX_LEN);
int deletebc = 0;
memset(&sdata, 0, sizeof(sdata));
if (!strcmp(myname, "jmap_vacation")) {
sdata.id = sdata.name = JMAP_URN_VACATION;
deletebc = 1;
}
else {
sdata.name = myname;
}
sdata.lastupdated = sbuf->st_mtime;
sdata.isactive = !strcmpnull(myname, mrock->active);
if (!store_script(mrock->mailbox, &sdata, content)) {
char path[PATH_MAX];
/* delete script */
snprintf(path, sizeof(path), "%s/%s", sievedir, fname);
unlink(path);
if (deletebc) {
sievedir_delete_script(sievedir, myname);
}
}
buf_destroy(content);
free(myname);
}
return SIEVEDIR_OK;
}
EXPORTED int sieve_ensure_folder(const char *userid, struct mailbox **mailboxptr)
{
const char *sievedir = user_sieve_path(userid);
struct stat sbuf;
int r;
r = stat(sievedir, &sbuf);
if (r && errno == ENOENT) {
if (!mailboxptr) {
/* Don't bother continuing if sievedir doesn't currently exist */
return 0;
}
r = cyrus_mkdir(sievedir, 0755);
if (!r) {
r = mkdir(sievedir, 0755);
}
}
if (r) return IMAP_IOERROR;
struct mboxlock *namespacelock = NULL;
char *mboxname = sieve_mboxname(userid);
r = mboxlist_lookup(mboxname, NULL, NULL);
if (r == IMAP_MAILBOX_NONEXISTENT) {
namespacelock = user_namespacelock(userid);
if (!namespacelock) {
r = IMAP_MAILBOX_LOCKED;
goto done;
}
/* maybe we lost the race on this one */
r = mboxlist_lookup(mboxname, NULL, NULL);
}
if (!r && mailboxptr) {
r = mailbox_open_iwl(mboxname, mailboxptr);
if (r) {
syslog(LOG_ERR, "IOERROR: failed to open %s (%s)",
mboxname, error_message(r));
goto done;
}
}
else if (r == IMAP_MAILBOX_NONEXISTENT) {
/* Create locally */
struct mailbox *mailbox = NULL;
mbentry_t mbentry = MBENTRY_INITIALIZER;
mbentry.name = (char *) mboxname;
mbentry.mbtype = MBTYPE_SIEVE;
r = mboxlist_createmailbox(&mbentry, 0/*options*/, 0/*highestmodseq*/,
1/*isadmin*/, userid, NULL/*auth_state*/,
0/*flags*/, &mailbox);
if (r) {
syslog(LOG_ERR, "IOERROR: failed to create %s (%s)",
mboxname, error_message(r));
goto done;
}
/* Migrate scripts from sievedir into mailbox */
struct migrate_rock mrock =
{ mailbox, xstrdupnull(sievedir_get_active(sievedir)) };
sievedir_foreach(sievedir, SIEVEDIR_SCRIPTS_ONLY, &migrate_cb, &mrock);
free(mrock.active);
if (mailboxptr) *mailboxptr = mailbox;
else mailbox_close(&mailbox);
}
done:
mboxname_release(&namespacelock);
free(mboxname);
return r;
}
EXPORTED int sieve_script_rebuild(const char *userid,
const char *sievedir, const char *script)
{
struct buf namebuf = BUF_INITIALIZER, *content_buf = NULL;
struct sieve_data *sdata = NULL;
struct sieve_db *db = NULL;
const char *content = NULL;
time_t lastupdated = 0;
struct stat bc_stat;
int r;
db = sievedb_open_userid(userid);
if (!db) {
r = IMAP_INTERNAL;
goto done;
}
/* Lookup script in Sieve DB */
r = sievedb_lookup_name(db, script, &sdata, 0);
if (!r) {
char *mboxname = sieve_mboxname(userid);
struct mailbox *mailbox = NULL;
lastupdated = sdata->lastupdated;
content_buf = buf_new();
r = mailbox_open_irl(mboxname, &mailbox);
if (r) {
syslog(LOG_ERR, "IOERROR: failed to open %s (%s)",
mboxname, error_message(r));
}
else {
r = sieve_script_fetch(mailbox, sdata, content_buf);
if (!r) {
content = buf_cstring(content_buf);
}
}
mailbox_close(&mailbox);
free(mboxname);
if (r) goto done;
}
else if (r == CYRUSDB_NOTFOUND) {
/* Get mtime of script file */
struct stat sbuf;
buf_printf(&namebuf, "%s/%s%s", sievedir, script, SCRIPT_SUFFIX);
r = stat(buf_cstring(&namebuf), &sbuf);
if (!r) {
lastupdated = sbuf.st_mtime;
}
else {
syslog(LOG_ERR, "IOERROR: stat %s: %m", buf_cstring(&namebuf));
}
}
if (r) {
r = IMAP_IOERROR;
goto done;
}
/* Get mtime of bytecode file */
buf_reset(&namebuf);
buf_printf(&namebuf, "%s/%s%s", sievedir, script, BYTECODE_SUFFIX);
r = stat(buf_cstring(&namebuf), &bc_stat);
if (r && errno != ENOENT) {
syslog(LOG_ERR, "IOERROR: stat %s: %m", buf_cstring(&namebuf));
r = IMAP_IOERROR;
goto done;
}
if (!r && bc_stat.st_mtime >= lastupdated) {
/* Check version of bytecode file */
sieve_execute_t *exe = NULL;
int version = -1;
r = sieve_script_load(buf_cstring(&namebuf), &exe);
if (!r) {
bc_header_parse((bytecode_input_t *) exe->bc_cur->data,
&version, NULL);
}
sieve_script_unload(&exe);
if (version == BYTECODE_VERSION) {
syslog(LOG_DEBUG,
"%s: %s is up to date\n", __func__, buf_cstring(&namebuf));
goto done;
}
}
if (!content) {
/* Fetch content from script file */
buf_reset(&namebuf);
buf_printf(&namebuf, "%s%s", script, SCRIPT_SUFFIX);
content_buf = sievedir_get_script(sievedir, buf_cstring(&namebuf));
if (!content_buf) {
r = IMAP_IOERROR;
goto done;
}
content = buf_cstring(content_buf);
}
/* Update bytecode */
char *errors = NULL;
r = sievedir_put_script(sievedir, script, content, &errors);
free(errors);
done:
buf_destroy(content_buf);
buf_free(&namebuf);
sievedb_close(db);
return r;
}
-#define CMD_ALTER_v12_TABLE \
- "ALTER TABLE sieve_scripts RENAME COLUMN content TO contentid;"
-
-#define CMD_UPDATE_v12_ROW \
- "UPDATE sieve_scripts SET" \
- " contentid = :contentid" \
- " WHERE rowid = :rowid;"
-
-#define CMD_UPDATE_v13_TABLE \
- "UPDATE sieve_scripts SET mailbox = :mailbox;"
-
-
-static int upgrade_cb(void *rock, struct sieve_data *sdata)
-{
- strarray_t *sha1 = (strarray_t *) rock;
- struct message_guid uuid;
-
- /* v12 stored script content in the column that is now named 'contentid' */
- const char *content = sdata->contentid;
-
- /* Generate SHA1 from content */
- message_guid_generate(&uuid, content, strlen(content));
-
- /* Add SHA1 to our array using rowid as the index */
- strarray_set(sha1, sdata->rowid, message_guid_encode(&uuid));
-
- return 0;
-}
-
-EXPORTED int sievedb_upgrade(sqldb_t *db)
-{
- strarray_t sha1 = STRARRAY_INITIALIZER;
- struct sieve_data sdata;
- struct sieve_db sievedb = { .db = db };
- struct read_rock rrock =
- { &sievedb, &sdata, 1 /*tombstones*/, upgrade_cb, &sha1 };
- struct sqldb_bindval bval[] = {
- { ":rowid", SQLITE_INTEGER, { .i = 0 } },
- { ":contentid", SQLITE_TEXT, { .s = NULL } },
- { ":mailbox", SQLITE_TEXT, { .s = NULL } },
- { NULL, SQLITE_NULL, { .s = NULL } } };
- mbentry_t *mbentry = NULL;
- int rowid;
- int r = 0;
-
- sdata.mailbox = NULL;
-
- if (db->version == 12) {
- /* Rename 'content' -> 'contentid' */
- r = sqldb_exec(db, CMD_ALTER_v12_TABLE, NULL, NULL, NULL);
- if (r) goto done;
-
- /* Create an array of SHA1 for the content in each record */
- r = sqldb_exec(db, CMD_GETFIELDS, NULL, &read_cb, &rrock);
- if (r) goto done;
-
- /* Rewrite 'contentid' columns with actual ids (SHA1) */
- for (rowid = 1; rowid < strarray_size(&sha1); rowid++) {
- bval[0].val.i = rowid;
- bval[1].val.s = strarray_nth(&sha1, rowid);
-
- r = sqldb_exec(db, CMD_UPDATE_v12_ROW, bval, NULL, NULL);
- if (r) goto done;
- }
- }
- else if (db->version == 13) {
- /* Fetch mailbox name from first record */
- rrock.cb = NULL;
- sdata.mailbox = NULL;
- r = sqldb_exec(db, CMD_GETFIELDS " LIMIT 1;",
- NULL, &read_cb, &rrock);
- if (r) goto done;
- }
-
- // this will only be set if we are upgrading from v12 or v13 and there are
- // records with a mailbox on them
- if (!sdata.mailbox) goto done;
-
- r = mboxlist_lookup_allow_all(sdata.mailbox, &mbentry, NULL);
- if (r) goto done;
-
- bval[2].val.s = mbentry->uniqueid;
- r = sqldb_exec(db, CMD_UPDATE_v13_TABLE, bval, NULL, NULL);
-
- done:
- mboxlist_entry_free(&mbentry);
- strarray_fini(&sha1);
-
- return r;
-}
-
EXPORTED char *sieve_mboxname(const char *userid)
{
struct buf boxbuf = BUF_INITIALIZER;
char *res = NULL;
init_internal();
buf_setcstr(&boxbuf, config_getstring(IMAPOPT_SIEVE_FOLDER));
res = mboxname_user_mbox(userid, buf_cstring(&boxbuf));
buf_free(&boxbuf);
return res;
}
diff --git a/imap/sieve_db.h b/imap/sieve_db.h
index f31699c09..0a2ebddd9 100644
--- a/imap/sieve_db.h
+++ b/imap/sieve_db.h
@@ -1,165 +1,162 @@
/* sieve_db.h -- abstract interface for per-user Sieve database
*
* Copyright (c) 1994-2020 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any legal
* details, please contact
* Carnegie Mellon University
* Center for Technology Transfer and Enterprise Creation
* 4615 Forbes Avenue
* Suite 302
* Pittsburgh, PA 15213
* (412) 268-7393, fax: (412) 268-7395
* innovation@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifndef SIEVE_DB_H
#define SIEVE_DB_H
#include <config.h>
#include "mailbox.h"
#include "sqldb.h"
struct sieve_db;
#define SIEVE_CREATE 0x01
#define SIEVE_TRUNC 0x02
#define SIEVE_EXTENSION ".sieve"
struct sieve_data {
unsigned rowid;
time_t creationdate;
time_t lastupdated;
const char *mailbox;
uint32_t imap_uid;
modseq_t modseq;
modseq_t createdmodseq;
const char *id;
const char *name;
const char *contentid;
unsigned isactive;
int alive;
};
typedef int sieve_cb_t(void *rock, struct sieve_data *sdata);
/* prepare for sieve operations in this process */
int sievedb_init(void);
/* done with all sieve operations for this process */
int sievedb_done(void);
/* get a database handle corresponding to userid */
struct sieve_db *sievedb_open_userid(const char *userid);
/* get a database handle corresponding to mailbox */
struct sieve_db *sievedb_open_mailbox(struct mailbox *mailbox);
/* close this handle */
int sievedb_close(struct sieve_db *sievedb);
/* lookup an entry from 'sievedb' by script name
(optionally inside a transaction for updates) */
int sievedb_lookup_name(struct sieve_db *sievedb, const char *name,
struct sieve_data **result, int tombstones);
/* lookup an entry from 'sievedb' by id
(optionally inside a transaction for updates) */
int sievedb_lookup_id(struct sieve_db *sievedb, const char *id,
struct sieve_data **result, int tombstones);
/* lookup an entry from 'sievedb' by IMAP uid
(optionally inside a transaction for updates) */
int sievedb_lookup_imapuid(struct sieve_db *sievedb, int uid,
struct sieve_data **result, int tombstones);
int sievedb_lookup_active(struct sieve_db *sievedb,
struct sieve_data **result);
/* process each entry in 'sievedb' with cb() */
int sievedb_foreach(struct sieve_db *sievedb,
int (*cb)(void *rock, struct sieve_data *data),
void *rock);
/* write an entry to 'sievedb' */
int sievedb_write(struct sieve_db *sievedb, struct sieve_data *sdata);
/* delete an entry from 'sievedb' */
int sievedb_delete(struct sieve_db *sievedb, unsigned rowid);
/* delete all entries for 'mailbox' from 'sievedb' */
int sievedb_delmbox(struct sieve_db *sievedb);
/* begin transaction */
int sievedb_begin(struct sieve_db *sievedb);
/* commit transaction */
int sievedb_commit(struct sieve_db *sievedb);
/* abort transaction */
int sievedb_abort(struct sieve_db *sievedb);
/* Process each entry for 'sievedb' with a modseq higher than oldmodseq,
* in ascending order of modseq.
* If max_records is positive, only call cb for at most this entries. */
int sievedb_get_updates(struct sieve_db *sievedb, modseq_t oldmodseq,
int max_records, sieve_cb_t *cb, void *rock);
/* count number of scripts */
int sievedb_count(struct sieve_db *sievedb, int *count);
-/* upgrade v12 sieve_script table to v13 */
-int sievedb_upgrade(sqldb_t *db);
-
int sieve_script_store(struct mailbox *mailbox, struct sieve_data *sdata,
const struct buf *content);
int sieve_script_activate(struct mailbox *mailbox, struct sieve_data *sdata);
int sieve_script_remove(struct mailbox *mailbox, struct sieve_data *sdata);
int sieve_script_rename(struct mailbox *mailbox,
struct sieve_data *sdata, const char *newname);
int sieve_script_fetch(struct mailbox *mailbox,
const struct sieve_data *sdata, struct buf *content);
int sieve_ensure_folder(const char *userid, struct mailbox **mailboxptr);
int sieve_script_rebuild(const char *userid,
const char *sievedir, const char *script);
/* calculate a mailbox name */
char *sieve_mboxname(const char *userid);
#endif /* SIEVE_DB_H */

File Metadata

Mime Type
text/x-diff
Expires
Sat, Apr 4, 3:32 AM (4 h, 6 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18822426
Default Alt Text
(64 KB)

Event Timeline