Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117751880
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
64 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
Mode
R111 cyrus-imapd
Attached
Detach File
Event Timeline