/* 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"
int predict_next_option;//FIXME
double predict_rate;//FIXME
static void rate_print_item(struct mu_dbm_datum const *key,
struct mu_dbm_datum const *val);
static int rate_expire_item(struct mu_dbm_datum const *content);
static struct db_format rate_format_struct = {
"rate",
DEFAULT_RATE_DATABASE,
1,
0,
DEFAULT_EXPIRE_INTERVAL,
rate_print_item,
rate_expire_item
};
struct db_format *rate_format = &rate_format_struct;
struct rate_result {
time_t timestamp;
time_t interval;
size_t count;
};
mf_status
get_rate(char *email, long *ret, unsigned long interval, size_t mincount,
size_t threshold)
{
mu_dbm_file_t db;
struct mu_dbm_datum key;
struct mu_dbm_datum contents;
struct rate_result *rp, rate;
long result;
time_t t;
int res;
int obsolete;
mu_debug(rate_format->debug_handle, MU_DEBUG_TRACE5,
("getting rate info for %s", email));
db = mf_dbm_open(rate_format->dbname, MU_STREAM_RDWR);
if (!db)
return mf_failure;
memset(&key, 0, sizeof key);
memset(&contents, 0, sizeof contents);
key.mu_dptr = email;
key.mu_dsize = strlen (email) + 1;
t = time(NULL);
if ((res = mu_dbm_fetch(db, &key, &contents)) == 0) {
rp = (struct rate_result *) contents.mu_dptr;
mu_debug(rate_format->debug_handle, MU_DEBUG_TRACE5,
("found time: %lu, interval: %lu, count: %lu, "
"rate: %g",
rp->timestamp, rp->interval,
(unsigned long) rp->count,
rp->interval == 0 ? -1 :
(double)rp->count/rp->interval));
} else {
if (res != MU_ERR_NOENT)
mu_error(_("cannot fetch `%s' from `%s': %s"),
email, rate_format->dbname,
mu_dbm_strerror(db));
/* Initialize the structure */
rate.timestamp = t;
rate.interval = 0;
rate.count = 0;
rp = &rate;
}
/* Update the structure */
obsolete = t - rp->timestamp > interval;
rp->interval += t - rp->timestamp;
rp->timestamp = t;
rp->count++;
if (rp->interval == 0 || rp->count < mincount)
result = 0;
else
result = ((double) rp->count / rp->interval) * interval;
mu_debug(rate_format->debug_handle, MU_DEBUG_TRACE5,
("rate for %s is %ld/%lu", email, result, interval));
/* Update the db */
if (threshold && result > threshold)
mu_debug(rate_format->debug_handle, MU_DEBUG_TRACE5,
("not updating %s rates", email));
else {
struct mu_dbm_datum dat;
if (obsolete
|| (rate_format->expire_interval
&& rp->interval > rate_format->expire_interval)) {
mu_debug(rate_format->debug_handle, MU_DEBUG_TRACE5,
("expiring %s rates", email));
rp->interval = 0;
rp->count = 1;
} else
mu_debug(rate_format->debug_handle, MU_DEBUG_TRACE5,
("updating %s rates", email));
dat.mu_dptr = (void*)rp;
dat.mu_dsize = sizeof(*rp);
res = mu_dbm_store(db, &key, &dat, 1);
if (res)
mu_error (_("cannot insert datum `%s' into `%s': %s"),
email, rate_format->dbname,
res == MU_ERR_FAILURE ?
mu_dbm_strerror(db) :
mu_strerror(res));
}
mu_dbm_datum_free(&contents);
mu_dbm_destroy(&db);
*ret = result;
return mf_success;
}
static void
rate_print_item(struct mu_dbm_datum const *key,
struct mu_dbm_datum const *val)
{
const struct rate_result *res = (const struct rate_result *)
val->mu_dptr;
double rate, exp_rate;
int size = key->mu_dsize - 1; /* Size includes the trailing nul */
printf("%*.*s ", size, size, key->mu_dptr);
if (res->interval) {
rate = (double)res->count / res->interval;
exp_rate = (double)(res->count + 1) /
(time(NULL) - res->timestamp + res->interval);
} else
rate = exp_rate = -1;
format_time_str(stdout, res->timestamp);
printf(" %6lu %6lu ",
res->interval,
(unsigned long) res->count);
if (rate > 0)
printf("%8.3g ", rate);
else
printf("%8.8s ", "N/A");
if (exp_rate > 0)
printf("%8.3g", exp_rate);
else
printf("%8.8s", "N/A");
if (predict_next_option) {
time_t next_time;
time_t now = time(NULL);
if (res->interval == 0) {
next_time = 0;
} else {
double interval = (double) (res->count + 1) /
predict_rate
- res->interval;
next_time = res->timestamp + interval;
}
if (next_time < now)
printf("; free to send\n");
else {
printf("; in %lu sec. on ",
(next_time - now));
format_time_str(stdout, next_time);
putchar('\n');
}
} else
printf("\n");
}
static int
rate_expire_item(struct mu_dbm_datum const *content)
{
struct rate_result const *res =
(struct rate_result const *) content->mu_dptr;
return rate_format->expire_interval &&
time(NULL) - res->timestamp + res->interval >
rate_format->expire_interval;
}