/*
 *	Copyright (C) 1988, 1989, 1992  TGV, Incorporated
 *	Deliver mail into PMDF
 *
 */
#include "address.h"
#include <stdio.h>
#include <signal.h>
#include <ctype.h>
#include <errno.h>
#include <ssdef.h>
#include <prvdef.h>
#include "vax-mm.h"
#ifdef	PMDF

/*
 * PMDF definitions and structures needed here.
 */

/* #define PMDF_DEBUG 1 */

#define rp_isgood(val)  (((val) & 255) <= 127)
#define rp_isbad(val)   (((val) & 255) > 127)

#define ALFA_SIZE 252

typedef struct  {
    int length;
    char body[ALFA_SIZE];
} vstring;

#define BIGALFA_SIZE 1020

typedef struct  {
    int length;
    char body[BIGALFA_SIZE];
} bigvstring;

typedef struct {
    int rp_val;
    vstring rp_line;
    int os_val1, os_val2;
} rp_bufstruct;

static int Find_Image_Symbol();
static int SIG_TO_RET();
extern Write_Draft();

/*
 * Interface stubs to connect to PMDF. LIB$FIND_IMAGE_SYMBOL is used to
 * advantage to avoid code duplication and unnecessary code loading. See
 * PMDF_ROOT:[SRC]MM.PAS for explanations of these routines.
 */

int pmdf_set_mm_delivery(int value)
{
    static int (*os_set_mm_delivery)() = NULL;
    int Status;
    static unsigned int sysprv[2] = {PRV$M_SYSPRV,0};
    unsigned int old_privs[2];

#ifdef PMDF_DEBUG
    printf("pmdf_set_mm_delivery call.\n");
#endif PMDF_DEBUG

    if (os_set_mm_delivery == NULL) {
	Status = Find_Image_Symbol("PMDF_SHARE_LIBRARY", "OS_SET_MM_DELIVERY", &os_set_mm_delivery);
	if (!(Status&1)) return 0;
    }
    Status = sys$setprv(1,sysprv,0,old_privs);
    if (!(Status&1)) return 0;
    Status = (*os_set_mm_delivery)(&value);
    if (!(old_privs[0] & PRV$M_SYSPRV)) sys$setprv(0,sysprv,0,0);
    return Status;
}

static int MMinit()
{
    static int (*mm_init)() = NULL;
    int Status;

#ifdef PMDF_DEBUG
    printf("mm_init call.\n");
#endif PMDF_DEBUG

    if (mm_init == NULL) {
	Status = Find_Image_Symbol("PMDF_SHARE_LIBRARY", "MM_INIT", &mm_init);
	if (!(Status&1)) return (FALSE);
    }	        
    return rp_isgood((*mm_init)());
}

static int MMsbinit()
{
    static int (*mm_sbinit)() = NULL;
    int Status;

#ifdef PMDF_DEBUG
    printf("mm_sbinit call.\n");
#endif PMDF_DEBUG

    if (mm_sbinit == NULL) {
	Status = Find_Image_Symbol("PMDF_SHARE_LIBRARY",
                                   "MM_SBINIT", &mm_sbinit);
	if (!(Status&1)) return (FALSE);
    }	        
    return rp_isgood((*mm_sbinit)());
}

static int MMwinit(retadr)
char *retadr;
{
    vstring vretadr, dummy;
    static int (*mm_winit)() = NULL;
    int Status;

#ifdef PMDF_DEBUG
    printf("mm_winit('l                               ', '%s') call.\n", retadr);
#endif PMDF_DEBUG

    if (mm_winit == NULL) {
	Status = Find_Image_Symbol("PMDF_SHARE_LIBRARY", "MM_WINIT", &mm_winit);
	if (!(Status&1)) return (FALSE);
    }	        

    strcpy(vretadr.body, retadr);
    vretadr.length = strlen(retadr);
    dummy.length = 0;
    return rp_isgood((*mm_winit)("l                               ", &vretadr, &dummy));
}

static int MMwadr(host, adr)
char *host, *adr;
{
    vstring vhost, vadr;
    static int (*mm_wadr)() = NULL;
    int Status;

#ifdef PMDF_DEBUG
    printf("mm_wadr('%s', '%s') call.\n", host, adr);
#endif PMDF_DEBUG

    if (mm_wadr == NULL) {
	Status = Find_Image_Symbol("PMDF_SHARE_LIBRARY", "MM_WADR", &mm_wadr);
	if (!(Status&1)) return (FALSE);
    }	        

    strcpy(vhost.body, host);
    vhost.length = strlen(host);
    strcpy(vadr.body, adr);
    vadr.length = strlen(adr);
    return rp_isgood((*mm_wadr)(&vhost, &vadr, &vadr));
}

