/*
 * ratDbFolder.c --
 *
 *      This file contains code which implements standard c-client folders.
 *
 * TkRat software and its included text is Copyright 1996-2002 by
 * Martin Forssén
 *
 * The full text of the legal notice is contained in the file called
 * COPYRIGHT, included with this distribution.
 */

#include <time.h>
#include "ratFolder.h"


/*
 * This is the private part of a Db folder info structure.
 */

typedef struct DbFolderInfo {
    int *listPtr;		/* List of messages in this folder */
    Tcl_Obj *searchExpr;       	/* The search expression used to create
				 * this folder. */
    char *keywords;		/* Keywords to add to inserted messages */
    char *exDate;		/* Expiration date of inserted messages */
    char *exType;		/* Expiration type of new messages */
    Tcl_Obj **infoPtr;		/* List of information caches */
} DbFolderInfo;

typedef enum {Db_Name, Db_Mail} DbAdrInfo;

/*
 * Procedures private to this module.
 */
static RatInitProc Db_InitProc;
static RatCloseProc Db_CloseProc;
static RatUpdateProc Db_UpdateProc;
static RatInsertProc Db_InsertProc;
static RatSetFlagProc Db_SetFlagProc;
static RatGetFlagProc Db_GetFlagProc;
static RatCreateProc Db_CreateProc;
static RatSetInfoProc Db_SetInfoProc;
static int GetAddressInfo(Tcl_Interp *interp, Tcl_DString *dsPtr, char *adr,
	DbAdrInfo info);


/*
 *----------------------------------------------------------------------
 *
 * RatDbFolderInit --
 *
 *      Initializes the dbase folder data.
 *
 * Results:
 *      The return value is normally TCL_OK; if something goes wrong
 *	TCL_ERROR is returned and an error message will be left in
 *	the result area.
 *
 * Side effects:
 *	The C-client library is initialized and the apropriate mail drivers
 *	are linked.
 *
 *
 *----------------------------------------------------------------------
 */

int
RatDbFolderInit(Tcl_Interp *interp)
{
    Tcl_CreateObjCommand(interp, "RatInsert", RatInsertCmd, (ClientData) NULL,
	    (Tcl_CmdDeleteProc *) NULL);
    return TCL_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * RatDbFolderCreate --
 *
 *      Creates a db folder entity.
 *
 * Results:
 *      The return value is normally TCL_OK; if something goes wrong
 *	TCL_ERROR is returned and an error message will be left in
 *	the result area.
 *
 * Side effects:
 *	A db folder is created.
 *
 *
 *----------------------------------------------------------------------
 */

RatFolderInfo*
RatDbFolderCreate(Tcl_Interp *interp, Tcl_Obj *defPtr)
{
    RatFolderInfo *infoPtr;
    DbFolderInfo *dbPtr;
    int *listPtr, number, i, objc, eobjc;
    RatDbEntry *entryPtr;
    Tcl_Obj **objv, **eobjv;

    Tcl_ListObjGetElements(interp, defPtr, &objc, &objv);
    
    Tcl_IncrRefCount(objv[5]);
    if (TCL_OK != RatDbSearch(interp, objv[5], &number, &listPtr)) {
	Tcl_DecrRefCount(objv[5]);
	RatLogF(interp, RAT_ERROR, "dbase_error", RATLOG_TIME,
		Tcl_GetStringResult(interp));
	Tcl_ResetResult(interp);
	Tcl_AppendResult(interp, "Failed to search dbase \"",
		Tcl_GetString(objv[5]), "\"", (char *) NULL);
	return (RatFolderInfo *) NULL;
    }

    infoPtr = (RatFolderInfo *) ckalloc(sizeof(*infoPtr));
    dbPtr = (DbFolderInfo *) ckalloc(sizeof(*dbPtr));

    infoPtr->name = cpystr("Database search");
    infoPtr->type = "dbase";
    infoPtr->number = number;
    infoPtr->recent = 0;
    infoPtr->unseen = 0;
    for (i=0; i<infoPtr->number; i++) {
	entryPtr = RatDbGetEntry(listPtr[i]);
	if (!strchr(entryPtr->content[STATUS], 'O')) {
	    infoPtr->recent++;
	}
	if (!strchr(entryPtr->content[STATUS], 'R')) {
	    infoPtr->unseen++;
	}
    }
    infoPtr->size = 0;
    for (i=0; i<number; i++) {
	infoPtr->size += atoi(RatDbGetEntry(listPtr[i])->content[RSIZE]);
    }
    infoPtr->initProc = Db_InitProc;
    infoPtr->finalProc = NULL;
    infoPtr->closeProc = Db_CloseProc;
    infoPtr->updateProc = Db_UpdateProc;
    infoPtr->insertProc = Db_InsertProc;
    infoPtr->setFlagProc = Db_SetFlagProc;
    infoPtr->getFlagProc = Db_GetFlagProc;
    infoPtr->infoProc = Db_InfoProc;
    infoPtr->setInfoProc = Db_SetInfoProc;
    infoPtr->createProc = Db_CreateProc;
    infoPtr->syncProc = NULL;
    infoPtr->private = (ClientData) dbPtr;
    dbPtr->listPtr = listPtr;
    dbPtr->searchExpr = objv[5];
    Tcl_ListObjGetElements(interp, objv[5], &eobjc, &eobjv);
    dbPtr->keywords = NULL;
    for (i=0; i<eobjc-1; i++) {
	if (!strcmp("keywords", Tcl_GetString(eobjv[i]))) {
	    dbPtr->keywords = cpystr(Tcl_GetString(eobjv[i+1]));
	    break;
	}
    }
    dbPtr->exDate = cpystr(Tcl_GetString(objv[4]));
    dbPtr->exType = cpystr(Tcl_GetString(objv[3]));
    dbPtr->infoPtr = (Tcl_Obj**)ckalloc(sizeof(Tcl_Obj*) * number *
					RAT_FOLDER_END);
    for (i=0; i < number*RAT_FOLDER_END; i++) {
	dbPtr->infoPtr[i] = NULL;
    }

    return infoPtr;
}


/*
 *----------------------------------------------------------------------
 *
 * Db_InitProc --
 *
 *      See the documentation for initProc in ratFolder.h
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	See the documentation for initProc in ratFolder.h
 *
 *
 *----------------------------------------------------------------------
 */
static void
Db_InitProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int index)
{
    return;
}


