/*
 * This module handles the non-display operations of the HPSS Queue manager.
 * The apllication calls functions in the module perform starting/stopping
 * of queues and deleteing of queue entries.  All actions taken are
 * logged to a journal file for auditting in order to prevent abuse.
 *
 * The text modules are extracted from the text data file:
 *     queue_start_success
 *     queue_start_failure
 *
 */
#include <stdio.h>
#include <stdlib.h>

#include <ctype.h>
#include <string.h>
#include <ssdef.h>		/* VMS system service return codes */
#include <descrip.h>		/* String descriptors */
#include  <sjcdef.h>		/* VMS job controller definitions */
#include <maildef.h>		/* VMS callable mail */

#include "hpss_share.h"
#include "hpss_qman.h"
int SYS$SNDJBCW(), SYS$ASCTIM(), SYS$GETMSG();
int MAIL$SEND_BEGIN(), MAIL$SEND_ADD_ADDRESS(), MAIL$SEND_ADD_BODYPART();
int MAIL$SEND_ADD_ATTRIBUTE(), MAIL$SEND_MESSAGE(), MAIL$SEND_END();
extern unsigned long mail$send_add_attribute() ;

static char asctim[24];
static $DESCRIPTOR (asctim_dx, asctim);

struct sndjbc_iosb {
    int status;
    int res;
};
struct itmlst {
    unsigned short length, code;
    void *buffer;
    int *ret_len;
};
#define ITMSET(x,cod,len,buf,retaddr) (x)->code=(cod); (x)->length=(len);\
  (x)->buffer = (buf); (x)->ret_len = (int *)(retaddr);
#define ITMSETBOOL(x,cod) (x)->code=(cod); (x)->length=0;\
  (x)->buffer = (void *) 0; (x)->ret_len = (int *) 0;
#define ITMEOL(x) (x)->code = 0; (x)->length = 0;
/*
 * Convert a VMS error code to text.
 */
char *error_text ( int code, char *buffer, int bufsize )
{
    int i, length, status, flags;
    static $DESCRIPTOR(buffer_dx,"");

    buffer_dx.dsc$a_pointer = buffer;
    buffer_dx.dsc$w_length = (bufsize > 255) ? 255 : bufsize-1;
    length = 0;
    flags = 1;			/* text only */
    status = SYS$GETMSG ( code, &length, &buffer_dx, flags, 0 );
    if ( (status&1) == 0 ) {
	sprintf ( buffer, "(Error decoding error code %d)", code );
    } else buffer[length] = '\0';
    return buffer;
}
/***************************************************************************/
/* Send a mail message to specified user if the named module is in the
 * text database.
 */
static int notify_user ( char *module_name, char *username, char *subj_fmt,
	int number, char *queue_name )
{
    int status, line_len;
    void *text;
    unsigned long mail_ctx;
    char line[256], subj_line[256];
    struct itmlst3 {
	short length, code;
	char *buffer;
	int *retlen;
    } in_item[10], out_item[10];
    /*
     * First lookup module.
     */
    text = (void *) 0;
    status = get_module_line ( module_name, &text, line, sizeof(line),
	&line_len );
    if ( (status&1) == 0 ) return status;
    /*
     * Begin mail message
     */
    mail_ctx = 0;
    in_item[0].length = in_item[0].code = 0;
    out_item[0].length = out_item[0].code = 0;

    status = MAIL$SEND_BEGIN ( &mail_ctx, in_item, out_item );
    if ( (status&1) == 0 ) printf (
	"Status of send_begin (user %s): %d\n", username, status );
    if ( (status&1) == 0 ) return status;
    /*
     * Add addressee and subject.
     */
    sprintf ( subj_line, subj_fmt, number, queue_name );
    in_item[0].length = strlen ( subj_line );
    in_item[0].code = MAIL$_SEND_SUBJECT;
    in_item[0].buffer = subj_line;
    in_item[0].retlen = (int *) 0;
    in_item[1].length = in_item[1].code = 0;

    status = MAIL$SEND_ADD_ATTRIBUTE ( &mail_ctx, in_item, out_item );
    if ( (status&1) == 0 ) {
	printf("Status of add_attr: %d - '%s'\n", status, subj_line );
	MAIL$SEND_END ( &mail_ctx, out_item, out_item );
	return status;
    }

    in_item[0].length = strlen ( username );
    in_item[0].code = MAIL$_SEND_USERNAME;
    in_item[0].buffer = username;

    status = MAIL$SEND_ADD_ADDRESS ( &mail_ctx, in_item, out_item );
    if ( (status&1) == 0 ) {
	printf("Status of add_addr: %d\n", status );
	MAIL$SEND_END ( &mail_ctx, out_item, out_item );
	return status;
    }
    /*
     * Send text lines.
     */
    in_item[0].code = MAIL$_SEND_RECORD;
    in_item[0].buffer = line;
    while ( 1 & get_module_line("", &text, line, sizeof(line), &line_len) ) {
	in_item[0].length = line_len;
	status = MAIL$SEND_ADD_BODYPART ( &mail_ctx, in_item, out_item );
	if ( (status&1) == 0 ) {
	    printf("Status of add_body: %d - %s\n", status, line );
	    MAIL$SEND_END ( &mail_ctx, out_item, out_item );
	    return status;
	}
    }
    /*
     * Complete the send operation and cleanup.
     */
    in_item[0].length = in_item[0].code = 0;
    status = MAIL$SEND_MESSAGE ( &mail_ctx, in_item, out_item );
    if ( (status&1) == 0 ) printf("Status of send_msg: %d\n", status );

    MAIL$SEND_END ( &mail_ctx, out_item, out_item );
    return status;
}
/***************************************************************************/
/* The following set of routines manage the 'action' log, which records
 * queue start and job delete activities for autditing purposes.
 */
