/* 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);
}
}
}