static int MMsetadd(to_list, cc_list, bcc_list)
int to_list, cc_list, bcc_list;
{
    static int (*mm_set_add)() = NULL;
    int Status;
    static int ptrue = -1;
    static int pfalse = 0;
    char *cp;
    int version;

    if (mm_set_add == NULL) {
	Status = Find_Image_Symbol("PMDF_SHARE_LIBRARY", "MM_SET_ADD",
                                   &mm_set_add);
	if (!(Status&1)) mm_set_add = (int (*)()) -1;
    }	        
    if ((int *)mm_set_add == (int *) -1) return (FALSE);
    cp = (char *)PMDF_Version();
    version = ((cp[0] - '0') * 10) +  (cp[2] - '0');

    (*mm_set_add)(to_list ? &ptrue : &pfalse,
                  cc_list ? &ptrue : &pfalse,
                  (bcc_list && (version > 40)) ? &ptrue : &pfalse,
                  &pfalse, &ptrue);
}

static int MMrrply(val, line)
int *val;
char *line;
{
    rp_bufstruct valstr;
    static int (*mm_rrply)() = NULL;
    int Status, retval;

#ifdef PMDF_DEBUG
    printf("mm_rrply() call.\n");
#endif PMDF_DEBUG

    if (mm_rrply == NULL) {
	Status = Find_Image_Symbol("PMDF_SHARE_LIBRARY", "MM_RRPLY", &mm_rrply);
	if (!(Status&1)) return (FALSE);
    }	        

    if (retval = rp_isgood((*mm_rrply)(&valstr))) {
        strncpy(line, valstr.rp_line.body, valstr.rp_line.length);
        line[valstr.rp_line.length] = '\0';
        *val = rp_isgood(valstr.rp_val);
    }
    return (retval);
}

static int MMwhy()
{
    char line[5122];
    int val, retval;
    if (retval = MMrrply(&val, line)) printf ("PMDF error: %s\n", line);
    return (retval);
}

static int MMwaend()
{
    static int (*mm_waend)() = NULL;
    int Status;

#ifdef PMDF_DEBUG
    printf("mm_waend call.\n");
#endif PMDF_DEBUG

    if (mm_waend == NULL) {
	Status = Find_Image_Symbol("PMDF_SHARE_LIBRARY", "MM_WAEND", &mm_waend);
	if (!(Status&1)) return (FALSE);
    }	        
    return rp_isgood((*mm_waend)());
}

static int MMbigwtxt(line, llength)
char *line;
int llength;
{
    bigvstring vline;
    static int (*mm_bigwtxt)() = NULL;
    int Status;

#ifdef PMDF_DEBUG
    printf("mm_bigwtxt() call.\n");
#endif PMDF_DEBUG

    if (mm_bigwtxt == NULL) {
	Status = Find_Image_Symbol("PMDF_SHARE_LIBRARY",
                                   "MM_BIGWTXT", &mm_bigwtxt);
	if (!(Status&1)) return (FALSE);
    }	        

    while(llength > 0) {
	vline.length = (llength > BIGALFA_SIZE) ? BIGALFA_SIZE : llength;
	memcpy(vline.body, line, vline.length);
        if rp_isbad((*mm_bigwtxt)(&vline)) return (FALSE);
	line += vline.length;
	llength -= vline.length;
    }
    return (TRUE);
}

static int MMwtend()
{
    static int (*mm_wtend)() = NULL;
    int Status;

#ifdef PMDF_DEBUG
    printf("mm_wtend call.\n");
#endif PMDF_DEBUG

    if (mm_wtend == NULL) {
	Status = Find_Image_Symbol("PMDF_SHARE_LIBRARY", "MM_WTEND", &mm_wtend);
	if (!(Status&1)) return (FALSE);
    }	        
    return rp_isgood((*mm_wtend)());
}

