#include <descrip.h>
#include <ssdef.h>
#include <syidef.h>
#include <lnmdef.h>
#include <opcdef.h>
#include <rms.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "bboard.h"

/* Translate a logical name, putting it into an xmalloc()ed buffer. */

char * log_tran(char * log_name)
{
    static struct dsc$descriptor_s table_name =
	{ 16, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$SYSTEM_TABLE" };

    struct item_list {
	unsigned short buf_len; 	/* Length of return buffer */
	unsigned short item_code;	/* What do we want about this log.? */
	void * addr;			/* Where's the buffer */
	int * ret_len;			/* And how long was the info? */
    } log_list[2];			/* One and one NULL to terminate */

    char * logical;			/* Ptr to logical name buffer */

    struct dsc$descriptor name; 	/* Logical to translate */
    int log_len;			/* How long was the info */
    int attr;				/* How do we scan for it? */

    /* Do some setup */

    logical = xmalloc(256);		/* Build a buffer */
    str_desc(&name, log_name);		/* Descriptor for the name */
    attr = LNM$M_CASE_BLIND;		/* Ignore case for translating */

    log_list[0].buf_len = 255;		/* buffer is 255 (+1 for '\0') */
    log_list[0].item_code = LNM$_STRING;    /* Want the string itself */
    log_list[0].addr = logical; 	/* Where the translation goes */
    log_list[0].ret_len = &log_len;	/* How long is the translation? */

    log_list[1].buf_len = 0;		/* Nothing for the second */
    log_list[1].item_code = 0;

    /* Now actually do the translation */

    status = sys$trnlnm(&attr, &table_name, &name, 0, &log_list);

    /* Did it happen? */

    if ((status == SS$_NOLOGNAM) || !(status & 1)) {
	log_len = 0;
    }

    /* Terminate the string, please */

    logical[log_len & 0xff] = '\0';

    /* If it didn't work, release the memory used */

    if (logical[0] == 0) {
	xfree(logical);
	logical = NULL;
    }

    /* And where's the buffer? */

    return (logical);
}

/* Figure out what the current version is */

char * get_vms_ver(void)
{
    static char ver[9]; 		/* Version string */
    int len;				/* How long is it? */
    struct item {
	short buf_len;			/* How long is the buffer */
	short code;			/* What do we want */
	void * buffer;			/* Where is the buffer */
	int * ret_len;			/* How long is the info? */
    } vms_request[2];			/* One and one NULL to terminate */

    /* Do some setup stuff */

    vms_request[0].buf_len = 8; 	/* max 8 characters for version_id */
    vms_request[0].code = SYI$_VERSION; /* Want the version */
    vms_request[0].buffer = ver;	/* Where it goes */
    vms_request[0].ret_len = &len;	/* Where to stuff the length */

    vms_request[1].buf_len = 0; 	/* Nothing for the second */
    vms_request[1].code = 0;

    /* Get system information, synchronously */

    sys$getsyiw(NULL, NULL, NULL, vms_request, NULL, NULL, NULL);

    /* Terminate the string */
    ver[8] = '\0';
    for (len = 7; len--; len != 0) {
	if (ver[len] != ' ')
	    break;
	else
	    ver[len] = '\0';
    }

    return(ver);
}

/* Send a message out to a user through JNET */

void send_msg(char * n, char * u, char * s)
{
    static struct dsc$descriptor_s node;
    static struct dsc$descriptor_s user;
    static struct dsc$descriptor_s msg;
    char * t_s; 			/* Need to fondle the string */

    /* Redo the message with a '*' for *SERV machines, if we miss otherwise */

    t_s = xmalloc(strlen(s)+4);
    sprintf(t_s, "* %s", s);

    /* Setup for Jnet */

    str_desc(&node, n);
    str_desc(&user, u);
    str_desc(&msg, t_s);
    mode = 2;

    /* Send it to Jnet to do it's thing */

    jan_send_msg(&mode, &node, &user, &msg);

    /* Don't need that hanging around */

    xfree(t_s);
}

/* Write a message to the log file */

void send_log_file(char * s)
{
    static struct dsc$descriptor_s log_msg;

    if (log_available != TRUE)
	return;				/* Can't log until hook is set up */
    str_desc(&log_msg, s);		/* Build a descriptor, please */
    jan_jlog(&hook_name, &log_msg);	/* Call Jnet to do it to it */
}

/* Start up a new log file */

void init_log_file(void)
{
    /* Build the logfile comment and send it through Jnet */

    sprintf(temp, "I %s (%s) started.", hook_name.dsc$a_pointer, version);
    send_log_file(temp);

    /* Tell OPCOM that we've started */

    send_opcom(temp+2);
}

/* Send a message to OPCOM */

void send_opcom(char * s)
{
    struct {
	struct hdr {			/* Trust me, this is necessary */
	    unsigned char type; 	/*  Read up on SYS$SNDOPR and you'll */
	    unsigned short target_0_15; /*  see why. */
	    unsigned char target_16_23;
	    unsigned long rqst_id;
	} h;
	char msg[200];
    } opc_request;
    struct dsc$descriptor opc;

    opc_request.h.type = OPC$_RQ_RQST;	/* Send out the string */
    opc_request.h.target_0_15 = OPC$M_NM_CENTRL;    /* To main operator */
    opc_request.h.target_16_23 = 0;
    opc_request.h.rqst_id = 0L; 	/* Default it */

    strcpy(opc_request.msg, s); 	/* Copy the string */

    opc.dsc$a_pointer = &opc_request;	/* Build a descriptor for the block */
    opc.dsc$w_length = strlen(s) + sizeof(struct hdr);

    /* And do it */

    sys$sndopr(&opc, 0);
}

/* Do the right things with the From: text lines */

void from_text_fondle(char * s)
{
    int front;

    /* First, get rid of '"'s. They mess up Notes */

    remove_quotes(s);

    /* Strip leading blanks */

    front = strspn(s, " ");		/* Find length of leading blanks */
    if (front) {
	strcpy(s, s+front);
    }

    /* Now, let's see if there's any way to get a return address */

    if (!strpbrk(s, "<> ")) {

	/* Well, there might be something useful there. Let's check */

	if (strchr(s, '@')) {
	    strcat(s, ">");             /* Throw a <> around the whole thing */
	    strcpy(s+1, s);
	    s[0] = '<';
	}
    }

    /* Last, let's see if we can do something interesting with the name */

    if (s[0] && !isalpha(s[0]) && !isdigit(s[0])) {
	strcpy(s+1, s);			/* Make room, make room */
	s[0] = '_';                     /* Something to make Notes happy */
    }
}

/* Do the right things with the Subj: lines */

void subj_text_fondle(char * s)
{
    /* First, get rid of '"'s. They mess up Notes */

    remove_quotes(s);

    /* Check for initial '$'s. They mess up DCL. [Isn't this fun, kids?] */

    if (s[0] == '$') {
	strcpy(s+1, s); 		/* Extra character */
	s[0] = ' ';
    }
}

/* Error to the log file, please */

void report_error(char * s, int error)
{
    static char msg[257];		/* Place for the message */
    struct dsc$descriptor msgd; 	/* And a descriptor for it */
    int msg_len;			/* How long was the message */

    /* Some setup */

    msg_len = 0;			/* I have no idea how big it is... */
    msgd.dsc$w_length = 256;		/* There's space for 256 characters */
    msgd.dsc$a_pointer = msg;		/* Where does it go? */

    /* What's the message for the error code */

    sys$getmsg(error, &msg_len, &msgd, 0, &temp);

    /* Terminate the string please */

    msg[msg_len] = '\0';

    /* Send the error message to the log file */

    sprintf(temp, "W %s %s :%s",hook_name.dsc$a_pointer, s, msg);
    send_log_file(temp);
}

/* Minor support function for errors, needed by jan_rec_file() */

static int rec_error(struct dsc$descriptor * s)
{
    return;
}

/* Receive a file and place it accordingly */

int receive_file(struct jnet_file_header * f)
{
    $DESCRIPTOR(d_space, " ");          /* Space holders for something */
    static struct dsc$descriptor_s d_file;
    static struct dsc$descriptor_s d_dest;
    static struct dsc$descriptor_s d_name;

    static struct dsc$descriptor_s
	d_device = { 10, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};

    static struct {
	int append;			/* Unknown */
	int confirm;			/* Unknown */
	int fortran;			/* FORTRAN line carriage control */
	int interrupt;			/* Unknown */
	int log;			/* Unknown */
	int special_format;		/* Special formats possible */
	int translate;			/* EBC to ASC */
    } opt = { TRUE, FALSE, TRUE, FALSE, FALSE, TRUE, TRUE };

    int out_length;			/* Length of local file name */
    char dest_file[128];		/* Where we want it to go */

    /* Figure out all of the file names */

    sprintf(f->jnet_file, "JAN_SPOOL:%s.RSC;-0", hook_name.dsc$a_pointer);
    sprintf(dest_file, "%s%s.RECEIVE", scratch_dir.dsc$a_pointer,
	    hook_name.dsc$a_pointer);

    /* Get the TAG information from the file */

    status = parse_tag(f->jnet_file, f, &d_device);

#ifdef DEBUG
    printf("receive_file: got TAG information\n");
#endif

    /* If that didn't work, don't even think about trying the rest... */

    if (!(status & 1)) {
	report_error("Couldn't parse tag from file", status);
	return (status);
    }

    /* Jnet wants this stuff as descriptors */

    str_desc(&d_file, f->jnet_file);
    str_desc(&d_dest, dest_file);
    f->local_file[0] = '\0';
    str_desc(&d_name, f->local_file);
    d_name.dsc$w_length = 128;

    /* And now tell Jnet where/how/etc we want the file */

    status = jan_rec_file(&d_device, &d_file, &d_space, &d_dest, &d_dest,
	    &opt.translate, opt.log, opt.confirm, &opt.special_format,
	    &opt.fortran, &d_space, &opt.append, &opt.interrupt, &rec_error,
	    &d_name, &out_length);

    /* Did it work? */

    if (!(status & 1)) {
	sprintf(temp, "W Rec_file: %s %s", f->jnet_file, dest_file);
	send_log_file(temp);
	f->local_file[0] = '\0';
    } else {
	f->local_file[out_length & 0xfff] = '\0';
    }

    /* And how did it all go? */

    return(status);
}

/* Read (with RMS) and interpret the file header, which is the RSCS TAG info */

static int parse_tag(char * jf, struct jnet_file_header * f, struct
	dsc$descriptor * d_device)
{
    static struct dsc$descriptor_s
	d_tag	   = {	0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
	d_host	   = { 10, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
	d_user	   = { 10, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
	d_filename = { 10, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
	d_filetype = { 10, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
	d_class    = {	2, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};

    /* Fake out Jnet */

    $DESCRIPTOR(d_dummy, "                  ");

    /* RMS structures and other information */

    struct FAB fab;
    struct RAB rab;
    struct XABFHC xab;
    int mrs;

    /* Some places for Jnet to stuff information */

    int date[2];
    int rec_cnt;
    int spool_id;

    /* And now it gets wierd... */

    fab = cc$rms_fab;			/* Initialize some stuff... */
    rab = cc$rms_rab;
    xab = cc$rms_xabfhc;

    fab.fab$l_xab = &xab;		/* Set the cross pointers */
    rab.rab$l_fab = &fab;

    fab.fab$l_fna = jf; 		/* Name of the file to use */
    fab.fab$b_fns = strlen(jf);

#ifdef DEBUG
    printf("parse_tag: About to open the file\n");
#endif

    status = sys$open(&fab);		/* Open the file */
    if (!(status & 1)) {		/* Did it work? */
	return (status);
    }

    sys$connect(&rab);			/* Hook up the Record Information */
    mrs = (xab.xab$w_lrl & 0xfffe) + 2; /* Record length */

    rab.rab$l_ubf = temp;		/* Buffer for the first record */
    rab.rab$w_usz = mrs;		/* Record length */

    status = sys$get(&rab);		/* Now, let's read the record */
    if (!(status & 1)) {		/* Did it work? */
	return (status);
    }

    sys$disconnect(&rab);		/* Unhook the Record Info */

    sys$close(&fab);			/* And lose the file */

    d_tag.dsc$a_pointer = temp; 	/* The TAG in its entirety */
    d_tag.dsc$w_length = rab.rab$w_rsz; /* How big is it */

    d_host.dsc$a_pointer = f->host;	/* Where we want the info to go */
    d_user.dsc$a_pointer = f->user;
    d_filename.dsc$a_pointer = f->filename;
    d_filetype.dsc$a_pointer = f->filetype;
    d_device->dsc$a_pointer = f->device;
    d_class.dsc$a_pointer = f->class;

    /* Ask Jnet to figure out what's going on here */

    status = jan_tagparse(&d_tag, &d_dummy, &d_dummy, &d_host, &d_user,
	    &d_filename, &d_filetype, d_device, &d_class, &date, &rec_cnt,
	    &spool_id);

    /* Make sure that everything is NULL terminated */

    f->host[8] = '\0';
    f->user[8] = '\0';
    f->filename[8] = '\0';
    f->filetype[8] = '\0';
    f->device[5] = '\0';
    f->class[1] = '\0';

    /* And let caller know how it went */

    return (status);
}

/* Read a Jnet Immediate Message and stuff it */

int receive_message(char * node, char * user, char * msg)
{
    int msglen; 			/* How long was the message */
    struct dsc$descriptor_s node_dsc;	/* Descriptors for the pieces */
    struct dsc$descriptor_s user_dsc;
    struct dsc$descriptor_s msg_dsc;

    node[0] = '\0';                     /* Terminate all of the buffers */
    user[0] = '\0';
    msg[0] = '\0';

    str_desc(&node_dsc, node);		/* Bulid descriptors for all */
    str_desc(&user_dsc, user);
    str_desc(&msg_dsc, msg);

    node_dsc.dsc$w_length = 8;		/* And say how big the buffers are */
    user_dsc.dsc$w_length = 8;
    msg_dsc.dsc$w_length = 255;

    mode = 2;				/* Jnet stuff */

    /* Go grovelling to Jnet for the message and accoutrements */

    jan_receive_msg(&mode, &node_dsc, &user_dsc, &msg_dsc, &msglen);

#ifdef DEBUG
    printf("receive_message: Got the message\n");
#endif

    /* How did it go? */

    if (mode == 2) {
	return;
    }

    /* Terminate all of the strings */

    node[8] = '\0';
    user[8] = '\0';
    msg[msg_dsc.dsc$w_length&0xff] = '\0';

    /* Perform perversities on the message */

    scrunch(strupr(msg));

    /* Log the message reception */

    sprintf(temp, "C %s %s  %s", node, user, msg);
    send_log_file(temp);

    /* And remove trailing blanks for future use */

    strip(node);
    strip(user);

    return;
}