/*
 *----------------------------------------------------------------------
 *
 * Db_CloseProc --
 *
 *      See the documentation for closeProc in ratFolder.h
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	See the documentation for closeProc in ratFolder.h
 *
 *
 *----------------------------------------------------------------------
 */
static int
Db_CloseProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int expunge)
{
    DbFolderInfo *dbPtr = (DbFolderInfo *) infoPtr->private;
    int i;

    ckfree(dbPtr->listPtr);
    Tcl_DecrRefCount(dbPtr->searchExpr);
    ckfree(dbPtr->keywords);
    ckfree(dbPtr->exDate);
    ckfree(dbPtr->exType);
    for (i=0; i<infoPtr->number*RAT_FOLDER_END; i++) {
	if (dbPtr->infoPtr[i]) {
	    Tcl_DecrRefCount(dbPtr->infoPtr[i]);
	}
    }
    ckfree(dbPtr->infoPtr);
    ckfree(dbPtr);
    return TCL_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * Db_UpdateProc --
 *
 *      See the documentation for updateProc in ratFolder.h
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	See the documentation for updateProc in ratFolder.h
 *
 *
 *----------------------------------------------------------------------
 */
static int
Db_UpdateProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, RatUpdateType mode)
{
    DbFolderInfo *dbPtr = (DbFolderInfo *) infoPtr->private;
    int *listPtr, number, numNew, i;
    RatDbEntry *entryPtr;

    if (RAT_SYNC == mode) {
	int i, j, dst;

	if (TCL_OK != RatDbExpunge(interp)) {
	    return -1;
	}
	infoPtr->size = 0;
	for (i=dst=0; i<infoPtr->number; i++) {
	    if ((entryPtr = RatDbGetEntry(dbPtr->listPtr[i]))) {
		dbPtr->listPtr[dst] = dbPtr->listPtr[i];
		infoPtr->msgCmdPtr[dst] = infoPtr->msgCmdPtr[i];
		infoPtr->size += atoi(entryPtr->content[RSIZE]);
		for (j=0; j<RAT_FOLDER_END; j++) {
		    dbPtr->infoPtr[dst*RAT_FOLDER_END+j] =
			    dbPtr->infoPtr[i*RAT_FOLDER_END+j];
		}
		dst++;
	    } else {
		if (infoPtr->msgCmdPtr[i]) {
		    RatMessageDelete(interp, infoPtr->msgCmdPtr[i]);
		}
		for (j=0; j<RAT_FOLDER_END; j++) {
		    if (dbPtr->infoPtr[i*RAT_FOLDER_END+j]) {
			Tcl_DecrRefCount(dbPtr->infoPtr[i*RAT_FOLDER_END+j]);
		    }
		}
	    }
	}
	infoPtr->number = dst;
    }

    numNew = 0;
    if (RAT_SYNC == mode || RAT_UPDATE == mode) {
	if (TCL_OK != RatDbSearch(interp, dbPtr->searchExpr,&number,&listPtr)){
	    RatLogF(interp, RAT_ERROR, "dbase_error", RATLOG_TIME,
		    Tcl_GetStringResult(interp));
	    Tcl_ResetResult(interp);
	    Tcl_AppendResult(interp, "Failed to search dbase \"",
		    Tcl_GetString(dbPtr->searchExpr), "\"", (char *) NULL);
	    return -1;
	}
	for (i=0 ; number == infoPtr->number
		&& i<number
		&& listPtr[i] == dbPtr->listPtr[i];
		i++);
	if (i != number || i != infoPtr->number) {
	    for (i=0; i<infoPtr->number*RAT_FOLDER_END; i++) {
		if (dbPtr->infoPtr[i]) {
		    Tcl_DecrRefCount(dbPtr->infoPtr[i]);
		}
	    }
	    ckfree(dbPtr->infoPtr);
	    ckfree(dbPtr->listPtr);
	    dbPtr->listPtr = listPtr;
	    numNew = infoPtr->number - number;
	    infoPtr->number = number;
	    dbPtr->infoPtr = (Tcl_Obj**)
		    ckalloc(sizeof(Tcl_Obj*)*number*RAT_FOLDER_END);
	    for (i=0; i<number*RAT_FOLDER_END; i++) {
		dbPtr->infoPtr[i] = NULL;
	    }
	}
	infoPtr->recent = 0;
	infoPtr->unseen = 0;
	for (i=0; i<infoPtr->number; i++) {
	    entryPtr = RatDbGetEntry(dbPtr->listPtr[i]);
	    if (!strchr(entryPtr->content[RAT_FOLDER_STATUS], 'O')) {
		infoPtr->recent++;
	    }
	    if (!strchr(entryPtr->content[RAT_FOLDER_STATUS], 'R')) {
		infoPtr->unseen++;
	    }
	}
    }
    return numNew;
}


