From 415050e0f73c043328793d5e0b53dec1b6634ae8 Mon Sep 17 00:00:00 2001 From: Pietro Cerutti Date: Wed, 26 Oct 2016 12:29:29 +0000 Subject: [PATCH] Split hcache code into per-backend files Also, - make configure arguments consistent in the form of --with-[=DIR] - rename WITH_DB4 macro to WITH_BDB (db5 is supported as well) Issue: #216 --- configure.ac | 128 +++--- doc/makedoc-defs.h | 4 +- globals.h | 4 +- hcache-bdb.c | 202 ++++++++++ hcache-gdbm.c | 117 ++++++ hcache-kc.c | 122 ++++++ hcache-lmdb.c | 278 +++++++++++++ hcache-qdbm.c | 90 +++++ hcache-tc.c | 107 +++++ hcache.c | 981 +++++++-------------------------------------- hcache.h | 220 +++++++++- imap/imap.c | 4 +- imap/message.c | 12 +- imap/util.c | 12 +- init.h | 4 +- mh.c | 71 +++- newsrc.c | 11 +- nntp.c | 22 +- pop.c | 12 +- 19 files changed, 1428 insertions(+), 973 deletions(-) create mode 100644 hcache-bdb.c create mode 100644 hcache-gdbm.c create mode 100644 hcache-kc.c create mode 100644 hcache-lmdb.c create mode 100644 hcache-qdbm.c create mode 100644 hcache-tc.c diff --git a/configure.ac b/configure.ac index 8e72c16f0..9a22684ed 100644 --- a/configure.ac +++ b/configure.ac @@ -905,18 +905,34 @@ AC_ARG_ENABLE(exact-address, AS_HELP_STRING([--enable-exact-address],[Enable reg fi]) dnl -- start cache -- -db_found=no db_requested=auto hcache_db_used=no AC_ARG_ENABLE(hcache, AS_HELP_STRING([--enable-hcache],[Enable header caching])) -AC_ARG_WITH(tokyocabinet, AS_HELP_STRING([--without-tokyocabinet],[Don't use tokyocabinet even if it is available])) -AC_ARG_WITH(kyotocabinet, AS_HELP_STRING([--without-kyotocabinet],[Don't use kyotocabinet even if it is available])) -AC_ARG_WITH(qdbm, AS_HELP_STRING([--without-qdbm],[Don't use qdbm even if it is available])) -AC_ARG_WITH(gdbm, AS_HELP_STRING([--without-gdbm],[Don't use gdbm even if it is available])) -AC_ARG_WITH(bdb, AS_HELP_STRING([--with-bdb@<:@=DIR@:>@],[Use BerkeleyDB4 if gdbm is not available])) -AC_ARG_WITH(lmdb, AS_HELP_STRING([--with-lmdb@<:@=DIR@:>@],[Use LMDB if gdbm is not available])) - -db_found=no +AC_ARG_WITH(gdbm, + AS_HELP_STRING( + [--with-gdbm@<:@=DIR@:>@], + [Use gdbm for the header cache (default)])) +AC_ARG_WITH(tokyocabinet, + AS_HELP_STRING( + [--with-tokyocabinet@<:@=DIR@:>@], + [Use tokyocabinet for the header cache])) +AC_ARG_WITH(kyotocabinet, + AS_HELP_STRING( + [--with-kyotocabinet@<:@=DIR@:>@], + [Use kyotocabinet for the header cache])) +AC_ARG_WITH(qdbm, + AS_HELP_STRING( + [--with-qdbm@<:@=DIR@:>@], + [Use qdbm for the header cache])) +AC_ARG_WITH(bdb, + AS_HELP_STRING( + [--with-bdb@<:@=DIR@:>@], + [Use BerkeleyDB for the header cache])) +AC_ARG_WITH(lmdb, + AS_HELP_STRING( + [--with-lmdb@<:@=DIR@:>@], + [Use LMDB for the header cache])) + if test x$enable_hcache = xyes then AC_DEFINE(USE_HCACHE, 1, [Enable header caching]) @@ -934,7 +950,7 @@ then fi if test -n "$with_kyotocabinet" && test "$with_kyotocabinet" != "no" then - if test "$db_requested" != "auto" + if test $db_requested != auto then AC_MSG_ERROR([more than one header cache engine requested.]) else @@ -943,7 +959,7 @@ then fi if test -n "$with_qdbm" && test "$with_qdbm" != "no" then - if test "$db_requested" != "auto" + if test $db_requested != auto then AC_MSG_ERROR([more than one header cache engine requested.]) else @@ -952,7 +968,7 @@ then fi if test -n "$with_gdbm" && test "$with_gdbm" != "no" then - if test "$db_requested" != "auto" + if test $db_requested != auto then AC_MSG_ERROR([more than one header cache engine requested.]) else @@ -961,7 +977,7 @@ then fi if test -n "$with_bdb" && test "$with_bdb" != "no" then - if test "$db_requested" != "auto" + if test $db_requested != auto then AC_MSG_ERROR([more than one header cache engine requested.]) else @@ -970,7 +986,7 @@ then fi if test -n "$with_lmdb" && test "$with_lmdb" != "no" then - if test "$db_requested" != "auto" + if test $db_requested != auto then AC_MSG_ERROR([more than one header cache engine requested.]) else @@ -979,8 +995,8 @@ then fi dnl -- Tokyo Cabinet -- - if test "$with_tokyocabinet" != "no" \ - && test "$db_requested" = auto -o "$db_requested" = TokyoCabinet + if test "$with_tokyocabinet" != "no" && test $hcache_db_used = no \ + && test $db_requested = auto -o $db_requested = TokyoCabinet then if test -n "$with_tokyocabinet" && test "$with_tokyocabinet" != "yes" then @@ -992,18 +1008,19 @@ then AC_CHECK_LIB(tokyocabinet, tcbdbopen, [MUTTLIBS="$MUTTLIBS -ltokyocabinet" AC_DEFINE(HAVE_TC, 1, [Tokyo Cabinet Support]) - db_found=TokyoCabinet], + MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS hcache-tc.o" + hcache_db_used=TokyoCabinet], [CPPFLAGS="$OLDCPPFLAGS" LDFLAGS="$OLDLDFLAGS"])) - if test "$db_requested" != auto && test "$db_found" != "$db_requested" + if test $db_requested != auto && test $hcache_db_used != $db_requested then AC_MSG_ERROR([Tokyo Cabinet could not be used. Check config.log for details.]) fi fi dnl -- Kyoto Cabinet -- - if test "$with_kyotocabinet" != "no" \ - && test "$db_requested" = auto -o "$db_requested" = KyotoCabinet + if test "$with_kyotocabinet" != "no" && test $hcache_db_used = no \ + && test $db_requested = auto -o $db_requested = KyotoCabinet then if test -n "$with_kyotocabinet" && test "$with_kyotocabinet" != "yes" then @@ -1015,51 +1032,54 @@ then AC_CHECK_LIB(kyotocabinet, kcdbopen, [MUTTLIBS="$MUTTLIBS -lkyotocabinet" AC_DEFINE(HAVE_KC, 1, [Kyoto Cabinet Support]) - db_found=KyotoCabinet], + MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS hcache-kc.o" + hcache_db_used=KyotoCabinet], [CPPFLAGS="$OLDCPPFLAGS" LDFLAGS="$OLDLDFLAGS"])) - if test "$db_requested" != auto && test "$db_found" != "$db_requested" + if test $db_requested != auto && test $hcache_db_used != $db_requested then AC_MSG_ERROR([Kyoto Cabinet could not be used. Check config.log for details.]) fi fi dnl -- QDBM -- - if test "$with_qdbm" != "no" && test $db_found = no \ - && test "$db_requested" = auto -o "$db_requested" = QDBM + if test "$with_qdbm" != "no" && test $hcache_db_used = no \ + && test $db_requested = auto -o $db_requested = QDBM then if test -n "$with_qdbm" && test "$with_qdbm" != "yes" then if test -d $with_qdbm/include/qdbm; then CPPFLAGS="$CPPFLAGS -I$with_qdbm/include/qdbm" - else + else CPPFLAGS="$CPPFLAGS -I$with_qdbm/include" - fi + fi LDFLAGS="$LDFLAGS -L$with_qdbm/lib" - else - if test -d /usr/include/qdbm; then - CPPFLAGS="$CPPFLAGS -I/usr/include/qdbm" - fi + else + if test -d /usr/include/qdbm; then + CPPFLAGS="$CPPFLAGS -I/usr/include/qdbm" + fi fi saved_LIBS="$LIBS" AC_CHECK_HEADERS(villa.h) AC_CHECK_LIB(qdbm, vlopen, [MUTTLIBS="$MUTTLIBS -lqdbm" + MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS hcache-qdbm.o" AC_DEFINE(HAVE_QDBM, 1, [QDBM Support]) - db_found=QDBM], + hcache_db_used=QDBM + ], [CPPFLAGS="$OLDCPPFLAGS" LDFLAGS="$OLDLDFLAGS"]) LIBS="$saved_LIBS" - if test "$db_requested" != auto && test "$db_found" != "$db_requested" + if test $db_requested != auto && test $hcache_db_used != $db_requested then AC_MSG_ERROR([QDBM could not be used. Check config.log for details.]) fi fi dnl -- GDBM -- - if test x$with_gdbm != xno && test $db_found = no \ - && test "$db_requested" = auto -o "$db_requested" = GDBM + if test "$with_gdbm" != "no" && test $hcache_db_used = no \ + && test $db_requested = auto -o $db_requested = GDBM then if test "$with_gdbm" != "yes" then @@ -1075,11 +1095,12 @@ then LIBS="$saved_LIBS" if test "$ac_cv_gdbmopen" = yes then - AC_DEFINE(HAVE_GDBM, 1, [GDBM Support]) MUTTLIBS="$MUTTLIBS -lgdbm" - db_found=GDBM + MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS hcache-gdbm.o" + AC_DEFINE(HAVE_GDBM, 1, [GDBM Support]) + hcache_db_used=GDBM fi - if test "$db_requested" != auto && test "$db_found" != "$db_requested" + if test $db_requested != auto && test $hcache_db_used != $db_requested then AC_MSG_ERROR([GDBM could not be used. Check config.log for details.]) fi @@ -1087,8 +1108,8 @@ then dnl -- BDB -- ac_bdb_prefix="$with_bdb" - if test x$with_bdb != xno && test $db_found = no \ - && test "$db_requested" = auto -o "$db_requested" = BDB + if test "$with_bdb" != "no" && test $hcache_db_used = no \ + && test $db_requested = auto -o $db_requested = BDB then if test x$ac_bdb_prefix = xyes || test x$ac_bdb_prefix = x then @@ -1136,19 +1157,24 @@ then then AC_MSG_RESULT(yes) CPPFLAGS="$OLDCPPFLAGS -I$BDB_INCLUDE_DIR" + AC_DEFINE(HAVE_BDB, 1, [Berkeley DB Support]) LIBS="$OLDLIBS -L$BDB_LIB_DIR -l$BDB_LIB" - AC_DEFINE(HAVE_DB4, 1, [Berkeley DB4 Support]) - db_found=BDB + MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS hcache-bdb.o" + hcache_db_used=BDB else AC_MSG_RESULT(no) fi + if test $db_requested != auto && test $hcache_db_used != $db_requested + then + AC_MSG_ERROR([BDB could not be used. Check config.log for details.]) + fi fi dnl -- LMDB -- - if test x$with_lmdb != xno && test $db_found = no \ - && test "$db_requested" = auto -o "$db_requested" = LMDB + if test "$with_lmdb" != "no" && test $hcache_db_used = no \ + && test $db_requested = auto -o $db_requested = LMDB then - if test "$with_lmdb" != "yes" + if test "$with_lmdb" != "yes" && test "$with_lmdb" != "NONE" then CPPFLAGS="$CPPFLAGS -I$with_lmdb/include" LDFLAGS="$LDFLAGS -L$with_lmdb/lib" @@ -1164,30 +1190,30 @@ then then AC_DEFINE(HAVE_LMDB, 1, [LMDB Support]) MUTTLIBS="$MUTTLIBS -llmdb" - db_found=LMDB + MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS hcache-lmdb.o" + hcache_db_used=LMDB fi - if test "$db_requested" != auto && test "$db_found" != "$db_requested" + if test $db_requested != auto && test $hcache_db_used != $db_requested then AC_MSG_ERROR([LMDB could not be used. Check config.log for details.]) fi fi - if test $db_found = no + if test $hcache_db_used = no then - AC_MSG_ERROR([You need Tokyo Cabinet, Kyoto Cabinet, QDBM, GDBM, Berkeley DB4 or LMDB for hcache]) + AC_MSG_ERROR([You need Tokyo Cabinet, Kyoto Cabinet, QDBM, GDBM, Berkeley DB or LMDB for hcache]) fi fi -hcache_db_used=$db_found dnl -- end cache -- -AM_CONDITIONAL(BUILD_HCACHE, test x$db_found != xno) +AM_CONDITIONAL(BUILD_HCACHE, test x$hcache_db_used != xno) if test "$need_md5" = "yes" then MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS md5.o" fi -if test x$db_found != xno ; then +if test x$hcache_db_used != xno ; then MUTT_MD5="mutt_md5$EXEEXT" fi AC_SUBST(MUTT_MD5) diff --git a/doc/makedoc-defs.h b/doc/makedoc-defs.h index ac6cd33e0..6bfbb7d64 100644 --- a/doc/makedoc-defs.h +++ b/doc/makedoc-defs.h @@ -37,8 +37,8 @@ # ifndef USE_HCACHE # define USE_HCACHE # endif -# ifndef HAVE_DB4 -# define HAVE_DB4 +# ifndef HAVE_BDB +# define HAVE_BDB # endif # ifndef HAVE_GDBM # define HAVE_GDBM diff --git a/globals.h b/globals.h index d49337d43..dcaa7433e 100644 --- a/globals.h +++ b/globals.h @@ -75,9 +75,9 @@ WHERE char *MessageCachedir; #endif #if USE_HCACHE WHERE char *HeaderCache; -#if HAVE_GDBM || HAVE_DB4 +#if HAVE_GDBM || HAVE_BDB WHERE char *HeaderCachePageSize; -#endif /* HAVE_GDBM || HAVE_DB4 */ +#endif /* HAVE_GDBM || HAVE_BDB */ #endif /* USE_HCACHE */ WHERE char *MhFlagged; WHERE char *MhReplied; diff --git a/hcache-bdb.c b/hcache-bdb.c new file mode 100644 index 000000000..575a5f87b --- /dev/null +++ b/hcache-bdb.c @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2004 Thomas Glanzmann + * Copyright (C) 2004 Tobias Werth + * Copyright (C) 2004 Brian Fundakowski Feldman + * Copyright (C) 2016 Pietro Cerutti + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#ifdef HAVE_BDB + +#include "hcache.h" +#include "globals.h" +#include "mx.h" +#include +#include +#include + +typedef struct +{ + DB_ENV *env; + DB *db; + int fd; + char lockfile[_POSIX_PATH_MAX]; +} hcache_db_ctx_t; + +static void +dbt_init(DBT *dbt, void *data, size_t len) +{ + dbt->data = data; + dbt->size = dbt->ulen = len; + dbt->dlen = dbt->doff = 0; + dbt->flags = DB_DBT_USERMEM; +} + +static void +dbt_empty_init(DBT *dbt) +{ + dbt->data = NULL; + dbt->size = dbt->ulen = dbt->dlen = dbt->doff = 0; + dbt->flags = 0; +} + +static void * +hcache_bdb_open(const char *path) +{ + struct stat sb; + int ret; + u_int32_t createflags = DB_CREATE; + int pagesize; + + hcache_db_ctx_t *ctx = safe_malloc(sizeof(hcache_db_ctx_t)); + + if (mutt_atoi (HeaderCachePageSize, &pagesize) < 0 || pagesize <= 0) + pagesize = 16384; + + snprintf (ctx->lockfile, _POSIX_PATH_MAX, "%s-lock-hack", path); + + ctx->fd = open (ctx->lockfile, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); + if (ctx->fd < 0) + { + FREE(&ctx); + return NULL; + } + + if (mx_lock_file (ctx->lockfile, ctx->fd, 1, 0, 5)) + goto fail_close; + + ret = db_env_create (&ctx->env, 0); + if (ret) + goto fail_unlock; + + ret = (*ctx->env->open)(ctx->env, NULL, DB_INIT_MPOOL | DB_CREATE | DB_PRIVATE, 0600); + if (ret) + goto fail_env; + + ctx->db = NULL; + ret = db_create (&ctx->db, ctx->env, 0); + if (ret) + goto fail_env; + + if (stat(path, &sb) != 0 && errno == ENOENT) + { + createflags |= DB_EXCL; + ctx->db->set_pagesize(ctx->db, pagesize); + } + + ret = (*ctx->db->open)(ctx->db, NULL, path, NULL, DB_BTREE, createflags, + 0600); + if (ret) + goto fail_db; + + return ctx; + +fail_db: + ctx->db->close (ctx->db, 0); +fail_env: + ctx->env->close (ctx->env, 0); +fail_unlock: + mx_unlock_file (ctx->lockfile, ctx->fd, 0); +fail_close: + close (ctx->fd); + unlink (ctx->lockfile); + FREE(&ctx); + + return NULL; +} + +static void * +hcache_bdb_fetch(void *vctx, const char *key, size_t keylen) +{ + DBT dkey; + DBT data; + + if (!vctx) + return NULL; + + hcache_db_ctx_t *ctx = vctx; + + dbt_init(&dkey, (void *) key, keylen); + dbt_empty_init(&data); + data.flags = DB_DBT_MALLOC; + + ctx->db->get(ctx->db, NULL, &dkey, &data, 0); + + return data.data; +} + +static int +hcache_bdb_store(void *vctx, const char *key, size_t keylen, void *data, size_t dlen) +{ + DBT dkey; + DBT databuf; + + if (!vctx) + return -1; + + hcache_db_ctx_t *ctx = vctx; + + dbt_init(&dkey, (void *) key, keylen); + dbt_empty_init(&databuf); + databuf.flags = DB_DBT_USERMEM; + databuf.data = data; + databuf.size = dlen; + databuf.ulen = dlen; + + return ctx->db->put(ctx->db, NULL, &dkey, &databuf, 0); +} + +static int +hcache_bdb_delete(void *vctx, const char *key, size_t keylen) +{ + DBT dkey; + + if (!vctx) + return -1; + + hcache_db_ctx_t *ctx = vctx; + + dbt_init(&dkey, (void *) key, keylen); + return ctx->db->del(ctx->db, NULL, &dkey, 0); +} + +static void +hcache_bdb_close(void **vctx) +{ + if (!vctx || !*vctx) + return; + + hcache_db_ctx_t *ctx = *vctx; + + ctx->db->close (ctx->db, 0); + ctx->env->close (ctx->env, 0); + mx_unlock_file (ctx->lockfile, ctx->fd, 0); + close (ctx->fd); + unlink (ctx->lockfile); + FREE(vctx); /* __FREE_CHECKED__ */ +} + +static const char * +hcache_bdb_backend(void) +{ + return DB_VERSION_STRING; +} + +HCACHE_BACKEND_OPS(bdb) + +#endif /* HAVE_BDB */ diff --git a/hcache-gdbm.c b/hcache-gdbm.c new file mode 100644 index 000000000..ae8a3a35b --- /dev/null +++ b/hcache-gdbm.c @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2004 Thomas Glanzmann + * Copyright (C) 2004 Tobias Werth + * Copyright (C) 2004 Brian Fundakowski Feldman + * Copyright (C) 2016 Pietro Cerutti + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#ifdef HAVE_GDBM + +#include "hcache.h" +#include "globals.h" +#include + +static void * +hcache_gdbm_open(const char *path) +{ + int pagesize; + + if (mutt_atoi (HeaderCachePageSize, &pagesize) < 0 || pagesize <= 0) + pagesize = 16384; + + GDBM_FILE db = gdbm_open((char *) path, pagesize, GDBM_WRCREAT, 00600, NULL); + if (db) + return db; + + /* if rw failed try ro */ + return gdbm_open((char *) path, pagesize, GDBM_READER, 00600, NULL); +} + +static void * +hcache_gdbm_fetch(void *ctx, const char *key, size_t keylen) +{ + datum dkey; + datum data; + + if (!ctx) + return NULL; + + GDBM_FILE db = ctx; + + dkey.dptr = (char *)key; + dkey.dsize = keylen; + data = gdbm_fetch(db, dkey); + return data.dptr; +} + +static int +hcache_gdbm_store(void *ctx, const char *key, size_t keylen, void *data, size_t dlen) +{ + datum dkey; + datum databuf; + + if (!ctx) + return -1; + + GDBM_FILE db = ctx; + + dkey.dptr = (char *)key; + dkey.dsize = keylen; + + databuf.dsize = dlen; + databuf.dptr = data; + + return gdbm_store(db, dkey, databuf, GDBM_REPLACE); +} + +static int +hcache_gdbm_delete(void *ctx, const char *key, size_t keylen) +{ + datum dkey; + + if (!ctx) + return -1; + + GDBM_FILE db = ctx; + + dkey.dptr = (char *)key; + dkey.dsize = keylen; + + return gdbm_delete(db, dkey); +} + +static void +hcache_gdbm_close(void **ctx) +{ + if (!ctx) + return; + + GDBM_FILE db = *ctx; + gdbm_close(db); +} + +static const char * +hcache_gdbm_backend(void) +{ + return gdbm_version; +} + +HCACHE_BACKEND_OPS(gdbm) + +#endif /* HAVE_GDBM */ diff --git a/hcache-kc.c b/hcache-kc.c new file mode 100644 index 000000000..bf5de3974 --- /dev/null +++ b/hcache-kc.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2004 Thomas Glanzmann + * Copyright (C) 2004 Tobias Werth + * Copyright (C) 2004 Brian Fundakowski Feldman + * Copyright (C) 2016 Pietro Cerutti + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#ifdef HAVE_KC + +#include "hcache.h" +#include + +static void * +hcache_kc_open(const char *path) +{ + char kcdbpath[_POSIX_PATH_MAX]; + int printfresult; + + printfresult = snprintf(kcdbpath, sizeof(kcdbpath), + "%s#type=kct#opts=%s#rcomp=lex", + path, option(OPTHCACHECOMPRESS) ? "lc" : "l"); + if ((printfresult < 0) || (printfresult >= sizeof(kcdbpath))) + { + return NULL; + } + + KCDB *db = kcdbnew(); + if (!db) + return NULL; + + if (kcdbopen(db, kcdbpath, KCOWRITER | KCOCREATE)) + return db; + else + { +#ifdef DEBUG + int ecode = kcdbecode (db); + dprint (2, (debugfile, "kcdbopen failed for %s: %s (ecode %d)\n", kcdbpath, kcdbemsg (db), ecode)); +#endif + kcdbdel(db); + return NULL; + } +} + +static void * +hcache_kc_fetch(void *ctx, const char *key, size_t keylen) +{ + size_t sp; + + if (!ctx) + return NULL; + + KCDB *db = ctx; + return kcdbget(db, key, keylen, &sp); +} + +static int +hcache_kc_store(void *ctx, const char* key, size_t keylen, void *data, size_t dlen) +{ + if (!ctx) + return -1; + + KCDB *db = ctx; + return kcdbset(db, key, keylen, data, dlen); +} + +static int +hcache_kc_delete(void *ctx, const char *key, size_t keylen) +{ + if (!ctx) + return -1; + + KCDB *db = ctx; + return kcdbremove(db, key, keylen); +} + +static void +hcache_kc_close(void **ctx) +{ + if (!ctx || !*ctx) + return; + + KCDB *db = *ctx; + if (!kcdbclose(db)) + { +#ifdef DEBUG + int ecode = kcdbecode (db); + dprint (2, (debugfile, "kcdbclose failed: %s (ecode %d)\n", kcdbemsg (db), ecode)); +#endif + } + kcdbdel(db); +} + +static const char * +hcache_kc_backend(void) +{ + /* SHORT_STRING(128) should be more than enough for KCVERSION */ + static char version_cache[SHORT_STRING] = ""; + if (!version_cache[0]) + snprintf(version_cache, sizeof(version_cache), "kyotocabinet %s", KCVERSION); + + return version_cache; +} + +HCACHE_BACKEND_OPS(kc) + +#endif /* HAVE_KC */ diff --git a/hcache-lmdb.c b/hcache-lmdb.c new file mode 100644 index 000000000..c2d727b57 --- /dev/null +++ b/hcache-lmdb.c @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2004 Thomas Glanzmann + * Copyright (C) 2004 Tobias Werth + * Copyright (C) 2004 Brian Fundakowski Feldman + * Copyright (C) 2016 Pietro Cerutti + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#ifdef HAVE_LMDB + +#include "hcache.h" + +#define LMDB_DB_SIZE (1024 * 1024 * 1024) +#include + +enum mdb_txn_mode +{ + txn_uninitialized = 0, + txn_read = 1 << 0, + txn_write = 1 << 1 +}; + +typedef struct +{ + MDB_env *env; + MDB_txn *txn; + MDB_dbi db; + enum mdb_txn_mode txn_mode; +} hcache_lmdb_ctx_t; + +static int +mdb_get_r_txn(hcache_lmdb_ctx_t *ctx) +{ + int rc; + + if (ctx->txn && (ctx->txn_mode & (txn_read | txn_write)) > 0) + return MDB_SUCCESS; + + if (ctx->txn) + rc = mdb_txn_renew(ctx->txn); + else + rc = mdb_txn_begin(ctx->env, NULL, MDB_RDONLY, &ctx->txn); + + if (rc == MDB_SUCCESS) + ctx->txn_mode = txn_read; + + return rc; +} + +static int +mdb_get_w_txn(hcache_lmdb_ctx_t *ctx) +{ + int rc; + + if (ctx->txn && (ctx->txn_mode == txn_write)) + return MDB_SUCCESS; + + if (ctx->txn) + { + if (ctx->txn_mode == txn_read) + mdb_txn_reset(ctx->txn); + ctx->txn = NULL; + } + + rc = mdb_txn_begin(ctx->env, NULL, 0, &ctx->txn); + if (rc == MDB_SUCCESS) + ctx->txn_mode = txn_write; + + return rc; +} + +static void * +hcache_lmdb_open(const char *path) +{ + int rc; + + hcache_lmdb_ctx_t *ctx = safe_malloc(sizeof(hcache_lmdb_ctx_t)); + ctx->txn = NULL; + ctx->db = 0; + + rc = mdb_env_create(&ctx->env); + if (rc != MDB_SUCCESS) + { + fprintf(stderr, "hcache_open_lmdb: mdb_env_create: %s", mdb_strerror(rc)); + return NULL; + } + + mdb_env_set_mapsize(ctx->env, LMDB_DB_SIZE); + + rc = mdb_env_open(ctx->env, path, MDB_NOSUBDIR, 0644); + if (rc != MDB_SUCCESS) + { + fprintf(stderr, "hcache_open_lmdb: mdb_env_open: %s", mdb_strerror(rc)); + goto fail_env; + } + + rc = mdb_get_r_txn(ctx); + if (rc != MDB_SUCCESS) + { + fprintf(stderr, "hcache_open_lmdb: mdb_txn_begin: %s", mdb_strerror(rc)); + goto fail_env; + } + + rc = mdb_dbi_open(ctx->txn, NULL, MDB_CREATE, &ctx->db); + if (rc != MDB_SUCCESS) + { + fprintf(stderr, "hcache_open_lmdb: mdb_dbi_open: %s", mdb_strerror(rc)); + goto fail_dbi; + } + + mdb_txn_reset(ctx->txn); + ctx->txn_mode = txn_uninitialized; + return ctx; + +fail_dbi: + mdb_txn_abort(ctx->txn); + ctx->txn_mode = txn_uninitialized; + ctx->txn = NULL; + +fail_env: + mdb_env_close(ctx->env); + FREE(&ctx); + return NULL; +} + +static void * +hcache_lmdb_fetch(void *vctx, const char *key, size_t keylen) +{ + MDB_val dkey; + MDB_val data; + int rc; + + if (!vctx) + return NULL; + + hcache_lmdb_ctx_t *ctx = vctx; + + dkey.mv_data = (void *)key; + dkey.mv_size = keylen; + data.mv_data = NULL; + data.mv_size = 0; + rc = mdb_get_r_txn(ctx); + if (rc != MDB_SUCCESS) + { + ctx->txn = NULL; + fprintf(stderr, "txn_renew: %s\n", mdb_strerror(rc)); + return NULL; + } + rc = mdb_get(ctx->txn, ctx->db, &dkey, &data); + if (rc == MDB_NOTFOUND) + { + return NULL; + } + if (rc != MDB_SUCCESS) + { + fprintf(stderr, "mdb_get: %s\n", mdb_strerror(rc)); + return NULL; + } + /* Caller frees the data we return, so I MUST make a copy of it */ + + char *d = safe_malloc(data.mv_size); + memcpy(d, data.mv_data, data.mv_size); + + return d; +} + +static int +hcache_lmdb_store(void *vctx, const char *key, size_t keylen, void *data, size_t dlen) +{ + MDB_val dkey; + MDB_val databuf; + int rc; + + if (!vctx) + return -1; + + hcache_lmdb_ctx_t *ctx = vctx; + + dkey.mv_data = (void *)key; + dkey.mv_size = keylen; + databuf.mv_data = data; + databuf.mv_size = dlen; + rc = mdb_get_w_txn(ctx); + if (rc != MDB_SUCCESS) + { + fprintf(stderr, "txn_begin: %s\n", mdb_strerror(rc)); + return rc; + } + rc = mdb_put(ctx->txn, ctx->db, &dkey, &databuf, 0); + if (rc != MDB_SUCCESS) + { + fprintf(stderr, "mdb_put: %s\n", mdb_strerror(rc)); + mdb_txn_abort(ctx->txn); + ctx->txn_mode = txn_uninitialized; + ctx->txn = NULL; + return rc; + } + return rc; +} + +static int +hcache_lmdb_delete(void *vctx, const char *key, size_t keylen) +{ + MDB_val dkey; + int rc; + + if (!vctx) + return -1; + + hcache_lmdb_ctx_t *ctx = vctx; + + dkey.mv_data = (void *)key; + dkey.mv_size = keylen; + rc = mdb_get_w_txn(ctx); + if (rc != MDB_SUCCESS) + { + fprintf(stderr, "txn_begin: %s\n", mdb_strerror(rc)); + return rc; + } + rc = mdb_del(ctx->txn, ctx->db, &dkey, NULL); + if (rc != MDB_SUCCESS) + { + if (rc != MDB_NOTFOUND) + { + fprintf(stderr, "mdb_del: %s\n", mdb_strerror(rc)); + mdb_txn_abort(ctx->txn); + ctx->txn_mode = txn_uninitialized; + ctx->txn = NULL; + } + return rc; + } + + return rc; +} + +static void +hcache_lmdb_close(void **vctx) +{ + if (!vctx || !*vctx) + return; + + hcache_lmdb_ctx_t *ctx = *vctx; + + if (ctx->txn && ctx->txn_mode == txn_write) + { + mdb_txn_commit(ctx->txn); + ctx->txn_mode = txn_uninitialized; + ctx->txn = NULL; + } + + mdb_env_close(ctx->env); + FREE(vctx); /* __FREE_CHECKED__ */ +} + +static const char * +hcache_lmdb_backend(void) +{ + return "lmdb " MDB_VERSION_STRING; +} + +HCACHE_BACKEND_OPS(lmdb) + +#endif /* HAVE_LMDB */ diff --git a/hcache-qdbm.c b/hcache-qdbm.c new file mode 100644 index 000000000..dd302ef8c --- /dev/null +++ b/hcache-qdbm.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2004 Thomas Glanzmann + * Copyright (C) 2004 Tobias Werth + * Copyright (C) 2004 Brian Fundakowski Feldman + * Copyright (C) 2016 Pietro Cerutti + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#ifdef HAVE_QDBM + +#include "hcache.h" +#include +#include +#include + +static void * +hcache_qdbm_open(const char *path) +{ + int flags = VL_OWRITER | VL_OCREAT; + + if (option(OPTHCACHECOMPRESS)) + flags |= VL_OZCOMP; + + return vlopen (path, flags, VL_CMPLEX); +} + +static void * +hcache_qdbm_fetch(void *ctx, const char *key, size_t keylen) +{ + if (!ctx) + return NULL; + + VILLA *db = ctx; + return vlget(db, key, keylen, NULL); +} + +static int +hcache_qdbm_store(void *ctx, const char *key, size_t keylen, void *data, size_t dlen) +{ + if (!ctx) + return -1; + + VILLA *db = ctx; + return vlput(db, key, keylen, data, dlen, VL_DOVER); +} + +static int +hcache_qdbm_delete(void *ctx, const char *key, size_t keylen) +{ + if (!ctx) + return -1; + + VILLA *db = ctx; + return vlout(db, key, keylen); +} + +static void +hcache_qdbm_close(void **ctx) +{ + if (!ctx || !*ctx) + return; + + VILLA *db = *ctx; + vlclose(db); +} + +static const char * +hcache_qdbm_backend(void) +{ + return "qdbm " _QDBM_VERSION; +} + +HCACHE_BACKEND_OPS(qdbm) + +#endif /* HAVE_LMDB */ diff --git a/hcache-tc.c b/hcache-tc.c new file mode 100644 index 000000000..10e43a8ba --- /dev/null +++ b/hcache-tc.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2004 Thomas Glanzmann + * Copyright (C) 2004 Tobias Werth + * Copyright (C) 2004 Brian Fundakowski Feldman + * Copyright (C) 2016 Pietro Cerutti + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#ifdef HAVE_TC + +#include "hcache.h" +#include + +static void * +hcache_tc_open(const char *path) +{ + TCBDB *db = tcbdbnew(); + if (!db) + return NULL; + if (option(OPTHCACHECOMPRESS)) + tcbdbtune(db, 0, 0, 0, -1, -1, BDBTDEFLATE); + if (tcbdbopen(db, path, BDBOWRITER | BDBOCREAT)) + return db; + else + { +#ifdef DEBUG + int ecode = tcbdbecode (db); + dprint (2, (debugfile, "tcbdbopen failed for %s: %s (ecode %d)\n", path, tcbdberrmsg (ecode), ecode)); +#endif + tcbdbdel(db); + return NULL; + } +} + +static void * +hcache_tc_fetch(void *ctx, const char *key, size_t keylen) +{ + int sp; + + if (!ctx) + return NULL; + + TCBDB *db = ctx; + return tcbdbget(db, key, keylen, &sp); +} + +static int +hcache_tc_store(void *ctx, const char *key, size_t keylen, void *data, size_t dlen) +{ + if (!ctx) + return -1; + + TCBDB *db = ctx; + return tcbdbput(db, key, keylen, data, dlen); +} + +static int +hcache_tc_delete(void *ctx, const char *key, size_t keylen) +{ + if (!ctx) + return -1; + + TCBDB *db = ctx; + return tcbdbout(db, key, keylen); +} + +static void +hcache_tc_close(void **ctx) +{ + if (!ctx || !*ctx) + return; + + TCBDB *db = *ctx; + if (!tcbdbclose(db)) + { +#ifdef DEBUG + int ecode = tcbdbecode (db); + dprint (2, (debugfile, "tcbdbclose failed: %s (ecode %d)\n", tcbdberrmsg (ecode), ecode)); +#endif + } + tcbdbdel(db); +} + +static const char * +hcache_tc_backend(void) +{ + return "tokyocabinet " _TC_VERSION; +} + +HCACHE_BACKEND_OPS(tc) + +#endif /* HAVE_TC */ diff --git a/hcache.c b/hcache.c index 870e05417..9946c2367 100644 --- a/hcache.c +++ b/hcache.c @@ -2,6 +2,7 @@ * Copyright (C) 2004 Thomas Glanzmann * Copyright (C) 2004 Tobias Werth * Copyright (C) 2004 Brian Fundakowski Feldman + * Copyright (C) 2016 Pietro Cerutti * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,146 +23,72 @@ #include "config.h" #endif /* HAVE_CONFIG_H */ -#if HAVE_QDBM -#include -#include -#include -#elif HAVE_TC -#include -#elif HAVE_KC -#include -#elif HAVE_GDBM -#include -#elif HAVE_DB4 -#include -#elif HAVE_LMDB -#define LMDB_DB_SIZE (1024 * 1024 * 1024) -#include +#if !(HAVE_TC || HAVE_KC || HAVE_GDBM || HAVE_BDB || HAVE_LMDB || HAVE_QDBM) +#error "No hcache backend defined" #endif #include -#include #if HAVE_SYS_TIME_H #include #endif -#include "mutt.h" #include "hcache.h" #include "hcversion.h" -#include "mx.h" -#include "lib.h" #include "md5.h" -#include "rfc822.h" -unsigned int hcachever = 0x0; +static unsigned int hcachever = 0x0; -#if HAVE_QDBM -struct header_cache -{ - VILLA *db; - char *folder; - unsigned int crc; -}; -#elif HAVE_TC -struct header_cache -{ - TCBDB *db; - char *folder; - unsigned int crc; -}; -#elif HAVE_KC -struct header_cache -{ - KCDB *db; - char *folder; - unsigned int crc; -}; -#elif HAVE_GDBM -struct header_cache -{ - GDBM_FILE db; - char *folder; - unsigned int crc; -}; -#elif HAVE_DB4 -struct header_cache -{ - DB_ENV *env; - DB *db; - char *folder; - unsigned int crc; - int fd; - char lockfile[_POSIX_PATH_MAX]; -}; - -static void mutt_hcache_dbt_init(DBT * dbt, void *data, size_t len); -static void mutt_hcache_dbt_empty_init(DBT * dbt); -#elif HAVE_LMDB -enum mdb_txn_mode -{ - txn_uninitialized = 0, - txn_read = 1 << 0, - txn_write = 1 << 1 -}; +/** + * header_cache_t - header cache structure. + * + * This struct holds both the backend-agnostic and the backend-specific parts + * of the header cache. Backend code MUST initialize the fetch, store, + * delete and close function pointers in hcache_open, and MAY store + * backend-specific context in the ctx pointer. + */ struct header_cache { - MDB_env *env; - MDB_txn *txn; - MDB_dbi db; char *folder; unsigned int crc; - enum mdb_txn_mode txn_mode; + void *ctx; }; -static int mdb_get_r_txn(header_cache_t *h) -{ - int rc; - - if (h->txn && (h->txn_mode & (txn_read | txn_write)) > 0) - return MDB_SUCCESS; - - if (h->txn) - rc = mdb_txn_renew(h->txn); - else - rc = mdb_txn_begin(h->env, NULL, MDB_RDONLY, &h->txn); - - if (rc == MDB_SUCCESS) - h->txn_mode = txn_read; - - return rc; -} - -static int mdb_get_w_txn(header_cache_t *h) -{ - int rc; - - if (h->txn && (h->txn_mode == txn_write)) - return MDB_SUCCESS; - - if (h->txn) - { - if (h->txn_mode == txn_read) - mdb_txn_reset(h->txn); - h->txn = NULL; - } - - rc = mdb_txn_begin(h->env, NULL, 0, &h->txn); - if (rc == MDB_SUCCESS) - h->txn_mode = txn_write; - - return rc; -} -#endif - typedef union { struct timeval timeval; unsigned int uidvalidity; } validate; +#define HCACHE_BACKEND(name) extern hcache_ops_t hcache_##name##_ops; +HCACHE_BACKEND_LIST +#undef HCACHE_BACKEND + +static hcache_ops_t * +hcache_get_ops(void) +{ + // TODO - switch to run-time config + return +#if defined(HAVE_BDB) + &hcache_bdb_ops +#elif defined(HAVE_GDBM) + &hcache_gdbm_ops +#elif defined(HAVE_KC) + &hcache_kc_ops +#elif defined(HAVE_LMDB) + &hcache_lmdb_ops +#elif defined(HAVE_QDBM) + &hcache_qdbm_ops +#elif defined(HAVE_TC) + &hcache_tc_ops +#else + NULL +#endif + ; +} + static void * lazy_malloc(size_t siz) { - if (0 < siz && siz < 4096) + if (siz < 4096) siz = 4096; return safe_malloc(siz); @@ -172,7 +99,7 @@ lazy_realloc(void *ptr, size_t siz) { void **p = (void **) ptr; - if (p != NULL && 0 < siz && siz < 4096) + if (p != NULL && siz < 4096) return; safe_realloc(ptr, siz); @@ -683,7 +610,7 @@ mutt_hcache_per_folder(const char *path, const char *folder, */ static void * mutt_hcache_dump(header_cache_t *h, HEADER * header, int *off, - unsigned int uidvalidity, mutt_hcache_store_flags_t flags) + unsigned int uidvalidity) { unsigned char *d = NULL; HEADER nh; @@ -692,7 +619,7 @@ mutt_hcache_dump(header_cache_t *h, HEADER * header, int *off, *off = 0; d = lazy_malloc(sizeof (validate)); - if (flags & MUTT_GENERATE_UIDVALIDITY) + if (uidvalidity == 0) { struct timeval now; gettimeofday(&now, NULL); @@ -741,7 +668,7 @@ mutt_hcache_dump(header_cache_t *h, HEADER * header, int *off, } HEADER * -mutt_hcache_restore(const unsigned char *d, HEADER ** oh) +mutt_hcache_restore(const unsigned char *d) { int off = 0; HEADER *h = mutt_new_header(); @@ -764,252 +691,9 @@ mutt_hcache_restore(const unsigned char *d, HEADER ** oh) restore_char(&h->maildir_flags, d, &off, convert); - /* this is needed for maildir style mailboxes */ - if (oh) - { - h->old = (*oh)->old; - h->path = safe_strdup((*oh)->path); - mutt_free_header(oh); - } - return h; } -void * -mutt_hcache_fetch(header_cache_t *h, const char *filename, - size_t(*keylen) (const char *fn)) -{ - void* data; - - data = mutt_hcache_fetch_raw (h, filename, keylen); - - if (!data || !crc_matches(data, h->crc)) - { - FREE(&data); - return NULL; - } - - return data; -} - -void * -mutt_hcache_fetch_raw (header_cache_t *h, const char *filename, - size_t(*keylen) (const char *fn)) -{ -#ifndef HAVE_DB4 - char path[_POSIX_PATH_MAX]; - int ksize; -#endif -#ifdef HAVE_QDBM - char *data = NULL; -#elif HAVE_TC - void *data; - int sp; -#elif HAVE_KC - void *data; - size_t sp; -#elif HAVE_GDBM - datum key; - datum data; -#elif HAVE_DB4 - DBT key; - DBT data; -#elif HAVE_LMDB - MDB_val key; - MDB_val data; - size_t folderlen; - int rc; -#endif - - if (!h) - return NULL; - -#ifdef HAVE_DB4 - if (filename[0] == '/') - filename++; - - mutt_hcache_dbt_init(&key, (void *) filename, keylen(filename)); - mutt_hcache_dbt_empty_init(&data); - data.flags = DB_DBT_MALLOC; - - h->db->get(h->db, NULL, &key, &data, 0); - - return data.data; -#elif HAVE_LMDB - strncpy(path, h->folder, sizeof (path)); - safe_strcat(path, sizeof (path), filename); - - folderlen = strlen(h->folder); - ksize = folderlen + keylen(path + folderlen); - key.mv_data = (char *)path; - key.mv_size = ksize; - data.mv_data = NULL; - data.mv_size = 0; - rc = mdb_get_r_txn(h); - if (rc != MDB_SUCCESS) - { - h->txn = NULL; - fprintf(stderr, "txn_renew: %s\n", mdb_strerror(rc)); - return NULL; - } - rc = mdb_get(h->txn, h->db, &key, &data); - if (rc == MDB_NOTFOUND) - { - return NULL; - } - if (rc != MDB_SUCCESS) - { - fprintf(stderr, "mdb_get: %s\n", mdb_strerror(rc)); - return NULL; - } - /* Caller frees the data we return, so I MUST make a copy of it */ - - char *d = safe_malloc(data.mv_size); - memcpy(d, data.mv_data, data.mv_size); - - return d; - -#else - strncpy(path, h->folder, sizeof (path)); - safe_strcat(path, sizeof (path), filename); - - ksize = strlen (h->folder) + keylen (path + strlen (h->folder)); -#endif -#ifdef HAVE_QDBM - data = vlget(h->db, path, ksize, NULL); - - return data; -#elif HAVE_TC - data = tcbdbget(h->db, path, ksize, &sp); - - return data; -#elif HAVE_KC - data = kcdbget(h->db, path, ksize, &sp); - - return data; -#elif HAVE_GDBM - key.dptr = path; - key.dsize = ksize; - - data = gdbm_fetch(h->db, key); - - return data.dptr; -#endif -} - -/* - * flags - * - * MUTT_GENERATE_UIDVALIDITY - * ignore uidvalidity param and store gettimeofday() as the value - */ -int -mutt_hcache_store(header_cache_t *h, const char *filename, HEADER * header, - unsigned int uidvalidity, - size_t(*keylen) (const char *fn), - mutt_hcache_store_flags_t flags) -{ - char* data; - int dlen; - int ret; - - if (!h) - return -1; - - data = mutt_hcache_dump(h, header, &dlen, uidvalidity, flags); - ret = mutt_hcache_store_raw (h, filename, data, dlen, keylen); - - FREE(&data); - - return ret; -} - -int -mutt_hcache_store_raw (header_cache_t* h, const char* filename, void* data, - size_t dlen, size_t(*keylen) (const char* fn)) -{ -#ifndef HAVE_DB4 - char path[_POSIX_PATH_MAX]; - int ksize; -#endif -#if HAVE_GDBM - datum key; - datum databuf; -#elif HAVE_DB4 - DBT key; - DBT databuf; -#elif HAVE_LMDB - MDB_val key; - MDB_val databuf; - size_t folderlen; - int rc; -#endif - - if (!h) - return -1; - -#if HAVE_DB4 - if (filename[0] == '/') - filename++; - - mutt_hcache_dbt_init(&key, (void *) filename, keylen(filename)); - - mutt_hcache_dbt_empty_init(&databuf); - databuf.flags = DB_DBT_USERMEM; - databuf.data = data; - databuf.size = dlen; - databuf.ulen = dlen; - - return h->db->put(h->db, NULL, &key, &databuf, 0); -#elif HAVE_LMDB - folderlen = strlen(h->folder); - strncpy(path, h->folder, sizeof (path)); - safe_strcat(path, sizeof (path), filename); - ksize = folderlen + keylen(path + folderlen); - - key.mv_data = (char *)path; - key.mv_size = ksize; - databuf.mv_data = data; - databuf.mv_size = dlen; - rc = mdb_get_w_txn(h); - if (rc != MDB_SUCCESS) - { - fprintf(stderr, "txn_begin: %s\n", mdb_strerror(rc)); - return rc; - } - rc = mdb_put(h->txn, h->db, &key, &databuf, 0); - if (rc != MDB_SUCCESS) - { - fprintf(stderr, "mdb_put: %s\n", mdb_strerror(rc)); - mdb_txn_abort(h->txn); - h->txn_mode = txn_uninitialized; - h->txn = NULL; - return rc; - } - return rc; -#else - strncpy(path, h->folder, sizeof (path)); - safe_strcat(path, sizeof (path), filename); - - ksize = strlen(h->folder) + keylen(path + strlen(h->folder)); -#endif -#if HAVE_QDBM - return vlput(h->db, path, ksize, data, dlen, VL_DOVER); -#elif HAVE_TC - return tcbdbput(h->db, path, ksize, data, dlen); -#elif HAVE_KC - return kcdbset(h->db, path, ksize, data, dlen); -#elif HAVE_GDBM - key.dptr = path; - key.dsize = ksize; - - databuf.dsize = dlen; - databuf.dptr = data; - - return gdbm_store(h->db, key, databuf, GDBM_REPLACE); -#endif -} - static char* get_foldername(const char *folder) { char *p = NULL; @@ -1031,464 +715,15 @@ static char* get_foldername(const char *folder) return p; } -#if HAVE_QDBM -static int -hcache_open_qdbm (struct header_cache* h, const char* path) -{ - int flags = VL_OWRITER | VL_OCREAT; - - if (option(OPTHCACHECOMPRESS)) - flags |= VL_OZCOMP; - - h->db = vlopen (path, flags, VL_CMPLEX); - if (h->db) - return 0; - else - return -1; -} - -void -mutt_hcache_close(header_cache_t *h) -{ - if (!h) - return; - - vlclose(h->db); - FREE(&h->folder); - FREE(&h); -} - -int -mutt_hcache_delete(header_cache_t *h, const char *filename, - size_t(*keylen) (const char *fn)) -{ - char path[_POSIX_PATH_MAX]; - int ksize; - - if (!h) - return -1; - - strncpy(path, h->folder, sizeof (path)); - safe_strcat(path, sizeof (path), filename); - - ksize = strlen(h->folder) + keylen(path + strlen(h->folder)); - - return vlout(h->db, path, ksize); -} - -#elif HAVE_TC -static int -hcache_open_tc (struct header_cache* h, const char* path) -{ - h->db = tcbdbnew(); - if (!h->db) - return -1; - if (option(OPTHCACHECOMPRESS)) - tcbdbtune(h->db, 0, 0, 0, -1, -1, BDBTDEFLATE); - if (tcbdbopen(h->db, path, BDBOWRITER | BDBOCREAT)) - return 0; - else - { -#ifdef DEBUG - int ecode = tcbdbecode (h->db); - dprint (2, (debugfile, "tcbdbopen failed for %s: %s (ecode %d)\n", path, tcbdberrmsg (ecode), ecode)); -#endif - tcbdbdel(h->db); - return -1; - } -} - -void -mutt_hcache_close(header_cache_t *h) -{ - if (!h) - return; - - if (!tcbdbclose(h->db)) - { -#ifdef DEBUG - int ecode = tcbdbecode (h->db); - dprint (2, (debugfile, "tcbdbclose failed for %s: %s (ecode %d)\n", h->folder, tcbdberrmsg (ecode), ecode)); -#endif - } - tcbdbdel(h->db); - FREE(&h->folder); - FREE(&h); -} - -int -mutt_hcache_delete(header_cache_t *h, const char *filename, - size_t(*keylen) (const char *fn)) -{ - char path[_POSIX_PATH_MAX]; - int ksize; - - if (!h) - return -1; - - strncpy(path, h->folder, sizeof (path)); - safe_strcat(path, sizeof (path), filename); - - ksize = strlen(h->folder) + keylen(path + strlen(h->folder)); - - return tcbdbout(h->db, path, ksize); -} - -#elif HAVE_KC -static int -hcache_open_kc (struct header_cache *h, const char *path) -{ - char kcdbpath[_POSIX_PATH_MAX]; - int printfresult; - - printfresult = snprintf(kcdbpath, sizeof(kcdbpath), - "%s#type=kct#opts=%s#rcomp=lex", - path, option(OPTHCACHECOMPRESS) ? "lc" : "l"); - if ((printfresult < 0) || (printfresult >= sizeof(kcdbpath))) - { - return -1; - } - - h->db = kcdbnew(); - if (!h->db) - return -1; - - if (kcdbopen(h->db, kcdbpath, KCOWRITER | KCOCREATE)) - return 0; - else - { -#ifdef DEBUG - int ecode = kcdbecode (h->db); - dprint (2, (debugfile, "kcdbopen failed for %s: %s (ecode %d)\n", kcdbpath, kcdbemsg (h->db), ecode)); -#endif - kcdbdel(h->db); - return -1; - } -} - -void -mutt_hcache_close(header_cache_t *h) -{ - if (!h) - return; - - if (!kcdbclose(h->db)) - { -#ifdef DEBUG - int ecode = kcdbecode (h->db); - dprint (2, (debugfile, "kcdbclose failed for %s: %s (ecode %d)\n", h->folder, kcdbemsg (h->db), ecode)); -#endif - } - kcdbdel(h->db); - FREE(&h->folder); - FREE(&h); -} - -int -mutt_hcache_delete(header_cache_t *h, const char *filename, - size_t(*keylen) (const char *fn)) -{ - char path[_POSIX_PATH_MAX]; - int ksize; - - if (!h) - return -1; - - strncpy(path, h->folder, sizeof (path)); - safe_strcat(path, sizeof (path), filename); - - ksize = strlen(h->folder) + keylen(path + strlen(h->folder)); - - return kcdbremove(h->db, path, ksize); -} - -#elif HAVE_GDBM -static int -hcache_open_gdbm (struct header_cache* h, const char* path) -{ - int pagesize; - - if (mutt_atoi (HeaderCachePageSize, &pagesize) < 0 || pagesize <= 0) - pagesize = 16384; - - h->db = gdbm_open((char *) path, pagesize, GDBM_WRCREAT, 00600, NULL); - if (h->db) - return 0; - - /* if rw failed try ro */ - h->db = gdbm_open((char *) path, pagesize, GDBM_READER, 00600, NULL); - if (h->db) - return 0; - - return -1; -} - -void -mutt_hcache_close(header_cache_t *h) -{ - if (!h) - return; - - gdbm_close(h->db); - FREE(&h->folder); - FREE(&h); -} - -int -mutt_hcache_delete(header_cache_t *h, const char *filename, - size_t(*keylen) (const char *fn)) -{ - datum key; - char path[_POSIX_PATH_MAX]; - - if (!h) - return -1; - - strncpy(path, h->folder, sizeof (path)); - safe_strcat(path, sizeof (path), filename); - - key.dptr = path; - key.dsize = strlen(h->folder) + keylen(path + strlen(h->folder)); - - return gdbm_delete(h->db, key); -} -#elif HAVE_DB4 - -static void -mutt_hcache_dbt_init(DBT * dbt, void *data, size_t len) -{ - dbt->data = data; - dbt->size = dbt->ulen = len; - dbt->dlen = dbt->doff = 0; - dbt->flags = DB_DBT_USERMEM; -} - -static void -mutt_hcache_dbt_empty_init(DBT * dbt) -{ - dbt->data = NULL; - dbt->size = dbt->ulen = dbt->dlen = dbt->doff = 0; - dbt->flags = 0; -} - -static int -hcache_open_db4 (struct header_cache* h, const char* path) -{ - struct stat sb; - int ret; - u_int32_t createflags = DB_CREATE; - int pagesize; - - if (mutt_atoi (HeaderCachePageSize, &pagesize) < 0 || pagesize <= 0) - pagesize = 16384; - - snprintf (h->lockfile, _POSIX_PATH_MAX, "%s-lock-hack", path); - - h->fd = open (h->lockfile, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); - if (h->fd < 0) - return -1; - - if (mx_lock_file (h->lockfile, h->fd, 1, 0, 5)) - goto fail_close; - - ret = db_env_create (&h->env, 0); - if (ret) - goto fail_unlock; - - ret = (*h->env->open)(h->env, NULL, DB_INIT_MPOOL | DB_CREATE | DB_PRIVATE, - 0600); - if (ret) - goto fail_env; - - ret = db_create (&h->db, h->env, 0); - if (ret) - goto fail_env; - - if (stat(path, &sb) != 0 && errno == ENOENT) - { - createflags |= DB_EXCL; - h->db->set_pagesize(h->db, pagesize); - } - - ret = (*h->db->open)(h->db, NULL, path, h->folder, DB_BTREE, createflags, - 0600); - if (ret) - goto fail_db; - - return 0; - - fail_db: - h->db->close (h->db, 0); - fail_env: - h->env->close (h->env, 0); - fail_unlock: - mx_unlock_file (h->lockfile, h->fd, 0); - fail_close: - close (h->fd); - unlink (h->lockfile); - - return -1; -} - -void -mutt_hcache_close(header_cache_t *h) -{ - if (!h) - return; - - h->db->close (h->db, 0); - h->env->close (h->env, 0); - mx_unlock_file (h->lockfile, h->fd, 0); - close (h->fd); - unlink (h->lockfile); - FREE (&h->folder); - FREE (&h); -} - -int -mutt_hcache_delete(header_cache_t *h, const char *filename, - size_t(*keylen) (const char *fn)) -{ - DBT key; - - if (!h) - return -1; - - if (filename[0] == '/') - filename++; - - mutt_hcache_dbt_init(&key, (void *) filename, keylen(filename)); - return h->db->del(h->db, NULL, &key, 0); -} -#elif HAVE_LMDB - -static int -hcache_open_lmdb (struct header_cache* h, const char* path) -{ - int rc; - - h->txn = NULL; - - rc = mdb_env_create(&h->env); - if (rc != MDB_SUCCESS) - { - fprintf(stderr, "hcache_open_lmdb: mdb_env_create: %s", mdb_strerror(rc)); - return -1; - } - - mdb_env_set_mapsize(h->env, LMDB_DB_SIZE); - - rc = mdb_env_open(h->env, path, MDB_NOSUBDIR, 0644); - if (rc != MDB_SUCCESS) - { - fprintf(stderr, "hcache_open_lmdb: mdb_env_open: %s", mdb_strerror(rc)); - goto fail_env; - } - - rc = mdb_get_r_txn(h); - if (rc != MDB_SUCCESS) - { - fprintf(stderr, "hcache_open_lmdb: mdb_txn_begin: %s", mdb_strerror(rc)); - goto fail_env; - } - - rc = mdb_dbi_open(h->txn, NULL, MDB_CREATE, &h->db); - if (rc != MDB_SUCCESS) - { - fprintf(stderr, "hcache_open_lmdb: mdb_dbi_open: %s", mdb_strerror(rc)); - goto fail_dbi; - } - - mdb_txn_reset(h->txn); - h->txn_mode = txn_uninitialized; - return 0; - -fail_dbi: - mdb_txn_abort(h->txn); - h->txn_mode = txn_uninitialized; - h->txn = NULL; - -fail_env: - mdb_env_close(h->env); - return -1; -} - -void -mutt_hcache_close(header_cache_t *h) -{ - if (!h) - return; - - if (h->txn && h->txn_mode == txn_write) - { - mdb_txn_commit(h->txn); - h->txn_mode = txn_uninitialized; - h->txn = NULL; - } - - mdb_env_close(h->env); - FREE (&h->folder); - FREE (&h); -} - -int -mutt_hcache_delete(header_cache_t *h, const char *filename, - size_t(*keylen) (const char *fn)) -{ - MDB_val key; - int rc; - - if (!h) - return -1; - - if (filename[0] == '/') - filename++; - - key.mv_data = (char *)filename; - key.mv_size = strlen(filename); - rc = mdb_get_w_txn(h); - if (rc != MDB_SUCCESS) - { - fprintf(stderr, "txn_begin: %s\n", mdb_strerror(rc)); - return rc; - } - rc = mdb_del(h->txn, h->db, &key, NULL); - if (rc != MDB_SUCCESS) - { - if (rc != MDB_NOTFOUND) - { - fprintf(stderr, "mdb_del: %s\n", mdb_strerror(rc)); - mdb_txn_abort(h->txn); - h->txn_mode = txn_uninitialized; - h->txn = NULL; - } - return rc; - } - - return rc; -} -#endif - header_cache_t * mutt_hcache_open(const char *path, const char *folder, hcache_namer_t namer) { - struct header_cache *h = safe_calloc(1, sizeof (struct header_cache)); - int (*hcache_open) (struct header_cache* h, const char* path); + hcache_ops_t *ops = hcache_get_ops(); + header_cache_t *h = safe_calloc(1, sizeof (header_cache_t)); struct stat sb; -#if HAVE_QDBM - hcache_open = hcache_open_qdbm; -#elif HAVE_TC - hcache_open = hcache_open_tc; -#elif HAVE_KC - hcache_open = hcache_open_kc; -#elif HAVE_GDBM - hcache_open = hcache_open_gdbm; -#elif HAVE_DB4 - hcache_open = hcache_open_db4; -#elif HAVE_LMDB - hcache_open = hcache_open_lmdb; -#endif + if (!ops) + return NULL; /* Calculate the current hcache version from dynamic configuration */ if (hcachever == 0x0) { @@ -1525,11 +760,6 @@ mutt_hcache_open(const char *path, const char *folder, hcache_namer_t namer) hcachever = digest.intval; } -#if HAVE_LMDB - h->db = 0; -#else - h->db = NULL; -#endif h->folder = get_foldername(folder); h->crc = hcachever; @@ -1542,14 +772,16 @@ mutt_hcache_open(const char *path, const char *folder, hcache_namer_t namer) path = mutt_hcache_per_folder(path, h->folder, namer); - if (!hcache_open (h, path)) + h->ctx = ops->open(path); + if (h->ctx) return h; else { /* remove a possibly incompatible version */ if (!stat (path, &sb) && !unlink (path)) { - if (!hcache_open (h, path)) + h->ctx = ops->open(path); + if (h->ctx) return h; } FREE(&h->folder); @@ -1559,39 +791,100 @@ mutt_hcache_open(const char *path, const char *folder, hcache_namer_t namer) } } -#if HAVE_DB4 -const char *mutt_hcache_backend (void) +void mutt_hcache_close(header_cache_t *h) { - return DB_VERSION_STRING; + hcache_ops_t *ops = hcache_get_ops(); + if (!h || !ops) + return; + + ops->close(&h->ctx); + FREE (&h->folder); + FREE (&h); } -#elif HAVE_LMDB -const char *mutt_hcache_backend (void) + +void * +mutt_hcache_fetch(header_cache_t *h, const char *key, size_t keylen) { - return "lmdb " MDB_VERSION_STRING; + void* data; + + data = mutt_hcache_fetch_raw (h, key, keylen); + + if (!data || !crc_matches(data, h->crc)) + { + FREE(&data); + return NULL; + } + + return data; } -#elif HAVE_GDBM -const char *mutt_hcache_backend (void) + +void * +mutt_hcache_fetch_raw(header_cache_t *h, const char *key, size_t keylen) { - return gdbm_version; + char path[_POSIX_PATH_MAX]; + hcache_ops_t *ops = hcache_get_ops(); + + if (!h || !ops) + return NULL; + + keylen = snprintf(path, sizeof(path), "%s%s", h->folder, key); + + return ops->fetch(h->ctx, path, keylen); } -#elif HAVE_QDBM -const char *mutt_hcache_backend (void) + +int +mutt_hcache_store(header_cache_t *h, const char *key, size_t keylen, + HEADER * header, unsigned int uidvalidity) { - return "qdbm " _QDBM_VERSION; + char* data; + int dlen; + int ret; + + if (!h) + return -1; + + data = mutt_hcache_dump(h, header, &dlen, uidvalidity); + ret = mutt_hcache_store_raw (h, key, keylen, data, dlen); + + FREE(&data); + + return ret; } -#elif HAVE_TC -const char *mutt_hcache_backend (void) + +int +mutt_hcache_store_raw(header_cache_t *h, const char* key, size_t keylen, + void* data, size_t dlen) { - return "tokyocabinet " _TC_VERSION; + char path[_POSIX_PATH_MAX]; + hcache_ops_t *ops = hcache_get_ops(); + + if (!h || !ops) + return -1; + + keylen = snprintf(path, sizeof(path), "%s%s", h->folder, key); + + return ops->store(h->ctx, path, keylen, data, dlen); } -#elif HAVE_KC -const char *mutt_hcache_backend (void) + +int +mutt_hcache_delete(header_cache_t *h, const char *key, size_t keylen) { - /* SHORT_STRING(128) should be more than enough for KCVERSION */ - static char version_cache[SHORT_STRING] = ""; - if (!version_cache[0]) - snprintf(version_cache, sizeof(version_cache), "kyotocabinet %s", KCVERSION); + char path[_POSIX_PATH_MAX]; + hcache_ops_t *ops = hcache_get_ops(); - return version_cache; + if (!h) + return -1; + + keylen = snprintf(path, sizeof(path), "%s%s", h->folder, key); + + return ops->delete(h->ctx, path, keylen); +} + +const char * +mutt_hcache_backend() +{ + hcache_ops_t *ops = hcache_get_ops(); + if (!ops) + return NULL; + return ops->backend(); } -#endif diff --git a/hcache.h b/hcache.h index ca0b425e2..23786d90f 100644 --- a/hcache.h +++ b/hcache.h @@ -2,6 +2,7 @@ * Copyright (C) 2004 Thomas Glanzmann * Copyright (C) 2004 Tobias Werth * Copyright (C) 2004 Brian Fundakowski Feldman + * Copyright (C) 2016 Pietro Cerutti * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,30 +22,209 @@ #ifndef _HCACHE_H_ #define _HCACHE_H_ 1 +#include "mutt.h" + struct header_cache; typedef struct header_cache header_cache_t; +/** + * hcache_open_t - backend-specific routing to open the header cache database. + * + * @param path The path to the database file. + * @return Pointer to backend-specific context on success, NULL otherwise. + * + * The hcache_open function has the purpose of opening a backend-specific + * connection to the database file specified by the path parameter. Backends + * MUST return non-NULL specific context informations on success. This will be + * stored in the ctx member of the header_cache_t structure and passed on to + * all other backend-specific functions (see below). + */ +typedef void * (*hcache_open_t)(const char *path); + +/** + * hcache_fetch_t - backend-specific routine to fetch a message's headers. + * + * @param ctx The backend-specific context retrieved via hcache_open. + * @param key A message identification string. + * @param keylen The length of the string pointed to by key. + * @return Pointer to the message's headers on success, NULL otherwise. + */ +typedef void * (*hcache_fetch_t)(void *ctx, const char *key, size_t keylen); + +/** + * hcache_store_t - backend-specific routine to store a message's headers. + * + * @param ctx The backend-specific context retrieved via hcache_open. + * @param key A message identification string. + * @param keylen The length of the string pointed to by key. + * @param data The message headers data. + * @param datalen The length of the string pointed to by data. + * @return 0 on success, a backend-specific error code otherwise. + */ +typedef int (*hcache_store_t)(void *ctx, const char *key, size_t keylen, + void *data, size_t datalen); + +/** + * hcache_delete_t - backend-specific routine to delete a message's headers. + * + * @param ctx The backend-specific context retrieved via hcache_open. + * @param key A message identification string. + * @param keylen The length of the string pointed to by key. + * @return 0 on success, a backend-specific error code otherwise. + */ +typedef int (*hcache_delete_t)(void *ctx, const char *key, size_t keylen); + +/** + * hcache_close_t - backend-specific routine to close a context. + * + * @param ctx The backend-specific context retrieved via hcache_open. + * + * Backend code is responsible for freeing any resources associated with the + * @ctx parameter. For this reason, backend code is passed a pointer-to-pointer + * to the context, so that FREE can be invoked on it. + */ +typedef void (*hcache_close_t)(void **ctx); + +/** + * hcache_backend_t - backend-specific identification string. + * + * @return String describing the currently used hcache backend. + */ +typedef const char *(*hcache_backend_t)(void); + +typedef struct +{ + hcache_open_t open; + hcache_fetch_t fetch; + hcache_store_t store; + hcache_delete_t delete; + hcache_close_t close; + hcache_backend_t backend; +} hcache_ops_t; + typedef int (*hcache_namer_t)(const char* path, char* dest, size_t dlen); -header_cache_t *mutt_hcache_open(const char *path, const char *folder, - hcache_namer_t namer); -void mutt_hcache_close(header_cache_t *h); -HEADER *mutt_hcache_restore(const unsigned char *d, HEADER **oh); -void *mutt_hcache_fetch(header_cache_t *h, const char *filename, size_t (*keylen)(const char *fn)); -void *mutt_hcache_fetch_raw (header_cache_t *h, const char *filename, - size_t (*keylen)(const char *fn)); - -typedef enum { - MUTT_GENERATE_UIDVALIDITY = 1 /* use gettimeofday() as value */ -} mutt_hcache_store_flags_t; - -/* uidvalidity is an IMAP-specific unsigned 32 bit number */ -int mutt_hcache_store(header_cache_t *h, const char *filename, HEADER *header, - unsigned int uidvalidity, size_t (*keylen)(const char *fn), mutt_hcache_store_flags_t flags_t); -int mutt_hcache_store_raw (header_cache_t *h, const char* filename, void* data, - size_t dlen, size_t(*keylen) (const char* fn)); -int mutt_hcache_delete(header_cache_t *h, const char *filename, size_t (*keylen)(const char *fn)); - -const char *mutt_hcache_backend (void); +/** + * mutt_hcache_open - open the connection to the header cache. + * + * @param path Location of the header cache (often as specified by the user). + * @param folder Name of the folder containing the messages. + * @param namer Optional (might be NULL) client-specific function to form the + * final name of the hcache database file. + * @return Pointer to a header_cache_t struct on success, NULL otherwise. + */ +header_cache_t * +mutt_hcache_open(const char *path, const char *folder, hcache_namer_t namer); + +/** + * mutt_hcache_close - close the connection to the header cache. + * + * @param h Pointer to the header_cache_t structure got by mutt_hcache_open. + */ +void +mutt_hcache_close(header_cache_t *h); + +/** + * mutt_hcache_fetch - fetch and validate a message's header from the cache. + * + * @param h Pointer to the header_cache_t structure got by mutt_hcache_open. + * @param key Message identification string. + * @param keylen Length of the string pointed to by key. + * @return Pointer to the data if found and valid, NULL otherwise. + * @note This function performs a check on the validity of the data found by + * comparing it with the crc value of the header_cache_t structure. + */ +void * +mutt_hcache_fetch(header_cache_t *h, const char *key, size_t keylen); + +/** + * mutt_hcache_fetch_raw - fetch a message's header from the cache. + * + * @param h Pointer to the header_cache_t structure got by mutt_hcache_open. + * @param key Message identification string. + * @param keylen Length of the string pointed to by key. + * @return Pointer to the data if found, NULL otherwise. + * @note This function does not perform any check on the validity of the data + * found. + */ +void * +mutt_hcache_fetch_raw(header_cache_t *h, const char *key, size_t keylen); + +/** + * mutt_hcache_restore - restore a HEADER from data retrieved from the cache. + * + * @param d Data retrieved using mutt_hcache_fetch or mutt_hcache_fetch_raw. + * @return Pointer to the restored header (cannot be NULL). + * @note The returned HEADER must be free'd by caller code with + * mutt_free_header. + */ +HEADER * +mutt_hcache_restore(const unsigned char *d); + +/** + * mutt_hcache_store - store a HEADER along with a validity datum. + * + * @param h Pointer to the header_cache_t structure got by mutt_hcache_open. + * @param key Message identification string. + * @param keylen Length of the string pointed to by key. + * @param header Message header to store. + * @param uidvalidity IMAP-specific UIDVALIDITY value, or 0 to use the current + * time. + * @return 0 on success, -1 otherwise. + */ +int +mutt_hcache_store(header_cache_t *h, const char *key, size_t keylen, + HEADER *header, unsigned int uidvalidity); + +/** + * mutt_hcache_store_raw - store a key / data pair. + * + * @param h Pointer to the header_cache_t structure got by mutt_hcache_open. + * @param key Message identification string. + * @param keylen Length of the string pointed to by key. + * @param data Payload to associate with key. + * @param dlen Length of the buffer pointed to by the @data parameter. + * @return 0 on success, -1 otherwise. + */ +int +mutt_hcache_store_raw(header_cache_t *h, const char* key, size_t keylen, + void* data, size_t dlen); + +/** + * mutt_hcache_delete - delete a key / data pair. + * + * @param h Pointer to the header_cache_t structure got by mutt_hcache_open. + * @param key Message identification string. + * @param keylen Length of the string pointed to by key. + * @return 0 on success, -1 otherwise. + */ +int +mutt_hcache_delete(header_cache_t *h, const char *key, size_t keylen); + +/** + * mutt_hcache_backend - get a backend-specific identification string. + * + * @return String describing the currently used hcache backend. + */ +const char * +mutt_hcache_backend(void); + +#define HCACHE_BACKEND_LIST \ + HCACHE_BACKEND(bdb) \ + HCACHE_BACKEND(gdbm) \ + HCACHE_BACKEND(kc) \ + HCACHE_BACKEND(lmdb) \ + HCACHE_BACKEND(qdbm) \ + HCACHE_BACKEND(tc) + +#define HCACHE_BACKEND_OPS(name) \ + hcache_ops_t hcache_##name##_ops = { \ + .open = hcache_##name##_open, \ + .fetch = hcache_##name##_fetch, \ + .store = hcache_##name##_store, \ + .delete = hcache_##name##_delete, \ + .close = hcache_##name##_close, \ + .backend = hcache_##name##_backend \ + }; #endif /* _HCACHE_H_ */ diff --git a/imap/imap.c b/imap/imap.c index e65d5312e..7e1c21694 100644 --- a/imap/imap.c +++ b/imap/imap.c @@ -1680,8 +1680,8 @@ IMAP_STATUS* imap_mboxcache_get (IMAP_DATA* idata, const char* mbox, int create) hc = imap_hcache_open (idata, mbox); if (hc) { - uidvalidity = mutt_hcache_fetch_raw (hc, "/UIDVALIDITY", imap_hcache_keylen); - uidnext = mutt_hcache_fetch_raw (hc, "/UIDNEXT", imap_hcache_keylen); + uidvalidity = mutt_hcache_fetch_raw (hc, "/UIDVALIDITY", 12); + uidnext = mutt_hcache_fetch_raw (hc, "/UIDNEXT", 8); mutt_hcache_close (hc); if (uidvalidity) { diff --git a/imap/message.c b/imap/message.c index 3e8b555d5..857914c88 100644 --- a/imap/message.c +++ b/imap/message.c @@ -124,8 +124,8 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend) if (idata->hcache && !msgbegin) { - uid_validity = mutt_hcache_fetch_raw (idata->hcache, "/UIDVALIDITY", imap_hcache_keylen); - puidnext = mutt_hcache_fetch_raw (idata->hcache, "/UIDNEXT", imap_hcache_keylen); + uid_validity = mutt_hcache_fetch_raw (idata->hcache, "/UIDVALIDITY", 12); + puidnext = mutt_hcache_fetch_raw (idata->hcache, "/UIDNEXT", 8); if (puidnext) { uidnext = *puidnext; @@ -359,16 +359,16 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend) status->uidnext = maxuid + 1; #if USE_HCACHE - mutt_hcache_store_raw (idata->hcache, "/UIDVALIDITY", &idata->uid_validity, - sizeof (idata->uid_validity), imap_hcache_keylen); + mutt_hcache_store_raw (idata->hcache, "/UIDVALIDITY", 12, + &idata->uid_validity, sizeof (idata->uid_validity)); if (maxuid && idata->uidnext < maxuid + 1) { dprint (2, (debugfile, "Overriding UIDNEXT: %u -> %u\n", idata->uidnext, maxuid + 1)); idata->uidnext = maxuid + 1; } if (idata->uidnext > 1) - mutt_hcache_store_raw (idata->hcache, "/UIDNEXT", &idata->uidnext, - sizeof (idata->uidnext), imap_hcache_keylen); + mutt_hcache_store_raw (idata->hcache, "/UIDNEXT", 8, + &idata->uidnext, sizeof (idata->uidnext)); imap_hcache_close (idata); #endif /* USE_HCACHE */ diff --git a/imap/util.c b/imap/util.c index cb911a112..6b44e2fd0 100644 --- a/imap/util.c +++ b/imap/util.c @@ -223,12 +223,12 @@ HEADER* imap_hcache_get (IMAP_DATA* idata, unsigned int uid) return NULL; sprintf (key, "/%u", uid); - uv = (unsigned int*)mutt_hcache_fetch (idata->hcache, key, - imap_hcache_keylen); + uv = mutt_hcache_fetch (idata->hcache, key, + imap_hcache_keylen(key)); if (uv) { if (*uv == idata->uid_validity) - h = mutt_hcache_restore ((unsigned char*)uv, NULL); + h = mutt_hcache_restore ((const unsigned char*)uv); else dprint (3, (debugfile, "hcache uidvalidity mismatch: %u", *uv)); FREE (&uv); @@ -245,8 +245,8 @@ int imap_hcache_put (IMAP_DATA* idata, HEADER* h) return -1; sprintf (key, "/%u", HEADER_DATA (h)->uid); - return mutt_hcache_store (idata->hcache, key, h, idata->uid_validity, - imap_hcache_keylen, 0); + return mutt_hcache_store (idata->hcache, key, imap_hcache_keylen(key), h, + idata->uid_validity); } int imap_hcache_del (IMAP_DATA* idata, unsigned int uid) @@ -257,7 +257,7 @@ int imap_hcache_del (IMAP_DATA* idata, unsigned int uid) return -1; sprintf (key, "/%u", uid); - return mutt_hcache_delete (idata->hcache, key, imap_hcache_keylen); + return mutt_hcache_delete (idata->hcache, key, imap_hcache_keylen(key)); } #endif diff --git a/init.h b/init.h index 9de4de95f..3a6d50ca2 100644 --- a/init.h +++ b/init.h @@ -1064,7 +1064,7 @@ struct option_t MuttVars[] = { ** cached folders. */ #endif /* HAVE_QDBM */ -#if defined(HAVE_GDBM) || defined(HAVE_DB4) +#if defined(HAVE_GDBM) || defined(HAVE_BDB) { "header_cache_pagesize", DT_STR, R_NONE, UL &HeaderCachePageSize, UL "16384" }, /* ** .pp @@ -1073,7 +1073,7 @@ struct option_t MuttVars[] = { ** values can waste space, memory, or CPU time. The default should be more ** or less optimal for most use cases. */ -#endif /* HAVE_GDBM || HAVE_DB4 */ +#endif /* HAVE_GDBM || HAVE_BDB */ #endif /* USE_HCACHE */ { "help", DT_BOOL, R_BOTH|R_REFLOW, OPTHELP, 1 }, /* diff --git a/mh.c b/mh.c index c90679ec6..45af0b33c 100644 --- a/mh.c +++ b/mh.c @@ -1128,6 +1128,8 @@ static void maildir_delayed_parsing (CONTEXT * ctx, struct maildir **md, #if USE_HCACHE header_cache_t *hc = NULL; void *data; + const char *key; + size_t keylen; struct timeval *when = NULL; struct stat lastchanged; int ret; @@ -1183,16 +1185,27 @@ static void maildir_delayed_parsing (CONTEXT * ctx, struct maildir **md, } if (ctx->magic == MUTT_MH) - data = mutt_hcache_fetch (hc, p->h->path, strlen); + { + key = p->h->path; + keylen = strlen(key); + } else - data = mutt_hcache_fetch (hc, p->h->path + 3, &maildir_hcache_keylen); + { + key = p->h->path + 3; + keylen = maildir_hcache_keylen(key); + } + data = mutt_hcache_fetch (hc, key, keylen); when = (struct timeval *) data; if (data != NULL && !ret && lastchanged.st_mtime <= when->tv_sec) { - p->h = mutt_hcache_restore ((unsigned char *)data, &p->h); + HEADER* h = mutt_hcache_restore((unsigned char *)data); + h->old = p->h->old; + h->path = safe_strdup(p->h->path); + mutt_free_header(&p->h); + p->h = h; if (ctx->magic == MUTT_MAILDIR) - maildir_parse_flags (p->h, fn); + maildir_parse_flags (p->h, fn); } else { @@ -1203,9 +1216,16 @@ static void maildir_delayed_parsing (CONTEXT * ctx, struct maildir **md, p->header_parsed = 1; #if USE_HCACHE if (ctx->magic == MUTT_MH) - mutt_hcache_store (hc, p->h->path, p->h, 0, strlen, MUTT_GENERATE_UIDVALIDITY); + { + key = p->h->path; + keylen = strlen(key); + } else - mutt_hcache_store (hc, p->h->path + 3, p->h, 0, &maildir_hcache_keylen, MUTT_GENERATE_UIDVALIDITY); + { + key = p->h->path + 3; + keylen = maildir_hcache_keylen(key); + } + mutt_hcache_store (hc, key, keylen, p->h, 0); #endif } else mutt_free_header (&p->h); @@ -1895,6 +1915,10 @@ int mh_sync_mailbox_message (CONTEXT * ctx, int msgno, header_cache_t *hc) int mh_sync_mailbox_message (CONTEXT * ctx, int msgno) #endif { +#if USE_HCACHE + const char *key; + size_t keylen; +#endif char path[_POSIX_PATH_MAX], tmp[_POSIX_PATH_MAX]; HEADER *h = ctx->hdrs[msgno]; @@ -1905,12 +1929,20 @@ int mh_sync_mailbox_message (CONTEXT * ctx, int msgno) || (option (OPTMHPURGE) && ctx->magic == MUTT_MH)) { #if USE_HCACHE - if (hc) { - if (ctx->magic == MUTT_MAILDIR) - mutt_hcache_delete (hc, h->path + 3, &maildir_hcache_keylen); - else if (ctx->magic == MUTT_MH) - mutt_hcache_delete (hc, h->path, strlen); - } + if (hc) + { + if (ctx->magic == MUTT_MH) + { + key = h->path; + keylen = strlen(key); + } + else + { + key = h->path + 3; + keylen = maildir_hcache_keylen(key); + } + mutt_hcache_delete (hc, key, keylen); + } #endif /* USE_HCACHE */ unlink (path); } @@ -1947,10 +1979,17 @@ int mh_sync_mailbox_message (CONTEXT * ctx, int msgno) #if USE_HCACHE if (hc && h->changed) { - if (ctx->magic == MUTT_MAILDIR) - mutt_hcache_store (hc, h->path + 3, h, 0, &maildir_hcache_keylen, MUTT_GENERATE_UIDVALIDITY); - else if (ctx->magic == MUTT_MH) - mutt_hcache_store (hc, h->path, h, 0, strlen, MUTT_GENERATE_UIDVALIDITY); + if (ctx->magic == MUTT_MH) + { + key = h->path; + keylen = strlen(key); + } + else + { + key = h->path + 3; + keylen = maildir_hcache_keylen(key); + } + mutt_hcache_store (hc, key, keylen, h, 0); } #endif diff --git a/newsrc.c b/newsrc.c index ad8255f03..d30d5545a 100644 --- a/newsrc.c +++ b/newsrc.c @@ -644,7 +644,7 @@ void nntp_hcache_update (NNTP_DATA *nntp_data, header_cache_t *hc) return; /* fetch previous values of first and last */ - hdata = mutt_hcache_fetch_raw (hc, "index", strlen); + hdata = mutt_hcache_fetch_raw (hc, "index", 5); if (hdata) { dprint (2, (debugfile, @@ -664,7 +664,7 @@ void nntp_hcache_update (NNTP_DATA *nntp_data, header_cache_t *hc) snprintf (buf, sizeof (buf), "%d", current); dprint (2, (debugfile, "nntp_hcache_update: mutt_hcache_delete %s\n", buf)); - mutt_hcache_delete (hc, buf, strlen); + mutt_hcache_delete (hc, buf, strlen(buf)); } } FREE (&hdata); @@ -678,7 +678,7 @@ void nntp_hcache_update (NNTP_DATA *nntp_data, header_cache_t *hc) nntp_data->lastMessage); dprint (2, (debugfile, "nntp_hcache_update: mutt_hcache_store index: %s\n", buf)); - mutt_hcache_store_raw (hc, "index", buf, strlen (buf) + 1, strlen); + mutt_hcache_store_raw (hc, "index", 5, buf, strlen (buf)); } } #endif @@ -709,12 +709,11 @@ void nntp_bcache_update (NNTP_DATA *nntp_data) /* Remove hcache and bcache of newsgroup */ void nntp_delete_group_cache (NNTP_DATA *nntp_data) { - char file[_POSIX_PATH_MAX]; - if (!nntp_data || !nntp_data->nserv || !nntp_data->nserv->cacheable) return; #ifdef USE_HCACHE + char file[_POSIX_PATH_MAX]; nntp_hcache_namer (nntp_data->group, file, sizeof (file)); cache_expand (file, sizeof (file), &nntp_data->nserv->conn->account, file); unlink (file); @@ -1030,7 +1029,7 @@ NNTP_SERVER *nntp_select_server (char *server, int leave_lock) continue; /* fetch previous values of first and last */ - hdata = mutt_hcache_fetch_raw (hc, "index", strlen); + hdata = mutt_hcache_fetch_raw (hc, "index", 5); if (hdata) { anum_t first, last; diff --git a/nntp.c b/nntp.c index b928aaa4b..494149ec4 100644 --- a/nntp.c +++ b/nntp.c @@ -1117,14 +1117,14 @@ static int parse_overview_line (char *line, void *data) /* try to replace with header from cache */ snprintf (buf, sizeof (buf), "%d", anum); - hdata = mutt_hcache_fetch (fc->hc, buf, strlen); + hdata = mutt_hcache_fetch (fc->hc, buf, strlen(buf)); if (hdata) { dprint (2, (debugfile, "parse_overview_line: mutt_hcache_fetch %s\n", buf)); mutt_free_header (&hdr); ctx->hdrs[ctx->msgcount] = - hdr = mutt_hcache_restore (hdata, NULL); + hdr = mutt_hcache_restore (hdata); FREE (&hdata); hdr->data = 0; hdr->read = 0; @@ -1148,7 +1148,7 @@ static int parse_overview_line (char *line, void *data) { dprint (2, (debugfile, "parse_overview_line: mutt_hcache_store %s\n", buf)); - mutt_hcache_store (fc->hc, buf, hdr, 0, strlen, MUTT_GENERATE_UIDVALIDITY); + mutt_hcache_store (fc->hc, buf, strlen(buf), hdr, 0); } } #endif @@ -1249,7 +1249,7 @@ static int nntp_fetch_headers (CONTEXT *ctx, void *hc, { dprint (2, (debugfile, "nntp_fetch_headers: mutt_hcache_delete %s\n", buf)); - mutt_hcache_delete (fc.hc, buf, strlen); + mutt_hcache_delete (fc.hc, buf, strlen(buf)); } #endif } @@ -1282,13 +1282,13 @@ static int nntp_fetch_headers (CONTEXT *ctx, void *hc, #ifdef USE_HCACHE /* try to fetch header from cache */ - hdata = mutt_hcache_fetch (fc.hc, buf, strlen); + hdata = mutt_hcache_fetch (fc.hc, buf, strlen(buf)); if (hdata) { dprint (2, (debugfile, "nntp_fetch_headers: mutt_hcache_fetch %s\n", buf)); ctx->hdrs[ctx->msgcount] = - hdr = mutt_hcache_restore (hdata, NULL); + hdr = mutt_hcache_restore (hdata); FREE (&hdata); hdr->data = 0; @@ -1798,7 +1798,7 @@ int nntp_sync_mailbox (CONTEXT *ctx) if (hdr->deleted && !hdr->read) nntp_data->unread--; dprint (2, (debugfile, "nntp_sync_mailbox: mutt_hcache_store %s\n", buf)); - mutt_hcache_store (hc, buf, hdr, 0, strlen, MUTT_GENERATE_UIDVALIDITY); + mutt_hcache_store (hc, buf, strlen(buf), hdr, 0); } #endif } @@ -2042,14 +2042,14 @@ int nntp_check_mailbox (CONTEXT *ctx, int *index_hint) messages[anum - first] = 1; snprintf (buf, sizeof (buf), "%d", anum); - hdata = mutt_hcache_fetch (hc, buf, strlen); + hdata = mutt_hcache_fetch (hc, buf, strlen(buf)); if (hdata) { int deleted; dprint (2, (debugfile, "nntp_check_mailbox: mutt_hcache_fetch %s\n", buf)); - hdr = mutt_hcache_restore (hdata, NULL); + hdr = mutt_hcache_restore (hdata); FREE (&hdata); hdr->data = 0; deleted = hdr->deleted; @@ -2089,7 +2089,7 @@ int nntp_check_mailbox (CONTEXT *ctx, int *index_hint) continue; snprintf (buf, sizeof (buf), "%d", anum); - hdata = mutt_hcache_fetch (hc, buf, strlen); + hdata = mutt_hcache_fetch (hc, buf, strlen(buf)); if (hdata) { dprint (2, (debugfile, @@ -2098,7 +2098,7 @@ int nntp_check_mailbox (CONTEXT *ctx, int *index_hint) mx_alloc_memory (ctx); ctx->hdrs[ctx->msgcount] = - hdr = mutt_hcache_restore (hdata, NULL); + hdr = mutt_hcache_restore (hdata); FREE (&hdata); hdr->data = 0; if (hdr->deleted) diff --git a/pop.c b/pop.c index 294d71d8d..f8d4d79a2 100644 --- a/pop.c +++ b/pop.c @@ -303,7 +303,7 @@ static int pop_fetch_headers (CONTEXT *ctx) if (!ctx->quiet) mutt_progress_update (&progress, i + 1 - old_count, -1); #if USE_HCACHE - if ((data = mutt_hcache_fetch (hc, ctx->hdrs[i]->data, strlen))) + if ((data = mutt_hcache_fetch (hc, ctx->hdrs[i]->data, strlen(ctx->hdrs[i]->data)))) { char *uidl = safe_strdup (ctx->hdrs[i]->data); int refno = ctx->hdrs[i]->refno; @@ -317,7 +317,7 @@ static int pop_fetch_headers (CONTEXT *ctx) * (the old h->data should point inside a malloc'd block from * hcache so there shouldn't be a memleak here) */ - HEADER *h = mutt_hcache_restore ((unsigned char *) data, NULL); + HEADER *h = mutt_hcache_restore ((unsigned char *) data); mutt_free_header (&ctx->hdrs[i]); ctx->hdrs[i] = h; ctx->hdrs[i]->refno = refno; @@ -333,7 +333,8 @@ static int pop_fetch_headers (CONTEXT *ctx) #if USE_HCACHE else { - mutt_hcache_store (hc, ctx->hdrs[i]->data, ctx->hdrs[i], 0, strlen, MUTT_GENERATE_UIDVALIDITY); + mutt_hcache_store (hc, ctx->hdrs[i]->data, strlen(ctx->hdrs[i]->data), + ctx->hdrs[i], 0); } FREE(&data); @@ -700,7 +701,7 @@ int pop_sync_mailbox (CONTEXT *ctx, int *index_hint) { mutt_bcache_del (pop_data->bcache, ctx->hdrs[i]->data); #if USE_HCACHE - mutt_hcache_delete (hc, ctx->hdrs[i]->data, strlen); + mutt_hcache_delete (hc, ctx->hdrs[i]->data, strlen(ctx->hdrs[i]->data)); #endif } } @@ -708,7 +709,8 @@ int pop_sync_mailbox (CONTEXT *ctx, int *index_hint) #if USE_HCACHE if (ctx->hdrs[i]->changed) { - mutt_hcache_store (hc, ctx->hdrs[i]->data, ctx->hdrs[i], 0, strlen, MUTT_GENERATE_UIDVALIDITY); + mutt_hcache_store (hc, ctx->hdrs[i]->data, strlen(ctx->hdrs[i]->data), + ctx->hdrs[i], 0); } #endif -- 2.40.0