static int MMwkill()
{
    static int (*mm_wkill)() = NULL;
    int Status;

#ifdef PMDF_DEBUG
    printf("mm_wkill call.\n");
#endif PMDF_DEBUG

    if (mm_wkill == NULL) {
	Status = Find_Image_Symbol("PMDF_SHARE_LIBRARY", "MM_WKILL", &mm_wkill);
	if (!(Status&1)) return (FALSE);
    }	        
    return rp_isgood((*mm_wkill)());
}


/*
 *	Deliver Net mail
 */
int PMDF_Deliver_Net_Mail(Address, Header, Header_Length, Text, Text_Length)
register struct Address *Address;
char *Header;
int   Header_Length;
char *Text;
int   Text_Length;
{
    static int first = TRUE;
    char from[5122], temp[5122], error[5122];
    char *j, *cp;
    int i, valid_count, invalid_encountered;
    FILE *f;

    /*
     *  Initialize PMDF if need be.
     */
    if (first) {
        if (!MMinit()) {
            printf("Unable to initialize PMDF. Message can't be submitted.\n");
            (void) MMwhy();
            return (FALSE);
        }
        first = FALSE;
    }

    if (!MMsbinit()) {
        printf("Can't initialize PMDF submission. Message can't be submitted.\n");
        (void) MMwhy();
        return (FALSE);
    }

    /*
     *  Find the from address; initialize message submission.
     */
    for (i = Header_Length, cp=Header, j=temp; i > 0; ) {
        if (imatch(cp,0,"Sender:")) {
            for (cp += skipbl(cp,8) ; *cp != '\n' ; *j++ = *cp++);
            *j = '\0';
            break;
        }
        while(--i > 0) if (*cp++ == '\n') break;
    }
    if (j == temp) {
    	for (i = Header_Length, cp=Header, j=temp; i > 0; ) {
            if (imatch(cp,0,"From:")) {
            	for (cp += skipbl(cp,6) ; *cp != '\n' ; *j++ = *cp++);
            	*j = '\0';
            	break;
            }
            while(--i > 0) if (*cp++ == '\n') break;
    	}
    }
    just_address(from, temp);
    if (!MMwinit(from)) {
        printf("Unable to write to PMDF. Message can't be submitted.\n");
        (void) MMwhy();
        return (FALSE);
    }

    /*
     *  Submit each destination address.
     */
    valid_count = 0; invalid_encountered = 0;
    for( ; Address ; Address = Address->Next_Address) {
    	if (Address->RFC822_Address) {
    	    strcpy(temp, Address->RFC822_Address);
    	} else if(Address->Type == ADDRESS_NETWORK) {
            if(Address->Host) {
                strcpy(temp, Address->User);
                strcat(temp, "@");
                strcat(temp, Address->Host);
            } else
                strcpy(temp, Address->User);
        } else
            strcpy(temp, Address->User);
        MMsetadd(Address->Flags & ADDRESS_ON_TO_LINE,
                 Address->Flags & ADDRESS_ON_CC_LINE,
                 Address->Flags & ADDRESS_ON_BCC_LINE);
        if(!MMwadr((char *)Get_Hostname(), temp)) {
            printf("?Cannot submit: %s\n", temp);
	    invalid_encountered++;
	}
        else if(!MMrrply(&i, error)) {
            printf("?Cannot obtain status for: %s\n", temp);
	    invalid_encountered++;
	}
        else if(!i) {
            printf("?Illegal address: %s - %s\n", temp, error);
	    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");
        (void) MMwkill();
        return (FALSE);
    }

    /*
     * Terminate address list.
     */
    if (!MMwaend()) {
        printf("Can't end PMDF address list. Message can't be submitted.\n");
        (void) MMwhy();
        (void) MMwkill();
        return (FALSE);
    }

    /*
     *  Process message body.
     */
    if (!(MMbigwtxt(Header, Header_Length)) ||
        !(MMbigwtxt(Text, Text_Length))) {
        printf("Cannot write message body. Message can't be submitted.\n");
        (void) MMwhy();
        (void) MMwkill();
        return (FALSE);
    }

    /*
     * Finalize submission.
     */
    enable_image_privilege();
    i = MMwtend();
    disable_image_privilege();
    if(!i) {
        printf("Can't end PMDF message text. Message can't be submitted.\n");
        (void) MMwhy();
        (void) MMwkill();
        return (FALSE);
    }

    if(!MMrrply(&i, error)) {
        printf("?Cannot obtain send status from PMDF.\n");
        return (FALSE);
    } else if(!i) {
        printf("?Error during message send: %s\n", error);
        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	PMDF