/*
 *----------------------------------------------------------------------
 *
 * Db_InsertProc --
 *
 *      See the documentation for insertProc in ratFolder.h
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	See the documentation for insertProc in ratFolder.h
 *
 *
 *----------------------------------------------------------------------
 */
static int
Db_InsertProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int argc,
	char *argv[])
{
    DbFolderInfo *dbPtr = (DbFolderInfo *) infoPtr->private;
    Tcl_CmdInfo cmdInfo;
    int i;

    for (i=0; i<argc; i++) {
	if (0 == Tcl_GetCommandInfo(interp, argv[i], &cmdInfo)) {
	    Tcl_AppendResult(interp, "No such message: ", argv[i], NULL);
	    return TCL_ERROR;
	}
	RatInsertMsg(interp, (MessageInfo*)cmdInfo.objClientData,
		dbPtr->keywords, dbPtr->exDate, dbPtr->exType);
    }
    return TCL_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * Db_SetFlagProc --
 *
 *      See the documentation for setFlagProc in ratFolder.h
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	See the documentation for setFlagProc in ratFolder.h
 *
 *
 *----------------------------------------------------------------------
 */
static int
Db_SetFlagProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int index,
	RatFlag flag, int value)
{
    DbFolderInfo *dbPtr = (DbFolderInfo *) infoPtr->private;
    RatDbEntry *entryPtr = RatDbGetEntry(dbPtr->listPtr[index]);
    char newStatus[5];
    int flagArray[4] = {0,0,0,0};
    int dst, i;

    for (i=0; entryPtr->content[STATUS][i]; i++) {
	switch(entryPtr->content[STATUS][i]) {
	    case 'R':	flagArray[RAT_SEEN] = 1; break;
	    case 'D':	flagArray[RAT_DELETED] = 1; break;
	    case 'F':	flagArray[RAT_FLAGGED] = 1; break;
	    case 'A':	flagArray[RAT_ANSWERED] = 1; break;
	    case 'T':	flagArray[RAT_DRAFT] = 1; break;
	    case 'O':	flagArray[RAT_RECENT] = 1; break;
	}
    }
    flagArray[flag] = value;
    dst = 0;
    if (flagArray[RAT_SEEN]) { newStatus[dst++] = 'R'; }
    if (flagArray[RAT_DELETED]) { newStatus[dst++] = 'D'; }
    if (flagArray[RAT_FLAGGED]) { newStatus[dst++] = 'F'; }
    if (flagArray[RAT_ANSWERED]) { newStatus[dst++] = 'A'; }
    if (flagArray[RAT_DRAFT]) { newStatus[dst++] = 'T'; }
    if (flagArray[RAT_RECENT]) { newStatus[dst++] = 'O'; }
    newStatus[dst] = '\0';
    i = index*RAT_FOLDER_END+RAT_FOLDER_STATUS;
    if (dbPtr->infoPtr[i]) {
	Tcl_DecrRefCount(dbPtr->infoPtr[i]);
	dbPtr->infoPtr[i] = NULL;
    }
    return RatDbSetStatus(interp, dbPtr->listPtr[index], newStatus);
}