static FILE *action_log = (FILE *) 0;
static int action_log_dirty;
int set_action_log ( char *fname )
{
    /*
     * Open existing file if present.  Open file for shared access so
     * we can view it without killing this program.
     */
    action_log = fopen ( fname, "a", "alq=100", "deq=200", "mrs=512",
	"shr=upd", "ctx=rec" );
    action_log_dirty = 0;
    return (action_log) ? 1 : 0;
}

static void log_action ( char *username, char *action, char *queue, int job,
	char *job_owner )
{
    int timlen, len;
    if ( !action_log ) return;		/* no action log open */
    /*
     * Place timestamp on record written.
     */
    timlen = 0;
    asctim_dx.dsc$w_length = sizeof(asctim) - 1;
    SYS$ASCTIM ( &timlen, &asctim_dx, 0, 0 );
    asctim[timlen<sizeof(asctim)?timlen:sizeof(asctim)-1] = '\0';

    len = fprintf ( action_log, "%s, user %s %s queue %s (#%d)%s%s\n",
	asctim, username, action, queue, job, *job_owner ? " owned by " : "",
	job_owner );
    action_log_dirty = 1;		/* flag that flush needed */

    if ( len < 0 ) {
	fprintf(stderr,"Error writing to action log, time: %s\n", asctim );
    }
}
void flush_action_log ()
{
    if ( action_log && action_log_dirty ) {
	action_log_dirty = 0;
	fflush ( action_log );
	fsync ( fileno ( action_log ) );
    }
}
/***************************************************************************/
/* Start a stopped queue.
 */
