/*
 *	Copyright (C) 1993  TGV, Incorporated
 *	Deliver mail into MX
 *
 */
#include "mx_defs.h"
#include "address.h"
#include <stdio.h>
#include <signal.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <ssdef.h>
#include <stsdef.h>
#include <descrip.h>
#include "vax-mm.h"
#ifdef MX

#define INIT_SDESC(dsc, len, ptr) {dsc.dsc$a_pointer=(ptr); dsc.dsc$w_length=(len);\
		    dsc.dsc$b_dtype=DSC$K_DTYPE_T; dsc.dsc$b_class=DSC$K_CLASS_S;}

    static char *HeaderTable[] = {
	"From:","Sender:","To:","Resent-To:","CC:","Resent-CC:","BCC:","Resent-BCC:",
	"Message-ID:","Resent-Message-ID:","In-Reply-To:","References:","Keywords:",
	"Subject:","Encrypted:","Date:","Reply-To:","Received:","Resent-Reply-To:","Resent-From:",
	"Resent-Sender:","Resent-Date:","Return-Path:", "Placeholder for MX_HDR_K_OTHER",
	"X-Warning:","X-To:","X-Resent-To:","X-CC:","X-Resent-CC:","X-BCC:","X-Resent-BCC:"};
    static int HeaderCode[] = {MX_K_HDR_FROM,MX_K_HDR_SENDER,MX_K_HDR_TO,MX_K_HDR_R_TO,
	MX_K_HDR_CC,MX_K_HDR_R_CC,MX_K_HDR_BCC,MX_K_HDR_R_BCC,MX_K_HDR_MESSAGE_ID,
	MX_K_HDR_R_MESSAGE_ID,MX_K_HDR_IN_REPLY_TO,MX_K_HDR_REFERENCES,MX_K_HDR_KEYWORDS,
	MX_K_HDR_SUBJECT,MX_K_HDR_ENCRYPTED,MX_K_HDR_DATE,MX_K_HDR_REPLY_TO,MX_K_HDR_RECEIVED,
	MX_K_HDR_R_REPLY_TO,MX_K_HDR_R_FROM,MX_K_HDR_R_SENDER,MX_K_HDR_R_DATE,MX_K_HDR_RETURN_PATH,
	MX_K_HDR_OTHER,MX_K_HDR_X_WARNING,MX_K_HDR_X_TO,MX_K_HDR_X_R_TO,MX_K_HDR_X_CC,MX_K_HDR_X_R_CC,
	MX_K_HDR_X_BCC,MX_K_HDR_X_R_BCC};

static int Find_Image_Symbol();
static int SIG_TO_RET();

/*
 * Interface stubs to connect to MX. LIB$FIND_IMAGE_SYMBOL is used to
 * advantage to avoid code duplication and unnecessary code loading.
 */

static unsigned int msg_init(unsigned int *ctx, unsigned int *entnum)
{
    static int (*rtn)() = NULL;
    int Status;

#ifdef MX_DEBUG
    printf("init call.\n");
#endif MX_DEBUG

    if (rtn == NULL) {
	Status = Find_Image_Symbol("MX_MAILSHR", "MX_MSG_INIT", &rtn);
	if (!$VMS_STATUS_SUCCESS(Status)) Status = Find_Image_Symbol("MX_MAILSHRP", "MX_MSG_INIT", &rtn);
	if (!$VMS_STATUS_SUCCESS(Status)) return FALSE;
    }
    return (*rtn)(ctx, entnum);

}
static unsigned int set_from(unsigned int *ctx, struct dsc$descriptor *dsc)
{
    static int (*rtn)() = NULL;
    int Status;

#ifdef MX_DEBUG
    printf("set_from call.\n");
#endif MX_DEBUG

    if (rtn == NULL) {
	Status = Find_Image_Symbol("MX_MAILSHR", "MX_MSG_SET_FROM", &rtn);
	if (!$VMS_STATUS_SUCCESS(Status)) Status = Find_Image_Symbol("MX_MAILSHRP", "MX_MSG_SET_FROM", &rtn);
	if (!$VMS_STATUS_SUCCESS(Status)) return FALSE;
    }
    return (*rtn)(ctx, dsc);

}

