README.dbapi (version 0.2)

  Copyright (C) 2004 Peter Conrad <conrad@tivano.de>

  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-<identifier>.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-<identifier> 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()).