int start_queue ( int *ctx, struct queue_def *qd, char *queue_name,
	char *username )
{
    struct itmlst item[5];
    int status;
    struct sndjbc_iosb iosb;
    char errmsg[256];
    /*
     * Build item list.
     */
    ITMSET(&item[0],SJC$_QUEUE, strlen(queue_name), queue_name, 0 )
    ITMEOL(&item[1])
    /*
     */
    hpss_printf_c ( ctx, "Content-type: text/html\r\n\r\n" );
    hpss_printf_c ( ctx, 
	"<HTML><HEAD><TITLE>Start queue %s</TITLE></HEAD><BODY>\r\n",
	queue_name );

    status = SYS$SNDJBCW ( 0, SJC$_START_QUEUE, 0, item, &iosb, 0, 0 );
    printf("Status of start_queue sndjbc: %d %d\n", status, iosb.status );
    if ( (status&1) == 1 ) status = iosb.status;
    if ( (status&1) == 0 ) {
	hpss_printf_c ( ctx, 
	    "<DL><DT>Error starting queue %s:</DT><DD>%s</DD></DL>\r\n", 
		queue_name, error_text(status,errmsg,sizeof(errmsg)) );
	send_text ( ctx, "queue_start_failure" );
    } else {
	/*
	 * Lookup up text module for queue start
	 */
	log_action ( username, "started", queue_name, 0, "" );
	hpss_printf_c ( ctx, "Queue %s started\n", queue_name );
	send_text ( ctx, "queue_start_success" );
    }
    hpss_printf_c ( ctx, "</BODY></HTML>\r\n" );
    return 1;
}
/****************************************************************************/
int delete_queue_entry ( int *ctx, struct queue_def *qd, char *queue_name, 
	char *username, char *path_info )
{
    struct itmlst item[5];
    int status, number;
    struct sndjbc_iosb iosb;
    char *job_user, errmsg[256];
    /*
     * Decode and verify the entry number.
     */
    number = atoi ( (path_info) ? path_info : "-1" );
    job_user = "*UNKNOWN*";
    if ( number > 0 ) {
        status = get_queue_status ( qd, 1, username );
	if ( (status&1) == 1 ) {
	    struct job_def *job;
	    for ( job = qd->first_job; job; job = job->next ) {
		if ( job->entry_number == number ) {
		    job_user = job->username;
		    break;
		}
	    }
	}
    }
    /*
     * Build item list.
     */
    ITMSET(&item[0],SJC$_QUEUE, strlen(queue_name), queue_name, 0 )
    ITMSET(&item[1],SJC$_ENTRY_NUMBER, sizeof(number), &number, 0 )
    ITMEOL(&item[2])
    /*
     */
    hpss_printf_c ( ctx, "Content-type: text/html\r\n\r\n" );
    hpss_printf_c ( ctx, 
	"<HTML><HEAD><TITLE>Delete entry %s</TITLE></HEAD><BODY>\r\n",
	queue_name );

    status = SYS$SNDJBCW ( 0, SJC$_DELETE_JOB, 0, item, &iosb, 0, 0 );
    /* printf("Status of delete_entry (%s) sndjbc: %d %d\n", 
		path_info, status, iosb.status ); */
    if ( (status&1) == 1 ) status = iosb.status;
    if ( (status&1) == 0 ) {
	hpss_printf_c ( ctx, 
	    "<DL><DT>Error deleting entry %d in queue %s:</DT><DD>%s</DD></DL>\r\n", 
		number, queue_name, error_text(status,errmsg,sizeof(errmsg)) );
	send_text ( ctx, "delete_entry_failure" );
    } else {
	/*
	 * Lookup up text module for delete_entry
	 */
	log_action ( username, "deleted entry in", queue_name, number,
		job_user );
	hpss_printf_c ( ctx, "Entry %d in queue %s deleted\n", 
		number, queue_name );
	send_text ( ctx, "delete_entry_success" );
	/*
	 * Send mail to affected user if different than authenticated user.
	 */
	if ( strcmp ( username, job_user ) != 0 ) {
	    status = notify_user ( "delete_entry_mail", job_user, 
		"Entry %d in queue %s deleted", number, queue_name );
	    if ( (status&1) == 1 ) {
		hpss_printf_c ( ctx,
		    "<P>Job owner (%s) notified of deletion via E-mail.\r\n",
		    job_user );
	    } else if ( status != SS$_ENDOFFILE ) {
		hpss_printf_c ( ctx, 
			"<P><B>Error sending E-mail to %s:</B> %s\r\n",
			job_user, error_text(status,errmsg,sizeof(errmsg)) );
	    }
	}
    }
    hpss_printf_c ( ctx, "</BODY></HTML>\r\n" );
    return 1;
}
/****************************************************************************/
int release_queue_entry ( int *ctx, struct queue_def *qd, char *queue_name, 
	char *username, char *path_info )
{
    struct itmlst item[5];
    int status, number;
    struct sndjbc_iosb iosb;
    char errmsg[256];
    /*
     * Decode and verify the entry number.
     */
    number = atoi ( (path_info) ? path_info : "-1" );
    /*
     * Build item list.
     */
    ITMSET(&item[0],SJC$_QUEUE, strlen(queue_name), queue_name, 0 )
    ITMSET(&item[1],SJC$_ENTRY_NUMBER, sizeof(number), &number, 0 )
    ITMSETBOOL(&item[2],SJC$_NO_HOLD)
    ITMEOL(&item[3])
    /*
     */
    hpss_printf_c ( ctx, "Content-type: text/html\r\n\r\n" );
    hpss_printf_c ( ctx, 
	"<HTML><HEAD><TITLE>Delete entry %s</TITLE></HEAD><BODY>\r\n",
	queue_name );

    status = SYS$SNDJBCW ( 0, SJC$_ALTER_JOB, 0, item, &iosb, 0, 0 );
    /* printf("Status of release_entry (%s) sndjbc: %d %d\n", 
		path_info, status, iosb.status ); */
    if ( (status&1) == 1 ) status = iosb.status;
    if ( (status&1) == 0 ) {
	hpss_printf_c ( ctx, 
	    "<DL><DT>Error releaseing entry %d in queue %s:</DT><DD>%s</DD></DL>\r\n", 
		number, queue_name, error_text(status,errmsg,sizeof(errmsg)) );
	send_text ( ctx, "release_entry_failure" );
    } else {
	/*
	 * Lookup up text module for queue start
	 */
	hpss_printf_c ( ctx, "Entry %d in queue %s deleted\n", 
		number, queue_name );
	send_text ( ctx, "release_entry_success" );
    }
    hpss_printf_c ( ctx, "</BODY></HTML>\r\n" );
    return 1;
}
