/*
 * ratFrMessage.c --
 *
 *	This file contains code which implements free messages.
 *
 * 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 "ratFolder.h"

/*
 * The ClientData for each message entity
 */
typedef struct FrMessageInfo {
    MESSAGE *messagePtr;
    char *from;
    char *headers;
    char *msgData;
    unsigned char *bodyData;
} FrMessageInfo;

/*
 * The ClientData for each bodypart entity
 */
typedef struct FrBodyInfo {
    unsigned char *text;
} FrBodyInfo;

/*
 * The number of message entities created. This is used to create new
 * unique command names.
 */
static int numFrMessages = 0;

static RatGetHeadersProc Fr_GetHeadersProc;
static RatGetEnvelopeProc Fr_GetEnvelopeProc;
static RatFetchTextProc Fr_FetchTextProc;
static RatEnvelopeProc Fr_EnvelopeProc;
static RatMsgDeleteProc Fr_MsgDeleteProc;
static RatMakeChildrenProc Fr_MakeChildrenProc;
static RatFetchBodyProc Fr_FetchBodyProc;
static RatBodyDeleteProc Fr_BodyDeleteProc;
static RatInfoProc Fr_GetInfoProc;
static RatGetInternalDateProc Fr_GetInternalDateProc;


/*
 *----------------------------------------------------------------------
 *
 * RatFrMessagesInit --
 *
 *      Initializes the given MessageProcInfo entry for a free message
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The given MessageProcInfo is initialized.
 *
 *
 *----------------------------------------------------------------------
 */

void
RatFrMessagesInit(MessageProcInfo *messageProcInfoPtr)
{
    messageProcInfoPtr->getHeadersProc = Fr_GetHeadersProc;
    messageProcInfoPtr->getEnvelopeProc = Fr_GetEnvelopeProc;
    messageProcInfoPtr->getInfoProc = Fr_GetInfoProc;
    messageProcInfoPtr->createBodyProc = Fr_CreateBodyProc;
    messageProcInfoPtr->fetchTextProc = Fr_FetchTextProc;
    messageProcInfoPtr->envelopeProc = Fr_EnvelopeProc;
    messageProcInfoPtr->msgDeleteProc = Fr_MsgDeleteProc;
    messageProcInfoPtr->makeChildrenProc = Fr_MakeChildrenProc;
    messageProcInfoPtr->fetchBodyProc = Fr_FetchBodyProc;
    messageProcInfoPtr->bodyDeleteProc = Fr_BodyDeleteProc;
    messageProcInfoPtr->getInternalDateProc = Fr_GetInternalDateProc;
}


/*
 *----------------------------------------------------------------------
 *
 * RatFrMessageCreate --
 *
 *      Creates a free message entity
 *
 * Results:
 *	The name of the new message entity.
 *
 * Side effects:
 *	None.
 *
 *
 *----------------------------------------------------------------------
 */

