Die von den Programmen des VxMail-Systems verwendete Datenbankschnittstelle soll diese unabhängig von der zugrundeliegenden Datenbank machen. Um dies zu verwirklichen, wird eine ausführliche Beschreibung dieser Schnittstelle benötigt. Dieser Text sollte diesen Anforderungen genügen.
Die Programme des VxMail-Systems beziehen ihre Benutzerdaten aus genau einer Quelle - unabhängig vom zugrundeliegenden Datenbanksystem; dies bedeutet, daß diese Programme die Benutzerdatenbank quasi als eine einzige Tabelle mit unterschiedlichen Typen von Einträgen (Datensätzen, Zeilen) sehen.
Der Typ jedes Datensatzes wird anhand eines speziellen Flag-Feldes festgestellt; folgende Typen von Datensätzen sind implementiert:
Typ | Beschreibung |
---|---|
RT_USER | ein normaler Benutzereintrag |
RT_LINK | ein Verweis auf einen Benutzereintrag |
RT_NSUSER | ein Benutzer in einer Domain, die nicht direkt und nicht vollständig vom VxMail-System verwaltet wird |
RT_ADMIN | ein Benutzer mit administrativev Rechten |
RT_DOMAIN | eine Domain, unter der Benutzer stehen können |
RT_DOMLINK | ein Verweis auf eine Domain |
RT_NSDOMAIN | eine Domain, die nicht direkt und nicht vollständig vom VxMail-System verwaltet wird |
RT_LLINK | Ein Alias für einen einzelnen Benutzer, eine Mailingliste, ein Programm oder eine Datei (Textdatei oder VxMail-Folder) |
RT_MAILSYS | der Administrator des gesamten VxMail-Systems |
RT_IP4TRANS | Übersetzung einer IP-Adresse (Protokoll- version 4) in eine E-Mail-Domain |
RT_IP6TRANS | Übersetzung einer IP-Adresse (Protokoll- version 6) in eine E-Mail-Domain |
RT_REDIRECT | Eine POP3-Umleitung auf einen anderen Rechner |
RT_IDENTITY | Der spezielle Eintrag, der die Datenbank identifiziert |
RT_VXTS | Schlüssel zur Dekodierung der Daten des VXTS-Pseudobefehls |
RT_INVALID | gesperrter Benutzereintrag |
RT_FILTER | Domain-bezogener E-Mail-Filter |
RT_BSMTP | Ein Benutzer, der seine E-Mails per (ver- zögertem) SMTP empfangen möchte. |
Jeder Datensatz - wie er von einem der Programme dev VxMail-Systems gesehen wird, umfaßt folgende Felder:
Folgende Dinge sind unbedingt zu beachten:
typedef struct { /* char *key; */ char *passwd; char *folder; char *flags; } dbif_rec;
Die Felder passwd und folder enthalten Zeichenketten in der Konvention der Programmiersprache C, d.h. Zeichenfolgen, die mit einem ASCII-NUL enden.
struct _DBIF_FILE_S; typedef struct _DBIF_FILE_S *DBIF_FILE;
Die Felder dieser Struktur werden nicht exportiert, so daß die genaue Definition dem Implementator der jeweiligen Datenbankschnittstelle überlassen bleibt.
struct _DBIF_CURSOR_S; typedef struct _DBIF_CURSOR_S *DBIF_CURSOR;
Die Felder dieser Struktur werden nicht exportiert, so daß die genaue Definition dem Implementator der jeweiligen Datenbankschnittstelle überlassen bleibt.
void dbif_reset (DBIF_FILE *dbf, char *dbname);
dbif_reset stellt eine Verbindung zur Benutzerdatenbank her, die ausschließlich einen lesenden Zugriff auf diese Datenbank zuläßt; dbname ist hierbei der Name der Datenbank.Die Datenbankverbindung kann später durch Verwendung des DBIF_FILE handles dbf referenziert werden.
void dbif_create (DBIF_FILE *dbf, char *dbname, int mode);
dbif_create löscht den Inhalt einer vorhandenen Datenbank bevor eine Lese- und Schreibverbindung zu dieser hergestellt wird; dbname ist hierbei der Name der Datenbank, dbf das DBIF_FILE handle, mit dessen Hilfe später die Datenbank referenziert werden kann; mode ist eine Maske für die unter Unix üblichen Dateizugriffsrechte; dieser Parameter wird für GDBM-artige Datenbanken benötigt und sollte bei Verwendung eines relationalen Datenbanksystems ignoriert werden.
void dbif_rw (DBIF_FILE *dbf, char *dbname);
dbif_rw stellt eine Lese- und Schreibverbindung zur Benutzerdatenbank her; dbname ist der NBame der Datenbank, dbf das DBIF_FILE handle, mit dessen Hilfe später auf die Datenbank zugegriffen werden kann.Sollte die Herstellung einer Datenbankverbindung fehlschlagen, dann wird in dbf der Wert (DBIF_FILE) 0 zurückgegeben.
void dbif_close (DBIF_FILE dbf);
dbif_close beendet alle noch nicht abgeschlossenen Transaktionen und unterbricht danach die durch dbf referenzierte Datenbankverbindung.
void dbif_sync (DBIF_FILE dbf);
dbif_sync schließt alle noch offenen Transaktionen auf der durch dbf referenzierten Datenbank ab.
dbif_rec *dbif_resolve (DBIF_FILE db, char *em_addr, char **res_addr);
dbif_resolve liest einen einzelnen Datensatz aus der Datenbank; hierbei werden Verweise auf Benutzer oder Domains bis zum Ziel (d.h. bis zu dem ersten Datensatz, der keinen Verweis darstellt) weiterverfolgt; db ist das DBIF_FILE handle, mit dessen Hilfe auf die Benutzerdatenbank zugegriffen wird, em_addr der Suchschlüssel; wird für res_addr ein Zeiger auf eine Variable vom Typ (char *) angegeben, dann wird in dieser ein Zeiger auf den Schlüssel zu einem gefundenen Datensatz zurückgegeben; wird kein Schlüssel benötigt oder erwartet, dann sollte NULL an dieser Stelle übergeben werden; der Funktionswert ist ein Zeiger auf den gefundenen Datensatz oder (dbif_rec *) 0, falls kein passender Datensatz gefunden wurde - oder - falls der gefundene Datensatz den Typ RT_INVALID hatte; eine als Parameter res_addr übergebene Variable bleibt unberührt, falls kein Datensatz oder ein Datensatz vom Typ RT_INVALID gefunden wurde.
dbif_rec *dbif_get (DBIF_FILE db, char *em_addr);
dbif_get liest einen einzelnen Datensatz aus der durch db referenzierten Datenbank; em_addr ist der Suchschlüssel; das Funktionsresultat ist ein Zeiger auf den gefundenen Datensatz; wurde kein passender Datensatz gefunden, dann wird (dbif_rec *) 0 zurückgegeben.
int dbif_insert (DBIF_FILE db, char *em_addr, dbif_rec *rec);
dbif_insert fügt einen neuen Datensatz rec unter dem Suchschlüssel em_addr in die durch db referenzierte Datenbank ein; nach einer erfolgreichen Operation ist das Funktionsresultat 0; ist schon ein Datensatz unter diesem Suchschlüssel vorhanden oder sonst irgendein Fehler aufgetreten, dann ist das Funktionsresultat ungleich 0.
int dbif_set (DBIF_FILE db, char *em_addr, dbif_rec *rec);
dbif_set fügt einen neuen Datensatz rec unter dem Suchschlüssel em_addr in die durch db referenzierte Datenbank ein oder überschreibt einen schon unter dem Suchschlüssel existierenden Datensatz; nach einer erfolgreichen Operation ist der Funktionswert 0; ist irgendein Fehler aufgetreten, dann ist der Funktionswert ungleich 0.
int dbif_del (DBIF_FILE db, char *em_addr);
dbif_del löscht einen unter dem Suchschlüssel em_addr stehenden Datensatz aus der durch db referenzierten Datenbank; Der Funktionswert ist 0 im Falle einer erfolgreich verlaufenen Operation und ungleich 0 im Falle eines Fehlers.
const char *dbif_errormsg (void);
dbif_errormsg gibt eine den letzten aufgetretenen Fehler beim Zugriff auf eine Datenbank beschreibende Zeichenkette zurück.
dbif_rec *dbif_mkrecl (char *passwd, size_t pwl, char *folder, size_t fll, int rtype, ...);
dbif_mkrecl erzeugt einen Benutzerdatensatz aus den Angaben passwd, folder, rtype, und - falls rtype == RT_DOMAIN oder rtype == RT_NSDOMAIN ist - der aktuellen Anzahl an Benutzern und der maximalen Anzahl an Benutzern (in dieser Reihenfolge) und gibt einen Zeiger auf diesen Datensatz als Funktionswert zurück. Diese Funktion erwartet die Längen von passwd und folder in den Parametern pwl bzw. fll.
dbif_rec *dbif_mkrec (char *passwd, char *folder, int rtype, ...);
dbif_mkrec erzeugt einen Benutzerdatensatz aus den Angaben passwd, folder, rtype, und - falls rtype == RT_DOMAIN oder rtype == RT_NSDOMAIN ist - der aktuellen Anzahl an Benutzern und der maximalen Anzahl an Benutzern (in dieser Reihenfolge) und gibt einen Zeiger auf diesen Datensatz als Funktionswert zurück. Diese Funktion erwartet ASCII NUL-terminierte Zeichenketten für passwd und folder.
int dbif_rectype (dbif_rec *r);
dbif_rectype gibt den Typ des Benutzerdatensatzes r zurück.
int dbif_setlim (dbif_rec *r, int nusers, int maxusers);
dbif_setlim trägt die aktuelle und die maximale Anzahl an Benutzern in den Datensatz r vom Typ RT_DOMAIN oder RT_NSDOMAIN ein; der Funktionswert ist 0, falls der übergebene Datensatz r den Typ RT_DOMAIN oder RT_NSDOMAIN hatte und ungleich 0 sonst.
int dbif_getlim (dbif_rec *r, int *nusers, int *maxusers);
dbif_getlim gibt die aktuelle und die maximale Anzahl an Benutzern des Datensatzes r vom Typ RT_DOMAIN bzw. RT_NSDOMAIN in den für nusers und maxusers übergebenen Variablen zurück.
void dbif_free (dbif_rec *p);
dbif_free gibt den für den Benutzerdatensatz alloziierten Speicherplatz vollständig wieder frei.
typedef int (*rrel_t) (char *lkey, dbif_rec *lrec, char *rkey, dbif_rec *rrec);
rrel_t beschreibt eine Vergleichsfunktion zwischen zwei (Schlüssel, Datensatz)-Paaren. Dieser Datentyp dient (lediglich) dazu, den Funktionsprototyp von dbif_select einfacher zu gestalten.
DBIF_CURSOR dbif_select (DBIF_FILE db, char *key, rrel_t ccfunc);
dbif_select wählt mit Hilfe des Suchschlüssels key und der Relation ccfunc Datensätze aus der durch db referenzierten Datenbank aus; Funktionswert ist ein DBIF_CURSOR handle, mit dem sequentiell auf die ausgewählten Datensätze zugegriffen werden kann.
char *dbif_first (DBIF_CURSOR dbc, dbif_rec **userrec);
dbif_first gibt den sequentiell ersten Datensatz aus der durch das DBIF_CURSOR handle dbc beschriebenen Auswahl zurück; hierbei ist der Funktionswert der zu diesem Datensatz gehörende Suchschlüssel; der eigentliche Datensatz wird in der für userrec übergebenen Variablen zurückgegeben; ist die Auswahl leer, dann wird (char *) 0 zurückgegeben; die für userrec übergebene Variable wird in diesem Fall nicht verändert.
char *dbif_next (DBIF_CURSOR dbc, dbif_rec **userrec);
dbif_next gibt den sequentiell jeweils nächsten Datensatz aus der durch das DBIF_CURSOR handle dbc beschriebenen Auswahl zurück; hierbei ist der Funktionswert der zu diesem Datensatz gehörende Suchschlüssel; der eigentliche Benutzerdatensatz wird in der für den Parameter userrec übergebenen Variablen zurückgegeben; ist kein weiterer Datensatz mehr vorhanden, dann wird (char*) 0 zurückgegeben; eine für userrec übergebene Variable wird in diesem Fall nicht verändert.
int dbif_end (DBIF_CURSOR dbc);
dbif_end gibt sämtliche für die durch das DBIF_CURSOR handle dbc referenzierte Auswahl an Datensätzen alloziierten Resourcen wieder frei.HINWEIS! Die durch ein DBIF_CURSOR handle referenzierten Datensätze müssen beliebig oft wieder gelesen werden können, d.h. eine Folge von Aufrufen
key = dbif_first (dbc, &userrec); key1 = dbif_next (dbc, &userrec1); ... ... key = dbif_first (dbc, &userrec); key1 = dbif_next (dbc, &userrec1);
muß möglich sein.
dbc = dbif_select(db, key, ccfunc);
Der Funktion dbif_select werden - neben der Referenz auf die Benutzerdatenbank noch zwei weitere Parameter übergeben, nämlich eine Zeichenkette key und eine Relation ccfunc (eine Benutzerdefinierte zweistellige Funktion von (Schlüssel, Datensatz)- Paaren mit booleschem Ergebnis):int ccrel(char *lk, dbif_rec *lr, char *rk, dbif_rec *rr);Diese Funktion wird innerhalb von dbif_select aufgerufen, und zwar mit dem zweiten Parameter von dbif_select (key) als erstem Argument; die weiteren Argumente sind
- der zu dem ersten Argument lk (2. Parameter von dbif_select (key)) gehörende Datensatz oder (dbif_rec *) 0, falls lk ==NULL ist
- der Schlüssel des Kandidaten für die Aufnahme in die Auswahl (rk)
- der Datensatz des Kandidaten für die Aufnahme in die Auswahl (rr).
Die an der Stelle von ccfunc dieser Stelle übergebene Vergleichsfunktion muß nur die Argumente auswerten, die wirklich für den Vergleich benötigt werden; so findet z.B. in der Funktion is_domain in vxdump.c nur ein Vergleich des Datensatztyps des Aufnahmekandidaten in die Auswahl statt, während in is_user zusätzlich noch ein Vergleich eines Teils des Schlüssels des Aufnahmekandidaten mit bei dbif_select übergebenen Schlüssel für einen Domaindatensatz statt.
Da prinzipiell als zweiter Parameter von dbif_select auch NULL übergeben werden darf, sollte eine Vergleichsfunktion - wenn sie eines der ersten beiden Argumente (lk oder lr) auswertet - das erste Argument (lk) auf NULL hin überprüfen, weil in diesem Fall das zweite Argument (dbif_rec *) 0 ist und somit Zugriffe zu einzelnen Elementen von lr illegalen Speicherzugriffen führen würden.
Die Versionen ab 1.38 von VxMail enthalten eine (sehr einfache) Fehlerbahandlung. Diese Fehlerbehandlung wurde von mir zum Teil aus der Fehlerbehandlung der GDBM-Funktionsbibliothek übertragen; da ich jedoch nicht alle Fehlercodes aus GDBM benötige, habe ich die Anzahl der möglichen Fehler eingeschränkt. Ich unterstütze folgendeFehlercodes:
DBIF_NOERR | kein Fehler, die letzte Operation war erfolgreich. |
DBIF_ENOMEM | Fehler bei der Alloziierung von dynamischem Speicher (malloc) |
DBIF_ACFAIL | Fehler beim Öffnen einer der Datenbankdateien |
DBIF_WRFAIL | Fehler beim Schreiben in eine der Datenbankdateien |
DBIF_RDFAIL | Fehler beim Lesen aus einer der Datenbankdateien |
DBIF_SLFAIL | Fehler beim Positionieren innerhalb einer der Datenbankdateien |
DBIF_NOWR | ein Schreibzugriff ist (momentan) nicht möglich (dbif_create oder dbif_rw) oder eine Schreiboperation mit einem NUR-Lesezugang zur Datenbank. |
DBIF_NORD | ein Lesezugriff ist (momentan) nicht möglich (dbif_reset) |
DBIF_NOIT | ein Datensatz zu dem angegebenen Schlüssel wurde nicht gefunden |
DBIF_NODB | ungültige/kaputte Datenbank |
DBIF_INT | interner Fehler der zugrundeliegenden Datenbank |
DBIF_DATA | fehlerhafte Daten |
DBIF_CCFAIL | select fehlgeschlagen (dbif_select) - es konnte keine temporäre Datei zur Aufnahme der Ergebnisse angelegt werden. |
Diese Fehlercodes müssen von einer Implementation der Datenbankschnittstelle nicht alle erzeugt werden - vom VxMail-System wird ohnehin (momentan) nur DBIF_NOWR direkt verwendet. Es sollten jedoch mindestens DBIF_INT, DBIF_NOMEM und DBIF_NOERR implementiert sein.
dbif_errormsg kann (und sollte) unabhängig von diesen Fehlercodes bzw. dbif_errno - der Funktion, die diese Fehlercodes zurückgibt - implementiert sein.
gibt den Fehlercode der letzten Datenbankoperation zurück.