Beschreibung der Datenbankschnittstelle

Abstract

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.

1  Datenbankformat

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:

key
eine ASCII NUL-terminierte Zeichenkette, die eindeutig den betreffenden Datensatz identifiziert; dieser Schlüssel taucht jedoch nicht in den Elementen des Datentyps dbif_rec auf - er wird auch nicht immer dort benötigt; stattdessen wird er an Stellen, wo er benötigt wird als Ergebnis der betreffenden Funktion oder über einen zusätzlichen Parameter zurückgegeben.


passwd
eine ASCII NUL-terminierte Zeichenkette; für normale Benutzereinträge, den Mail-Systemadministrator, eine DOMAIN-Umleitung und den (optionalen) VXTS-Datensatz enthält dieser Eintrag einen Schlüssel zur Authentifizierung; im Fall des speziellen Eintrags ::VxMail:: steht an dieser Stelle der Name des Rechners, unter dem das VxMail-System laufen soll - dieser ist vor allem für die Zusendung abgewiesener E-Mails und die spezielle - bei einer Info-Mail über die Umleitung des POP3-Services auf einen anderen Host interessant.


folder
Dieses Feld enthält - abhängig von dem Typ des Datensatzes - sehr unterschiedliche Informationen:

RT_USER
der vollständige Pfadname des E-Mail-Folders des Benutzers


RT_LINK
die E-Mail-Adresse des Benutzers, auf die dieser Eintrag verweist


RT_NSUSER
(siehe RT_USER)


RT_ADMIN
(siehe RT_USER)


RT_DOMAIN
der vollständige Pfadname des Verzeichnisses, in dem die Benutzer dieser Domain stehen sollen


RT_DOMLINK
der Name der Domain, auf die dieser Eintrag verweist


RT_NSDOMAIN
(siehe RT_DOMAIN)


RT_LLINK
das, worauf verwiesen wird.


RT_MAILSYS
der vollständige Pfadname des Folders des Mail-Systemadministrators des VxMail-Systems


RT_IP4TRANS
der Name der Domain, auf die dieser Eintrag verweist


RT_IP6TRANS
der Name der Domain, auf die dieser Eintrag verweist


RT_REDIRECT
der Name des Rechners, auf dem die Domain eigentlich verwaltet wird


RT_IDENTITY
der Name des Default-Domainadministrators
RT_VXTS
- unbelegt -


RT_INVALID
(siehe RT_USER)


RT_FILTER
der (vollständige) Pfadname der Filterdatei

flags
Dort steht der Typ des Datensatzes sowie - im Falle einer Domain (RT_DOMAIN bzw. RT_NSDOMAIN)- die aktuelle und die maximale Anzahl von Benutzern innerhalb dieser Domain

Folgende Dinge sind unbedingt zu beachten:

2  Beschreibung der Funktionen der Datenbankschnittstelle

2.1  Benutzerdatensatz:

    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.

2.2  DBIF_FILE handle

    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.

2.3  DBIF_CURSOR handle

    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.

2.4  Verbinden mit einer Datenbank

    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.

2.5  Beenden einer Datenbankverbindung

    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.

2.6  Zugriff auf einzelne Datensätze

    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.

2.7  Fehlerbehandlungsfunktion(en)

    const char *dbif_errormsg (void);
dbif_errormsg gibt eine den letzten aufgetretenen Fehler beim Zugriff auf eine Datenbank beschreibende Zeichenkette zurück.

2.8  Erzeugung von Benutzerdatensätzen im Speicher

    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.

2.9  Typ und Benutzerlimits von Benutzerdatensätzen

    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.

2.10  Speicherfreigabe für Benutzerdatensätze

    void dbif_free (dbif_rec *p);
dbif_free gibt den für den Benutzerdatensatz alloziierten Speicherplatz vollständig wieder frei.

2.11  Relationstyp (3. Argument von dbif_select)

    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.

2.12  Zugriff auf eine Auswahl von Datensätzen

    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.

3  Beschreibung der Bedingungsparameter für dbif_select

    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

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.

4  Fehlerbehandlung

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.

dbif_errno ()

gibt den Fehlercode der letzten Datenbankoperation zurück.


File translated from TEX by TTH, version 2.60.
On 6 Jan 2000, 16:20.