char*
RatFrMessageCreate(Tcl_Interp *interp, char *data, int length,
		   MessageInfo **msgPtrPtr)
{
    FrMessageInfo *frMsgPtr=(FrMessageInfo*)ckalloc(sizeof(FrMessageInfo));
    MessageInfo *msgPtr=(MessageInfo*)ckalloc(sizeof(MessageInfo));
    char *msgData, *cPtr;
    int headerLength, j, fromLength;

    for (headerLength = 0; data[headerLength]; headerLength++) {
	if (data[headerLength] == '\n' && data[headerLength+1] == '\n') {
	    headerLength++;
	    break;
	}
	if (data[headerLength]=='\r' && data[headerLength+1]=='\n'
		&& data[headerLength+2]=='\r' && data[headerLength+3]=='\n') {
	    headerLength += 2;
	    break;
	}
    }

    msgData = (char*)ckalloc(length+1);
    memcpy(msgData, data, length);
    msgData[length] = '\0';

    msgPtr->folderInfoPtr = NULL;
    msgPtr->type = RAT_FREE_MESSAGE;
    msgPtr->bodyInfoPtr = NULL;
    msgPtr->msgNo = 0;
    msgPtr->fromMe = RAT_ISME_UNKOWN;
    msgPtr->toMe = RAT_ISME_UNKOWN;
    msgPtr->clientData = (ClientData)frMsgPtr;
    for (j=0; j<sizeof(msgPtr->info)/sizeof(*msgPtr->info); j++) {
	msgPtr->info[j] = NULL;
    }
    frMsgPtr->msgData = msgData;
    frMsgPtr->messagePtr = RatParseMsg(interp, (unsigned char*)msgData);
    frMsgPtr->bodyData = frMsgPtr->messagePtr->text.text.data +
			 frMsgPtr->messagePtr->text.offset;
    frMsgPtr->headers = (char*)ckalloc(headerLength+1);
    strlcpy(frMsgPtr->headers, data, headerLength+1);
    if (!strncmp("From ", data, 5) && (cPtr = strchr(data, '\n'))) {
	fromLength = cPtr-data;
	frMsgPtr->from = (char*)ckalloc(fromLength+1);
	strlcpy(frMsgPtr->from, frMsgPtr->headers, fromLength);
    } else {
	frMsgPtr->from = NULL;
    }

    if (msgPtrPtr) {
	*msgPtrPtr = msgPtr;
    }

    sprintf(msgPtr->name, "RatFrMsg%d", numFrMessages++);
    Tcl_CreateObjCommand(interp, msgPtr->name, RatMessageCmd,
	    (ClientData)msgPtr, NULL);
    return msgPtr->name;
}


/*
 *----------------------------------------------------------------------
 *
 * Fr_GetHeadersProc --
 *
 *      See ratFolder.h
 *
 *----------------------------------------------------------------------
 */

static char*
Fr_GetHeadersProc(Tcl_Interp *interp, MessageInfo *msgPtr)
{
    FrMessageInfo *frMsgPtr = (FrMessageInfo*)msgPtr->clientData;
    return frMsgPtr->headers;
}


/*
 *----------------------------------------------------------------------
 *
 * Fr_GetEnvelopeProc --
 *
 *      See ratFolder.h
 *
 *----------------------------------------------------------------------
 */

static char*
Fr_GetEnvelopeProc(Tcl_Interp *interp, MessageInfo *msgPtr)
{
    FrMessageInfo *frMsgPtr = (FrMessageInfo*)msgPtr->clientData;
    static char buf[1024];
    MESSAGECACHE elt;
    ADDRESS *adrPtr;
    time_t date;
    struct tm tm, *tmPtr;

    if (frMsgPtr->messagePtr->env->return_path) {
	adrPtr = frMsgPtr->messagePtr->env->sender;
    } else if (frMsgPtr->messagePtr->env->sender) {
	adrPtr = frMsgPtr->messagePtr->env->sender;
    } else {
	adrPtr = frMsgPtr->messagePtr->env->from;
    }
    if (!strcmp(Tcl_GetHostName(), adrPtr->host)) {
	snprintf(buf, sizeof(buf), "From %s", adrPtr->mailbox);
    } else {
	strlcpy(buf, "From ", sizeof(buf));
	if (RatAddressSize(adrPtr, 0) > sizeof(buf)-32) {
	    snprintf(buf+5, sizeof(buf)-5, "ridiculously@long.address");
	} else {
	    rfc822_write_address_full(buf+5, adrPtr, NULL);
	}
    }
    if (T == mail_parse_date(&elt, frMsgPtr->messagePtr->env->date)) {
	tm.tm_sec = elt.seconds;
	tm.tm_min = elt.minutes;
	tm.tm_hour = elt.hours;
	tm.tm_mday = elt.day;
	tm.tm_mon = elt.month - 1;
	tm.tm_year = elt.year+69;
	tm.tm_wday = 0;
	tm.tm_yday = 0;
	tm.tm_isdst = -1;
	date = (int)mktime(&tm);
    } else {
	date = 0;
    }
    tmPtr = gmtime(&date);
    snprintf(buf + strlen(buf), sizeof(buf)-strlen(buf),
	    " %s %s %2d %02d:%02d GMT %04d\n", dayName[tmPtr->tm_wday],
	    monthName[tmPtr->tm_mon], tmPtr->tm_mday, tmPtr->tm_hour,
	    tmPtr->tm_min, tmPtr->tm_year+1900);
    return buf;
}


