/* This file is part of mailfromd.
Copyright (C) 2005, 2006, 2007 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 . */
#define MF_SOURCE_NAME MF_SOURCE_CACHE
#ifdef HAVE_CONFIG_H
# include
#endif
#include
#include "mailfromd.h"
struct cache_result {
time_t timestamp;
mf_status status;
};
#define EXPIRE_INTERVAL(s) \
((s) == mf_success ? cache_format->expire_interval : negative_expire_interval)
mf_status
cache_get(char *email)
{
mf_status rc;
DBM_FILE db;
DBM_DATUM key;
DBM_DATUM contents;
int readonly = 0;
int res;
if (!cache_format->enabled)
return mf_failure;
debug1(50, "getting cache info for %s", email);
if (mu_dbm_open(cache_format->dbname, &db, MU_STREAM_RDWR, 0600,
&readonly)) {
mu_error(_("mu_dbm_open(%s) failed: %s"), cache_format->dbname,
mu_dbm_strerror());
return mf_failure;
}
if (readonly)
debug1(1, "cannot lock %s: switching to read-only mode",
cache_format->dbname);
memset (&key, 0, sizeof key);
memset (&contents, 0, sizeof contents);
MU_DATUM_PTR (key) = email;
MU_DATUM_SIZE (key) = strlen (email) + 1;
if ((res = mu_dbm_fetch(&db, key, &contents)) == 0) {
char timebuf[80];
struct cache_result *res = (struct cache_result *)
MU_DATUM_PTR(contents);
time_t t = time(NULL);
debug3(50, "found status: %s (%d), time: %s",
mf_status_str(res->status), res->status,
mailfromd_timestr(res->timestamp,
timebuf, sizeof timebuf));
if (t - res->timestamp > EXPIRE_INTERVAL(res->status)) {
if (!readonly) {
debug1(50,
"removing expired entry for %s",email);
if (mu_dbm_delete(&db, key))
mu_error(_("Cannot remove record `%s' from `%s': %s"),
email, db.name,
mu_dbm_strerror());
}
rc = mf_failure;
} else {
rc = res->status;
}
} else {
if (res != MU_ERR_NOENT)
mu_error(_("Cannot fetch record `%s' from `%s': %s"),
email, db.name, mu_dbm_strerror());
rc = mf_failure;
}
mu_dbm_datum_free(&contents);
mu_dbm_close(&db);
return rc;
}
void
cache_insert(char *email, mf_status rc)
{
DBM_FILE db;
DBM_DATUM key;
DBM_DATUM contents;
struct cache_result res;
char timebuf[80];
if (!cache_format->enabled)
return;
time(&res.timestamp);
res.status = rc;
debug4(50,"inserting cache info for %s. status=%s (%d), time=%s",
email, mf_status_str(rc), rc,
mailfromd_timestr(res.timestamp, timebuf, sizeof timebuf));
if (mu_dbm_open(cache_format->dbname, &db, MU_STREAM_RDWR,
0600, NULL)) {
mu_error(_("mu_dbm_open(%s) failed: %s"), cache_format->dbname,
mu_dbm_strerror());
return;
}
memset(&key, 0, sizeof key);
memset(&contents, 0, sizeof contents);
MU_DATUM_PTR(key) = email;
MU_DATUM_SIZE(key) = strlen (email) + 1;
MU_DATUM_PTR(contents) = (void*)&res;
MU_DATUM_SIZE(contents) = sizeof(res);
if (mu_dbm_insert(&db, key, contents, 1))
mu_error(_("Cannot insert datum `%s' into `%s': %s"),
email, db.name, mu_dbm_strerror());
mu_dbm_close(&db);
}
static void
cache_print_item(const char *email, size_t size, const void *content)
{
const struct cache_result *res = content;
size--; /* Size includes the trailing nul */
printf("%*.*s", size, size, email);
printf(" %10.10s ", mf_status_str(res->status));
format_time_str(stdout, res->timestamp);
putchar('\n');
}
static int
cache_expire_item(const void *content)
{
const struct cache_result *res = (struct cache_result *)content;
return !res || time(NULL) - res->timestamp > EXPIRE_INTERVAL(res->status);
}
mf_status
cache_get2(char *email, 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(char *email, 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);
}
}
}
static struct db_format cache_format_struct = {
"cache",
DEFAULT_DATABASE,
1,
DEFAULT_EXPIRE_INTERVAL,
cache_print_item,
cache_expire_item
};
struct db_format *cache_format = &cache_format_struct;