static unsigned int msg_cancel(unsigned int *ctx)
{
    static int (*rtn)() = NULL;
    int Status;

#ifdef MX_DEBUG
    printf("cancel call.\n");
#endif MX_DEBUG

    if (rtn == NULL) {
	Status = Find_Image_Symbol("MX_MAILSHR", "MX_MSG_CANCEL", &rtn);
	if (!$VMS_STATUS_SUCCESS(Status)) Status = Find_Image_Symbol("MX_MAILSHRP", "MX_MSG_CANCEL", &rtn);
	if (!$VMS_STATUS_SUCCESS(Status)) return FALSE;
    }
    return (*rtn)(ctx);

}

static unsigned int add_address(unsigned int *ctx, struct dsc$descriptor *dsc)
{
    static int (*rtn)() = NULL;
    int Status;

#ifdef MX_DEBUG
    printf("add_address call.\n");
#endif MX_DEBUG

    if (rtn == NULL) {
	Status = Find_Image_Symbol("MX_MAILSHR", "MX_MSG_ADD_ADDRESS", &rtn);
	if (!$VMS_STATUS_SUCCESS(Status)) Status = Find_Image_Symbol("MX_MAILSHRP", "MX_MSG_ADD_ADDRESS", &rtn);
	if (!$VMS_STATUS_SUCCESS(Status)) return FALSE;
    }
    return (*rtn)(ctx, dsc);

}

static unsigned int add_header(unsigned int *ctx, int *code, struct dsc$descriptor *dsc)
{
    static int (*rtn)() = NULL;
    int Status;

#ifdef MX_DEBUG
    printf("add_header call.\n");
#endif MX_DEBUG

    if (rtn == NULL) {
	Status = Find_Image_Symbol("MX_MAILSHR", "MX_MSG_ADD_HEADER", &rtn);
	if (!$VMS_STATUS_SUCCESS(Status)) Status = Find_Image_Symbol("MX_MAILSHRP", "MX_MSG_ADD_HEADER", &rtn);
	if (!$VMS_STATUS_SUCCESS(Status)) return FALSE;
    }
    return (*rtn)(ctx, code, dsc);

}

static unsigned int add_text(unsigned int *ctx, struct dsc$descriptor *dsc)
{
    static int (*rtn)() = NULL;
    int Status;

#ifdef MX_DEBUG
    printf("add_text call.\n");
#endif MX_DEBUG

    if (rtn == NULL) {
	Status = Find_Image_Symbol("MX_MAILSHR", "MX_MSG_ADD_TEXT", &rtn);
	if (!$VMS_STATUS_SUCCESS(Status)) Status = Find_Image_Symbol("MX_MAILSHRP", "MX_MSG_ADD_TEXT", &rtn);
	if (!$VMS_STATUS_SUCCESS(Status)) return FALSE;
    }
    return (*rtn)(ctx, dsc);

}

static unsigned int msg_send(unsigned int *ctx)
{
    static int (*rtn)() = NULL;
    int Status;

#ifdef MX_DEBUG
    printf("send call.\n");
#endif MX_DEBUG

    if (rtn == NULL) {
	Status = Find_Image_Symbol("MX_MAILSHR", "MX_MSG_SEND", &rtn);
	if (!$VMS_STATUS_SUCCESS(Status)) Status = Find_Image_Symbol("MX_MAILSHRP", "MX_MSG_SEND", &rtn);
	if (!$VMS_STATUS_SUCCESS(Status)) return FALSE;
    }
    return (*rtn)(ctx);

}