/*
 *----------------------------------------------------------------------
 *
 * Fr_CreateBodyProc --
 *
 *      See ratFolder.h
 *
 *----------------------------------------------------------------------
 */

BodyInfo*
Fr_CreateBodyProc(Tcl_Interp *interp, MessageInfo *msgPtr)
{
    FrMessageInfo *frMsgPtr = (FrMessageInfo*)msgPtr->clientData;
    FrBodyInfo *frBodyInfoPtr = (FrBodyInfo*)ckalloc(sizeof(FrBodyInfo));
    msgPtr->bodyInfoPtr = CreateBodyInfo(msgPtr);

    msgPtr->bodyInfoPtr->bodyPtr = frMsgPtr->messagePtr->body;
    msgPtr->bodyInfoPtr->clientData = (ClientData)frBodyInfoPtr;
    frBodyInfoPtr->text = frMsgPtr->bodyData;
    return msgPtr->bodyInfoPtr;
}


/*
 *----------------------------------------------------------------------
 *
 * Fr_FetchTextProc --
 *
 *      See ratFolder.h
 *
 *----------------------------------------------------------------------
 */

static char*
Fr_FetchTextProc(Tcl_Interp *interp, MessageInfo *msgPtr)
{
    FrMessageInfo *frMsgPtr = (FrMessageInfo*)msgPtr->clientData;
    return (char*)frMsgPtr->bodyData;
}


/*
 *----------------------------------------------------------------------
 *
 * Fr_EnvelopeProc --
 *
 *      See ratFolder.h
 *
 *----------------------------------------------------------------------
 */

static ENVELOPE*
Fr_EnvelopeProc(MessageInfo *msgPtr)
{
    return ((FrMessageInfo*)msgPtr->clientData)->messagePtr->env;
}

/*
 *----------------------------------------------------------------------
 *
 * Fr_MsgDeleteProc --
 *
 *      See ratFolder.h
 *
 *----------------------------------------------------------------------
 */

static void
Fr_MsgDeleteProc(MessageInfo *msgPtr)
{
    FrMessageInfo *frMsgPtr = (FrMessageInfo*)msgPtr->clientData;
    mail_free_envelope(&frMsgPtr->messagePtr->env);
    mail_free_body(&frMsgPtr->messagePtr->body);
    ckfree(frMsgPtr->messagePtr);
    ckfree(frMsgPtr->headers);
    ckfree(frMsgPtr->msgData);
    ckfree(frMsgPtr);
}


/*
 *----------------------------------------------------------------------
 *
 * Fr_MakeChildrenProc --
 *
 *      See ratFolder.h
 *
 *----------------------------------------------------------------------
 */

static void
Fr_MakeChildrenProc(Tcl_Interp *interp, BodyInfo *bodyInfoPtr)
{
    FrMessageInfo *frMsgPtr = (FrMessageInfo*)bodyInfoPtr->msgPtr->clientData;
    BODY *bodyPtr = bodyInfoPtr->bodyPtr;
    BodyInfo *partInfoPtr, **partInfoPtrPtr;
    FrBodyInfo *frPartInfoPtr;
    PART *partPtr;

    if (!bodyInfoPtr->firstbornPtr) {
	partInfoPtrPtr = &bodyInfoPtr->firstbornPtr;
	for (partPtr = bodyPtr->nested.part; partPtr;
		partPtr = partPtr->next) {
	    frPartInfoPtr = (FrBodyInfo*)ckalloc(sizeof(FrBodyInfo));
	    partInfoPtr = CreateBodyInfo(bodyInfoPtr->msgPtr);
	    *partInfoPtrPtr = partInfoPtr;
	    partInfoPtr->bodyPtr = &partPtr->body;
	    partInfoPtrPtr = &partInfoPtr->nextPtr;
	    partInfoPtr->clientData = (ClientData)frPartInfoPtr;
	    frPartInfoPtr->text = frMsgPtr->bodyData +
				  partPtr->body.contents.offset;
	}
    }
}


/*
 *----------------------------------------------------------------------
 *
 * Fr_FetchBodyProc --
 *
 *      See ratFolder.h
 *
 *----------------------------------------------------------------------
 */