/*
 *----------------------------------------------------------------------
 *
 * Db_GetFlagProc --
 *
 *      See the documentation for getFlagProc in ratFolder.h
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	See the documentation for getFlagProc in ratFolder.h
 *
 *
 *----------------------------------------------------------------------
 */
static int
Db_GetFlagProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int index,
	RatFlag flag)
{
    DbFolderInfo *dbPtr = (DbFolderInfo *) infoPtr->private;
    RatDbEntry *entryPtr = RatDbGetEntry(dbPtr->listPtr[index]);
    char flagChar;
    int i;

    switch(flag) {
	case RAT_SEEN:		flagChar = 'R'; break;
	case RAT_DELETED:	flagChar = 'D'; break;
	case RAT_FLAGGED:	flagChar = 'F'; break;
	case RAT_ANSWERED:	flagChar = 'A'; break;
	case RAT_DRAFT:		flagChar = 'T'; break;
	case RAT_RECENT:	flagChar = 'O'; break;
	default:		return 0;
    }
    for (i=0; entryPtr->content[STATUS][i]; i++) {
	if (entryPtr->content[STATUS][i] == flagChar) {
	    return 1;
	}
    }
    return 0;
}


/*
 *----------------------------------------------------------------------
 *
 * GetAddressInfo --
 *
 *      Gets info from an address. The info argument decides what we
 *	wants to extract. The requested data will be appeded to dsPtr.
 *
 * Results:
 *	Returns true if this address points to me.
 *
 * Side effects:
 *	The dsPtr DString will be modified
 *
 *
 *----------------------------------------------------------------------
 */