char *MX_Version()
{
    static struct dsc$descriptor *(*rtn)() = NULL;
    static char MX_Version[33];
    int Status;

    if (rtn == NULL) {
	struct dsc$descriptor *mxv;

	MX_Version[0] = '\0';
	Status = Find_Image_Symbol("MX_SHR", "MX_VERSION", &rtn);
	if ($VMS_STATUS_SUCCESS(Status)) {
	    mxv = (*rtn)();
	    if (mxv) {
		int len = (mxv->dsc$w_length > 35 ? 32 : mxv->dsc$w_length-3);
		memcpy(MX_Version, mxv->dsc$a_pointer+3, len);
		MX_Version[len] = '\0';
	    }
	}
	if (!MX_Version[0]) strcpy(MX_Version, "unk vers");
    }

    return MX_Version;

}


/*
 *	Deliver Net mail
 */
int MX_Deliver_Net_Mail(Address, Header, Header_Length, Text, Text_Length)
register struct Address *Address;
char *Header;
int   Header_Length;
char *Text;
int   Text_Length;
{
    char from[5122], temp[5122], error[5122];
    char *cp, *cp1, *cp2, *text;
    int i, k, len, code, valid_count, invalid_encountered;
    unsigned int status, ctx, entnum;
    struct dsc$descriptor dsc;

    status = msg_init(&ctx, &entnum);
    if (!$VMS_STATUS_SUCCESS(status)) {
	printf("Error status 0x%x from MX_MSG_INIT.  Message cannot be submitted.\n", status);
	return FALSE;
    }

    /*
     *	Find the from address; initialize message submission.
     */
    for (i = Header_Length, cp=Header, cp1=temp; i > 1; ) {
	if (imatch(cp,0,"Sender:")) {
	    for (cp += skipbl(cp,8) ; *cp != '\n' ; *cp1++ = *cp++);
	    *cp1 = '\0';
	    break;
	}
	while(--i > 0) if (*cp++ == '\n') break;
    }
    if (cp1 == temp) {
    	for (i = Header_Length, cp=Header, cp1=temp; i > 1; ) {
	    if (imatch(cp,0,"From:")) {
	    	for (cp += skipbl(cp,6) ; *cp != '\n' ; *cp1++ = *cp++);
	    	*cp1 = '\0';
	    	break;
	    }
	    while(--i > 0) if (*cp++ == '\n') break;
    	}
    }
    just_address(from, temp);
    INIT_SDESC(dsc, strlen(from), from);
    enable_image_privilege();
    status = set_from(&ctx, &dsc);
    disable_image_privilege();
    if (!$VMS_STATUS_SUCCESS(status) && status != SS$_NOPRIV) {
	printf("Error status 0x%x from MX_MSG_SET_FROM.  Message cannot be submitted.\n", status);
	msg_cancel(&ctx);
	return FALSE;
    }

    /*
     *	Submit each destination address.
     */
    valid_count = 0; invalid_encountered = 0;
    for( ; Address ; Address = Address->Next_Address) {
	strcpy(temp, "<");
	strcat(temp, Address->User);
	strcat(temp, "@");
	if(Address->Type == ADDRESS_NETWORK) {
	    if(Address->Host) {
		strcat(temp, Address->Host);
	    } else {
		strcat(temp, Get_Hostname());
	    }
	} else {
	    strcat(temp, Get_Hostname());
	}
	strcat(temp, ">");
	INIT_SDESC(dsc, strlen(temp), temp);
	status = add_address(&ctx, &dsc);
	if (!$VMS_STATUS_SUCCESS(status)) {
	    printf("Error status 0x%x from MX_MSG_ADD_ADDRESS(%s).\n", status, temp);
	    invalid_encountered++;
	} else valid_count++;
    }

    /*
     * Check to see if at least some addresses were good...
     */
    if(valid_count == 0) {
	printf("All addresses bad. Message can't be submitted.\n");
	msg_cancel(&ctx);
	return (FALSE);
    }

    /*
     *	Process message headers.
     */
    for (i = Header_Length, cp=Header; i > 1; i -= (len+1), cp += (len+1)) {
	cp1 = (char *) strchr(cp, '\n');
	if (cp1) len = cp1-cp;
	else len = i;
	for (cp2 = cp; cp2 < cp+len; cp2++) if (isspace(*cp2)) break;
	strncpy(temp, cp, cp2-cp);
	temp[cp2-cp] = '\0';
	code = 0;
	for (k = 0; k < sizeof(HeaderTable)/sizeof(char *); k++) {
	    if (strcasecmp(temp, HeaderTable[k]) == 0) {
		code = HeaderCode[k];
		for (text = cp2; text < cp+len; text++) if (!isspace(*text)) break;
		break;
	    }
	}
	if (code == 0) {
	    code = MX_K_HDR_OTHER;
	    text = cp;
	}
	INIT_SDESC(dsc, len-(text-cp), text);
	status = add_header(&ctx, &code, &dsc);
	if (!$VMS_STATUS_SUCCESS(status)) {
	    printf("Error status 0x%x from MX_MSG_ADD_HEADER.\n", status);
	    msg_cancel(&ctx);
	    return FALSE;
	}
    }
    /*
     *	Process message text.
     */
    for (i = Text_Length, cp=Text; i > 0; i -= (len+1), cp += (len+1)) {
	cp1 = (char *) strchr(cp, '\n');
	if (cp1) len = cp1-cp;
	else len = i;
	INIT_SDESC(dsc, len, cp);
	status = add_text(&ctx, &dsc);
	if (!$VMS_STATUS_SUCCESS(status)) {
	    printf("Error status 0x%x from MX_MSG_ADD_TEXT.\n", status);
	    msg_cancel(&ctx);
	    return FALSE;
	}
    }

    status = msg_send(&ctx);
    if ($VMS_STATUS_SUCCESS(status)) {
	printf("Message (entry number %d) successfully delivered to MX\n", entnum);
    } else {
	printf("Error status 0x%x from MX_MSG_SEND.\n", status);
	return FALSE;
    }

    if (invalid_encountered)
	return (FALSE);
    else
	return (TRUE);
}