static char*
Fr_FetchBodyProc(BodyInfo *bodyInfoPtr, unsigned long *lengthPtr)
{
    FrBodyInfo *frBodyInfoPtr = (FrBodyInfo*)bodyInfoPtr->clientData;

    if (bodyInfoPtr->decodedTextPtr) {
	*lengthPtr = Tcl_DStringLength(bodyInfoPtr->decodedTextPtr);
	return Tcl_DStringValue(bodyInfoPtr->decodedTextPtr);
    }
    *lengthPtr = bodyInfoPtr->bodyPtr->contents.text.size;
    return (char*)frBodyInfoPtr->text;
}


/*
 *----------------------------------------------------------------------
 *
 * Fr_BodyDeleteProc --
 *
 *      See ratFolder.h
 *
 *----------------------------------------------------------------------
 */

static void
Fr_BodyDeleteProc(BodyInfo *bodyInfoPtr)
{
    FrBodyInfo *frBodyInfoPtr = (FrBodyInfo*)bodyInfoPtr->clientData;
    ckfree(frBodyInfoPtr);
}



/*
 *----------------------------------------------------------------------
 *
 * Fr_GetInternalDateProc --
 *
 *      See ratFolder.h
 *
 *----------------------------------------------------------------------
 */

static MESSAGECACHE*
Fr_GetInternalDateProc(Tcl_Interp *interp, MessageInfo *msgPtr)
{
    FrMessageInfo *frMsgPtr = (FrMessageInfo*)msgPtr->clientData;
    static MESSAGECACHE elt;

    if (frMsgPtr->from) {
	return RatParseFrom(frMsgPtr->from);
    } else {
	mail_parse_date(&elt, frMsgPtr->messagePtr->env->date);
	return &elt;
    }
}


/*
 *----------------------------------------------------------------------
 *
 * Fr_GetInfoProc --
 *
 *      See ratFolder.h
 *
 *----------------------------------------------------------------------
 */

