/* This file is part of Mailfromd. Copyright (C) 2005-2020 Sergey Poznyakoff 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 3, 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, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include "libmf.h" #include "mfdb.h" #include "filenames.h" time_t negative_expire_interval;//FIXME time_t tempfail_expire_interval = 37*60;/* FIXME: Equals to the sum of the ``hard'' timeouts */ static void cache_print_item(struct mu_dbm_datum const *key, struct mu_dbm_datum const *val); static int cache_expire_item(struct mu_dbm_datum const *content); static struct db_format cache_format_struct = { "cache", DEFAULT_DATABASE, 1, 0, DEFAULT_EXPIRE_INTERVAL, cache_print_item, cache_expire_item }; struct db_format *cache_format = &cache_format_struct; struct cache_result { time_t timestamp; mf_status status; }; #define EXPIRE_INTERVAL(s) \ ((s) == mf_success ? cache_format->expire_interval : \ (((s) == mf_temp_failure || (s) == mf_failure) \ ? tempfail_expire_interval : negative_expire_interval)) static char * format_timestr(time_t timestamp, char *timebuf, size_t bufsize) { struct tm tm; gmtime_r(×tamp, &tm); strftime(timebuf, bufsize, "%c", &tm); return timebuf; } mf_status cache_get(const char *email) { mf_status rc; mu_dbm_file_t db; struct mu_dbm_datum key; struct mu_dbm_datum contents; int result; int readonly = 0; if (!cache_format->enabled) return mf_failure; mu_debug(cache_format->debug_handle, MU_DEBUG_TRACE5, ("getting cache info for %s", email)); db = mf_dbm_open(cache_format->dbname, MU_STREAM_RDWR); if (!db) { db = mf_dbm_open(cache_format->dbname, MU_STREAM_READ); if (!db) return mf_failure; readonly = 1; } memset (&key, 0, sizeof key); memset (&contents, 0, sizeof contents); key.mu_dptr = (void*) email; key.mu_dsize = strlen (email) + 1; if ((result = mu_dbm_fetch(db, &key, &contents)) == 0) { char timebuf[80]; struct cache_result *res = (struct cache_result *) contents.mu_dptr; time_t t = time(NULL); mu_debug(cache_format->debug_handle, MU_DEBUG_TRACE5, ("found status: %s (%d), time: %s", mf_status_str(res->status), res->status, format_timestr(res->timestamp, timebuf, sizeof timebuf))); if (t - res->timestamp > EXPIRE_INTERVAL(res->status)) { if (!readonly) { mu_debug(cache_format->debug_handle, MU_DEBUG_TRACE5, ("removing expired entry for %s", email)); result = mu_dbm_delete(db, &key); if (result) mu_error(_("cannot remove record `%s' from `%s': %s"), email, cache_format->dbname, result == MU_ERR_FAILURE ? mu_dbm_strerror(db) : mu_strerror(result)); } rc = mf_failure; } else { rc = res->status; } } else { if (result == MU_ERR_FAILURE) mu_error(_("cannot fetch record `%s' from `%s': %s"), email, cache_format->dbname, mu_dbm_strerror(db)); rc = mf_failure; } mu_dbm_datum_free(&contents); mu_dbm_destroy(&db); return rc; } void cache_insert(const char *email, mf_status rc) { mu_dbm_file_t db; struct mu_dbm_datum key; struct mu_dbm_datum contents; struct cache_result res; char timebuf[80]; int result; if (!cache_format->enabled) return; time(&res.timestamp); res.status = rc; mu_debug(cache_format->debug_handle, MU_DEBUG_TRACE5, ("inserting cache info for %s. status=%s (%d), time=%s", email, mf_status_str(rc), rc, format_timestr(res.timestamp, timebuf, sizeof timebuf))); db = mf_dbm_open(cache_format->dbname, MU_STREAM_RDWR); if (!db) return; memset(&key, 0, sizeof key); memset(&contents, 0, sizeof contents); key.mu_dptr = (void*) email; key.mu_dsize = strlen (email) + 1; contents.mu_dptr = (void*)&res; contents.mu_dsize = sizeof(res); result = mu_dbm_store(db, &key, &contents, 1); if (result) mu_error(_("cannot store datum `%s' into `%s': %s"), email, cache_format->dbname, result == MU_ERR_FAILURE ? mu_dbm_strerror(db) : mu_strerror(result)); mu_dbm_destroy(&db); } static void cache_print_item(struct mu_dbm_datum const *key, struct mu_dbm_datum const *val) { const struct cache_result *res = (const struct cache_result *) val->mu_dptr; int size = key->mu_dsize - 1; /* Size includes the trailing nul */ printf("%*.*s", size, size, key->mu_dptr); printf(" %10.10s ", mf_status_str(res->status)); format_time_str(stdout, res->timestamp); putchar('\n'); } static int cache_expire_item(struct mu_dbm_datum const *content) { struct cache_result const *res = (struct cache_result const *)content->mu_dptr; return !res || time(NULL) - res->timestamp > EXPIRE_INTERVAL(res->status); } mf_status cache_get2(const char *email, const char *client_addr) { mf_status rc = mf_failure; size_t size; char *key; if (!cache_format->enabled) return mf_failure; size = strlen(email) + 1 + strlen(client_addr) + 1; key = malloc(size); if (key) { strcat(strcat(strcpy(key, email), ":"), client_addr); rc = cache_get(key); free(key); } return rc; } void cache_insert2(const char *email, const char *client_addr, mf_status rc) { if (!cache_format->enabled) return; else { size_t size = strlen(email) + 1 + strlen(client_addr) + 1; char *key = malloc(size); if (key) { strcat(strcat(strcpy(key, email), ":"), client_addr); cache_insert(key, rc); free(key); } } }