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