static Tcl_Obj*
Fr_GetInfoProc(Tcl_Interp *interp, ClientData clientData,
	RatFolderInfoType type, int index)
{
    static char buf[128];
    MessageInfo *msgPtr = (MessageInfo*)clientData;
    FrMessageInfo *frMsgPtr = (FrMessageInfo*)msgPtr->clientData;
    MESSAGECACHE elt;
    Tcl_Obj *oPtr = NULL;
    char *cPtr;
    int i, f;

    if (msgPtr->info[type]) {
	return msgPtr->info[type];
    }

    switch (type) {
	case RAT_FOLDER_SUBJECT:	/* fallthrough */
	case RAT_FOLDER_CANONSUBJECT:	/* fallthrough */
	case RAT_FOLDER_NAME:		/* fallthrough */
	case RAT_FOLDER_MAIL_REAL:	/* fallthrough */
	case RAT_FOLDER_MAIL:		/* fallthrough */
	case RAT_FOLDER_NAME_RECIPIENT:	/* fallthrough */
	case RAT_FOLDER_MAIL_RECIPIENT:	/* fallthrough */
	case RAT_FOLDER_TYPE:		/* fallthrough */
	case RAT_FOLDER_TO:		/* fallthrough */
	case RAT_FOLDER_FROM:		/* fallthrough */
	case RAT_FOLDER_SENDER:		/* fallthrough */
	case RAT_FOLDER_CC:		/* fallthrough */
	case RAT_FOLDER_REPLY_TO:	/* fallthrough */
	case RAT_FOLDER_MSGID:		/* fallthrough */
	case RAT_FOLDER_REF:		/* fallthrough */
	case RAT_FOLDER_PARAMETERS:
	    return RatGetMsgInfo(interp, type,msgPtr,frMsgPtr->messagePtr->env,
		    frMsgPtr->messagePtr->body, NULL, 0);
	case RAT_FOLDER_SIZE:		/* fallthrough */
	case RAT_FOLDER_SIZE_F:
	    return RatGetMsgInfo(interp, type, msgPtr, NULL, NULL, NULL,
		    frMsgPtr->messagePtr->header.text.size +
		    frMsgPtr->messagePtr->text.text.size);
	case RAT_FOLDER_DATE_F:	/* fallthrough */
	case RAT_FOLDER_DATE_N:	/* fallthrough */
	case RAT_FOLDER_DATE_IMAP4:
	    if (T != mail_parse_date(&elt, frMsgPtr->messagePtr->env->date)) {
		rfc822_date(buf);
		mail_parse_date(&elt, buf);
	    }
	    return RatGetMsgInfo(interp, type, msgPtr,
		    frMsgPtr->messagePtr->env, NULL, &elt, 0);
	case RAT_FOLDER_STATUS:
	    cPtr = frMsgPtr->headers;
	    do {
		if (!strncasecmp(cPtr, "status:", 7)) {
		    int seen, deleted, marked, answered;
		    ADDRESS *addressPtr;

		    seen = deleted = marked = answered = 0;
		    for (i=7; cPtr[i]; i++) {
			switch (cPtr[i]) {
			case 'R': seen = 1;	break;
			case 'D': deleted = 1;	break;
			case 'F': marked = 1;	break;
			case 'A': answered = 1;	break;
			}
		    }
		    if (RAT_ISME_UNKOWN == msgPtr->toMe) {
			msgPtr->toMe = RAT_ISME_NO;
			for (addressPtr = frMsgPtr->messagePtr->env->to;
				addressPtr;
				addressPtr = addressPtr->next) {
			    if (RatAddressIsMe(interp, addressPtr, 1)) {
				msgPtr->toMe = RAT_ISME_YES;
				break;
			    }
			}
		    }
		    i = 0;
		    if (!seen) {
			buf[i++] = 'N';
		    }
		    if (deleted) {
			buf[i++] = 'D';
		    }
		    if (marked) {
			buf[i++] = 'F';
		    }
		    if (answered) {
			buf[i++] = 'A';
		    }
		    if (RAT_ISME_YES == msgPtr->toMe) {
			buf[i++] = '+';
		    } else {
			buf[i++] = ' ';
		    }
		    buf[i] = '\0';
		    oPtr = Tcl_NewStringObj(buf, -1);
		    break;
		}
	    } while ((cPtr = strchr(cPtr, '\n')) && cPtr++ && *cPtr);
	    break;
	case RAT_FOLDER_FLAGS:
	    cPtr = frMsgPtr->headers;
	    buf[0] = '\0';
	    do {
		if (!strncasecmp(cPtr, "status:", 7)) {
		    for (i=7; cPtr[i] != '\n' && cPtr[i]; i++) {
			for (f=0; flag_name[f].imap_name; f++) {
			    if (flag_name[f].unix_char == cPtr[i]) {
				strlcat(buf, " ", sizeof(buf));
				strlcat(buf, flag_name[f].imap_name,
					sizeof(buf));
				break;
			    }
			}
		    }
		    if (*buf) {
			oPtr = Tcl_NewStringObj(buf+1, -1);
		    } else {
			oPtr = Tcl_NewObj();
		    }
		    break;
		}
	    } while ((cPtr = strchr(cPtr, '\n')) && cPtr++ && *cPtr);
	    break;
	case RAT_FOLDER_UNIXFLAGS:
	    cPtr = frMsgPtr->headers;
	    buf[0] = '\0';
	    do {
		if (!strncasecmp(cPtr, "status:", 7)) {
		    for (cPtr += 7; isspace(*cPtr); cPtr++);
		    oPtr = Tcl_NewStringObj(cPtr, -1);
		    break;
		}
	    } while ((cPtr = strchr(cPtr, '\n')) && cPtr++ && *cPtr);
	    break;
	case RAT_FOLDER_INDEX:
	    oPtr = Tcl_NewIntObj(1);
	    break;
	case RAT_FOLDER_THREADING:
	    oPtr = Tcl_NewObj();
	    break;
	case RAT_FOLDER_END:
	    break;
    }
    if (!oPtr) {
	oPtr = Tcl_NewObj();
    }
    msgPtr->info[type] = oPtr;
    Tcl_IncrRefCount(oPtr);
    return oPtr;
}


syntax highlighted by Code2HTML, v. 0.9.1