static int
GetAddressInfo(Tcl_Interp *interp, Tcl_DString *dsPtr, char *adr,
	DbAdrInfo info)
{
    ADDRESS *addressPtr = NULL;
    char *s, *host;
    int ret;

    host = RatGetCurrent(interp, RAT_HOST, "");
    s = cpystr(adr);
    rfc822_parse_adrlist(&addressPtr, s, host);
    ckfree(s);
    if (!addressPtr) {
	return 0;
    }
    ret = RatAddressIsMe(interp, addressPtr, 1);

    if (Db_Name == info && addressPtr->personal) {
	Tcl_DStringAppend(dsPtr, addressPtr->personal, -1);
    } else {
	Tcl_DStringAppend(dsPtr, RatAddressMail(addressPtr), -1);
    }
    mail_free_address(&addressPtr);
    return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Db_InfoProc --
 *
 *      See the documentation for infoProc in ratFolder.h
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	See the documentation for infoProc in ratFolder.h
 *
 *
 *----------------------------------------------------------------------
 */

Tcl_Obj*
Db_InfoProc(Tcl_Interp *interp, ClientData clientData, RatFolderInfoType type,
	int index)
{
    RatFolderInfo *infoPtr = (RatFolderInfo*)clientData;

    return Db_InfoProcInt(interp, infoPtr, type, index);
}


/*
 *----------------------------------------------------------------------
 *
 * Db_InfoProcInt --
 *
 *      See the documentation for infoProc in ratFolder.h. The difference
 *	between this function and Db_InfoProc is that this expects the
 *	index (rIndex) to be the real folder index.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	See the documentation for infoProc in ratFolder.h
 *
 *
 *----------------------------------------------------------------------
 */

Tcl_Obj*
Db_InfoProcInt(Tcl_Interp *interp, RatFolderInfo *infoPtr,
	RatFolderInfoType type, int rIndex)
{
    static Tcl_DString ds;
    static int initialized = 0;
    static char buf[1024];
    int i, seen, deleted, marked, answered, me, dbIndex, zone, f;
    DbFolderInfo *dbPtr = (DbFolderInfo*)infoPtr->private;
    ADDRESS *addressPtr, *address2Ptr;
    Tcl_Obj *oPtr = NULL;
    MESSAGECACHE elt;
    RatDbEntry *entryPtr;
    struct tm *tmPtr;
    Tcl_CmdInfo info;
    time_t time;
    char *host;

    dbIndex = dbPtr->listPtr[rIndex];

    if (dbPtr->infoPtr[rIndex*RAT_FOLDER_END+type]) {
	if (type == RAT_FOLDER_INDEX) {
	    Tcl_GetIntFromObj(interp,
		    dbPtr->infoPtr[rIndex*RAT_FOLDER_END+type], &i);
	    if (i < infoPtr->visible
		&& dbIndex == dbPtr->listPtr[infoPtr->presentationOrder[i]]) {
		return dbPtr->infoPtr[rIndex*RAT_FOLDER_END+type];
	    }
	} else {
	    return dbPtr->infoPtr[rIndex*RAT_FOLDER_END+type];
	}
    }

    entryPtr = RatDbGetEntry(dbIndex);

    if (!initialized) {
	Tcl_DStringInit(&ds);
	initialized = 1;
    }

    switch (type) {
	case RAT_FOLDER_SUBJECT:
	    oPtr = Tcl_NewStringObj(entryPtr->content[SUBJECT], -1);
	    break;
	case RAT_FOLDER_CANONSUBJECT:
	    oPtr = RatFolderCanonalizeSubject(entryPtr->content[SUBJECT]);
	    break;
	case RAT_FOLDER_NAME:
	    Tcl_DStringSetLength(&ds, 0);
	    if (GetAddressInfo(interp, &ds, entryPtr->content[FROM],Db_Name)) {
		Tcl_DStringSetLength(&ds, 0);
		Tcl_DStringAppend(&ds,
			Tcl_GetVar2(interp, "t", "to", TCL_GLOBAL_ONLY), -1);
		Tcl_DStringAppend(&ds, ": ", 2);
		GetAddressInfo(interp, &ds, entryPtr->content[TO], Db_Name);
	    }
	    oPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1);
	    break;
	case RAT_FOLDER_MAIL_REAL:
	    Tcl_DStringSetLength(&ds, 0);
	    GetAddressInfo(interp, &ds, entryPtr->content[FROM], Db_Mail);
	    oPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1);
	    break;
	case RAT_FOLDER_MAIL:
	    Tcl_DStringSetLength(&ds, 0);
	    if (GetAddressInfo(interp, &ds, entryPtr->content[FROM],Db_Mail)) {
		Tcl_DStringSetLength(&ds, 0);
		Tcl_DStringAppend(&ds,
			Tcl_GetVar2(interp, "t", "to", TCL_GLOBAL_ONLY), -1);
		Tcl_DStringAppend(&ds, ": ", 2);
		GetAddressInfo(interp, &ds, entryPtr->content[TO], Db_Mail);
	    }
	    oPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1);
	    break;
	case RAT_FOLDER_NAME_RECIPIENT:
	    if (RatIsEmpty(entryPtr->content[TO])) {
		oPtr = Tcl_NewStringObj(entryPtr->content[TO], -1);
		break;
	    }
	    Tcl_DStringSetLength(&ds, 0);
	    if (GetAddressInfo(interp, &ds, entryPtr->content[TO], Db_Name)) {
		Tcl_DStringSetLength(&ds, 0);
		Tcl_DStringAppend(&ds,
			Tcl_GetVar2(interp, "t", "from", TCL_GLOBAL_ONLY), -1);
		Tcl_DStringAppend(&ds, ": ", 2);
		GetAddressInfo(interp, &ds, entryPtr->content[FROM], Db_Name);
	    }
	    oPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1);
	    break;
	case RAT_FOLDER_MAIL_RECIPIENT:
	    if (RatIsEmpty(entryPtr->content[TO])) {
		oPtr = Tcl_NewStringObj(entryPtr->content[TO], -1);
		break;
	    }
	    Tcl_DStringSetLength(&ds, 0);
	    if (GetAddressInfo(interp, &ds, entryPtr->content[TO], Db_Mail)) {
		Tcl_DStringSetLength(&ds, 0);
		Tcl_DStringAppend(&ds,
			Tcl_GetVar2(interp, "t", "from", TCL_GLOBAL_ONLY), -1);
		Tcl_DStringAppend(&ds, ": ", 2);
		GetAddressInfo(interp, &ds, entryPtr->content[FROM], Db_Mail);
	    }
	    oPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1);
	    break;
	case RAT_FOLDER_SIZE:
	    oPtr = Tcl_NewIntObj(atoi(entryPtr->content[RSIZE]));
	    break;
	case RAT_FOLDER_SIZE_F:
	    oPtr = RatMangleNumber(atoi(entryPtr->content[RSIZE]));
	    break;
	case RAT_FOLDER_DATE_F:
	    time = atoi(entryPtr->content[DATE]);
	    tmPtr = localtime(&time);
	    oPtr = RatFormatDate(interp, tmPtr->tm_mon, tmPtr->tm_mday);
	    break;
	case RAT_FOLDER_DATE_N:
	    oPtr = Tcl_NewStringObj(entryPtr->content[DATE], -1);
	    break;
	case RAT_FOLDER_DATE_IMAP4:
	    Tcl_DStringSetLength(&ds, 256);
	    time = atoi(entryPtr->content[DATE]);
	    tmPtr = localtime(&time);
	    elt.day = tmPtr->tm_mday;
	    elt.month = tmPtr->tm_mon+1;
	    elt.year = tmPtr->tm_year+1900-BASEYEAR;
	    elt.hours = tmPtr->tm_hour;
	    elt.minutes = tmPtr->tm_min;
	    elt.seconds = tmPtr->tm_sec;
	    zone = RatGetTimeZone(time);
	    if (zone >= 0) {
		elt.zoccident = 1;
		elt.zhours = zone/(60*60);
		elt.zminutes = (zone%(60*60))/60;
	    } else {
		elt.zoccident = 0;
		elt.zhours = (-1*zone)/(60*60);
		elt.zminutes = ((-1*zone)%(60*60))/60;
	    }
	    oPtr = Tcl_NewStringObj(mail_date(Tcl_DStringValue(&ds), &elt),-1);
	    break;
	case RAT_FOLDER_STATUS:
	    seen = deleted = marked = answered = me = 0;
	    for (i=0; entryPtr->content[STATUS][i]; i++) {
		switch (entryPtr->content[STATUS][i]) {
		    case 'R': seen = 1;		break;
		    case 'D': deleted = 1;	break;
		    case 'F': marked = 1;	break;
		    case 'A': answered = 1;	break;
		}
	    }
	    addressPtr = NULL;
	    if (!RatIsEmpty(entryPtr->content[TO])) {
		Tcl_DStringSetLength(&ds, 0);
		Tcl_DStringAppend(&ds, entryPtr->content[TO], -1);
		host = RatGetCurrent(interp, RAT_HOST, "");
		rfc822_parse_adrlist(&addressPtr, Tcl_DStringValue(&ds), host);
		for (address2Ptr = addressPtr; !me && address2Ptr;
			address2Ptr = address2Ptr->next) {
		    if (RatAddressIsMe(interp, address2Ptr, 1)) {
			me = 1;
		    }
		}
	    }
	    mail_free_address(&addressPtr);
	    i = 0;
	    if (!seen) {
		buf[i++] = 'N';
	    }
	    if (deleted) {
		buf[i++] = 'D';
	    }
	    if (marked) {
		buf[i++] = 'F';
	    }
	    if (answered) {
		buf[i++] = 'A';
	    }
	    if (me) {
		buf[i++] = '+';
	    } else {
		buf[i++] = ' ';
	    }
	    buf[i] = '\0';
	    oPtr = Tcl_NewStringObj(buf, -1);
	    break;
	case RAT_FOLDER_TYPE:		
	    return NULL;
	case RAT_FOLDER_PARAMETERS:
	    return NULL;
	case RAT_FOLDER_INDEX:
	    for (i=0; i < infoPtr->number; i++) {
		if (dbIndex == dbPtr->listPtr[infoPtr->presentationOrder[i]]) {
		    sprintf(buf, "%d", i+1);
		    oPtr = Tcl_NewStringObj(buf, -1);
		    break;
		}
	    }
	    if (i == infoPtr->number) {
		oPtr = Tcl_NewStringObj("1", 1);
	    }
	    break;
	case RAT_FOLDER_TO:
	    oPtr = Tcl_NewStringObj(entryPtr->content[TO], -1);
	    break;
	case RAT_FOLDER_FROM:
	    oPtr = Tcl_NewStringObj(entryPtr->content[FROM], -1);
	    break;
	case RAT_FOLDER_SENDER:		/* fallthrough */
	case RAT_FOLDER_CC:		/* fallthrough */
	case RAT_FOLDER_REPLY_TO:
	    if (NULL == infoPtr->msgCmdPtr[rIndex]) {
		infoPtr->msgCmdPtr[rIndex] =
		    Db_CreateProc(infoPtr, interp, rIndex);
	    }
	    Tcl_GetCommandInfo(interp, infoPtr->msgCmdPtr[rIndex], &info);
	    oPtr = RatMsgInfo(interp, (MessageInfo*)info.objClientData, type);
	    break;
	case RAT_FOLDER_FLAGS:
	    Tcl_DStringSetLength(&ds, 0);
	    for (i=0; entryPtr->content[STATUS][i]; i++) {
		for (f=0; flag_name[f].imap_name; f++) {
		    if (flag_name[f].unix_char==entryPtr->content[STATUS][i]) {
			Tcl_DStringAppend(&ds, " ", -1);
			Tcl_DStringAppend(&ds, flag_name[f].imap_name, -1);
			break;
		    }
		}
	    }
	    if (Tcl_DStringLength(&ds)) {
		oPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds)+1, -1);
	    } else {
		oPtr = Tcl_NewStringObj("", 0);
	    }
	    break;
	case RAT_FOLDER_UNIXFLAGS:
	    oPtr = Tcl_NewStringObj(entryPtr->content[STATUS], -1);
	    break;
	case RAT_FOLDER_MSGID:
	    oPtr = Tcl_NewStringObj(entryPtr->content[MESSAGE_ID], -1);
	    break;
	case RAT_FOLDER_REF:
	    oPtr = Tcl_NewStringObj(entryPtr->content[REFERENCE], -1);
	    break;
	case RAT_FOLDER_THREADING:
	    return NULL;
	case RAT_FOLDER_END:
	    break;
    }
    dbPtr->infoPtr[rIndex*RAT_FOLDER_END+type] = oPtr;
    return oPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * Db_CreateProc --
 *
 *      See the documentation for createProc in ratFolder.h
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	See the documentation for createProc in ratFolder.h
 *
 *
 *----------------------------------------------------------------------
 */
static char*
Db_CreateProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int index)
{
    DbFolderInfo *dbPtr = (DbFolderInfo *) infoPtr->private;
    return RatDbMessageCreate(interp, infoPtr, index, dbPtr->listPtr[index]);
}

/*
 *----------------------------------------------------------------------
 *
 * Db_SetInfoProc --
 *
 *      Sets information about a message
 *
 * Results:
 *	None
 *
 * Side effects:
 *	None
 *
 *
 *----------------------------------------------------------------------
 */
static void
Db_SetInfoProc(Tcl_Interp *interp, ClientData clientData,
	RatFolderInfoType type, int index, Tcl_Obj *oPtr)
{
    RatFolderInfo *infoPtr = (RatFolderInfo*)clientData;
    DbFolderInfo *dbPtr = (DbFolderInfo*)infoPtr->private;
    int i = index*RAT_FOLDER_END+type;

    if (dbPtr->infoPtr[i]) {
	Tcl_DecrRefCount(dbPtr->infoPtr[i]);
    }
    dbPtr->infoPtr[i] = oPtr;
    if (oPtr) {
	Tcl_IncrRefCount(oPtr);
    }
}


syntax highlighted by Code2HTML, v. 0.9.1