static int Find_Image_Symbol(Image, Symbol, Value)
char *Image, *Symbol;
int *Value;
{
	struct {int len; char *buf;} ImageDsc, SymbolDsc;
	int Status;

	/*
	 *	Return all errors as statuses, never allow
	 *	LIB$FIND_IMAGE_SYMBOL to signal.
	 */

	(void) LIB$ESTABLISH(SIG_TO_RET);

	ImageDsc.len = strlen(ImageDsc.buf=Image);
	SymbolDsc.len = strlen(SymbolDsc.buf=Symbol);
	Status = LIB$FIND_IMAGE_SYMBOL(&ImageDsc, &SymbolDsc, Value);
	return(Status);
}

/*
 *  This is a special version of LIB$SIG_TO_RET which is a little
 *  smarter about decoding the double-condition signals from
 *  LIB$FIND_IMAGE_SYMBOL
 *
 *  One of the signals signal array looks like:
 *
 *	00000006	# of args
 *	001512BA	LIB$_ACTIMAGE
 *	00000001	    1 arg
 *	????????	    Descriptor to image name
 *	********	Real reason
 *	????????	PC
 *	????????	PSL
 */

#define CHF$L_SIG_NAME 4
#define CHF$L_MCH_SAVR0 12

static int SIG_TO_RET(Sig_Args, Mch_Args)
int Sig_Args[];
int Mch_Args[];
{
	int Status;

	if (Sig_Args[CHF$L_SIG_NAME/4] == SS$_UNWIND) return(SS$_NORMAL);

	/*
	 * Copy condition value to saved image of R0
	 */

	Status = Sig_Args[CHF$L_SIG_NAME/4];
	if (((Status&0x1fff0000) == 0x150000) && Sig_Args[0] >= 6) {
	    Status = Sig_Args[Sig_Args[2]+3];
	}
	Mch_Args[CHF$L_MCH_SAVR0/4] = Status;

	/*
	 * Set to unwind stack using default depth and default new PC,
	 * namely return to caller of the procedure which established the handler
	 */

	return(SYS$UNWIND(0,0));
}

#endif	MX
