README.dbapi (version 0.2) Copyright (C) 2004 Peter Conrad This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License (version 2) as published by the Free Software Foundation. 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Note: this document is of interest for developers who want to create new database interfaces for the greylisting plugin. Ordinary users probably won't find anything useful in here. Overview ======== During the creation of this greylisting plugin I have tried to make a clean separation between the greylisting functionality on the one hand, and the database-specific functionality on the other hand. Here, by "database" I mean any mechanism capable of storing (and retrieving from storage) the information required for the greylisting functionality: IP numbers, email addresses and timestamps of SMTP communication attempts and a "confirmed" flag for each entry. That separation has been mostly successful. The only "unclean" part is that expiration times must be checked inside the database. The reason for this is that different database types will differ greatly in how these checks can be implemented *efficiently*. The API consists of six simple functions that must be implemented for a database, and three global integer variables containing the configured timeout values. The API is especially simple, because the functions are called in a well defined order. They need not be thread-safe or reentrant (although care must be taken that concurrent *processes* accessing the database do not interfere with each other). The API functions *should* take care of database maintenance (e. g. the actual removal of expired entries from the database), if required. If manual maintenance is required, it *must* be documented in an accompanying README file. Every database implementation must consist of a single C source file "db-.c" in the "src" subdirectory of the distribution. Choose an appropriate identifier for the database type you want to support. "db" is not an appropriate identifier. :-) The implementation should be accompanied by a README.db- in the "doc" subdirectory of the distribution. The README should contain everything a *user* needs to know about the implementation, e. g. how the GL_DATABASE variable will be interpreted. Error handling ============== If an error occurs inside the database routines, an error message must be written to STDERR. Then, closedb() should be called, and after that, the program should exit with a non-zero error code. Remember: Keep It Small and Simple! Do not waste effort on recovering from an unexpected error condition. Instead, make sure an error message is generated (it will be logged by Qmail). That should alert someone who can fix the problem manually. No mail will be lost, because either the message gets through, anyway, or the sending MTA will try again later. Function call order =================== 0. closedb() can be called AT ANY TIME. Therefore, it must be made safe against double-free bugs and the like. After closedb(), no other API functions will be called, except possibly closedb() itself (again). Apart from that, the normal order of API calls will be: 1. opendb 2. find_entry 3. depending on the result of find_entry, at most one of a) add_entry b) update_entry c) delete_entry 4. closedb API functions ============= The API functions are defined the header file "db-api.h", which should be included by every database implementation. void opendb(char *name) ----------------------- Open files and allocate resources required for accessing the database. This may be a good place to perform maintenance if required. Another good idea might be to check some kind of version number. The given "name" should in some way specify the location of the database. It will be taken from the environment variable GL_DATABASE. void closedb() -------------- Close all files and free other resources related to API functions. REMEMBER: this is the only function that can be called multiple times! Make sure you don't free() resources twice! int find_entry(char *ip, char *sender, char *recipient) ------------------------------------------------------- Look up an entry in the database matching the given ip, sender and recipient. Return -1 if no such entry exists (or an entry exists but has expired), 0 if an entry exists but it is not yet confirmed and has not yet reached min_reject, 1 otherwise (i. e. the message is to be accepted). You should not make any assumptions about the format of the given string parameters, except that they are NUL-terminated strings. void add_entry(char *ip, char *sender, char *recipient) ------------------------------------------------------- Adds a new entry to the database with the given ip, sender and recipient, the current time and with the "confirmed" flag unset. You should not make any assumptions about the format of the given string parameters, except that they are NUL-terminated strings. void update_entry() ------------------- Update the entry found by the previous call to find_entry() to contain the current timestamp and the "confirmed" flag. void delete_entry() ------------------- Delete the entry found by the previous call to find_entry() from the database. Global variables ================ extern char *progname --------------------- Will be set to argv[0], should be used in error messages. extern int min_reject --------------------- Entries with the "not confirmed" flag set will not be accepted before at least min_reject seconds have gone by after their creation (find_entry must return 0 for such entries). extern int max_wait ------------------- Entries with the "not confirmed" flag set will expire no earlier than max_wait seconds after their timestamp (set at the time when the entry was added). extern int accept_good ---------------------- Entries with the "confirmed" flag set will expire no earlier than accept_good seconds after their timestamp (set by an earlier call to update()).