/*	`
 * gcc -o rx-gate rx-gate.c -O
 *
 * CGI Gateway to Rexx scripts. Due to the way Rexx scripts work, they don't
 * inherit local variables from the httpd that spawned them. This C program
 * does inhereit them, and makes the available via a Rexx port. It launches
 * the script with the cgi port as the scripts default port.
 *
 * Invoke this with the script name to use.
 *
 * Copyright © 1994, Mike Meyer, modified by Jeff Shepherd to use with Apache
 */

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include <exec/types.h>
#include <dos/dos.h>
#include <rexx/storage.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/rexxsyslib.h>

#define MAXVAR 256
#define STDOUT 1

struct RxsLib *RexxSysBase ;
struct MsgPort *port ;

#define dead(x) return corpse(x,NULL,NULL)
int corpse(char *, char *, char *) ;

char line[1024];
int main(int argc, char **argv) {
    char *value, command[MAXVAR], port_name[20];
    char error_msg[MAXVAR];
    char tempstdoutname[24], tempstdinname[29];
    char *com;
    BPTR tempstdout, oldstdout, tempstdin, oldstdin;
    FILE *fi;
    char *cmdline = NULL;
    int len;
    struct RexxMsg  *msg ;
    struct MsgPort *rexxport ;
    int i;
    int exit_status = 0;

    RexxSysBase = NULL ;
    port = NULL ;

#ifdef DEBUG
    puts("Content-type: text/plain\n");
#endif

    /* Get the Rexx library */
    if ((RexxSysBase = (struct RxsLib *)OpenLibrary("rexxsyslib.library", 0)) == NULL)
	    dead("Can't open rexx library") ;

    /* Get a message port */
    sprintf(port_name, "CGI-PORT-%lx", (ULONG)FindTask(NULL));

    /* temporary files for stdin and stdout */
    sprintf(tempstdoutname, "t:CGI-PORT-%lx", (ULONG)FindTask(NULL));
    sprintf(tempstdinname, "t:CGI-PORT-%lx-stdin", (ULONG)FindTask(NULL));
    Forbid() ;
    if (!FindPort(port_name)) port = CreatePort(port_name, 0) ;
    Permit() ;
    if (port == NULL)       /* BAD news */
	    dead("Can't create Rexx port") ;

    /* Build the message that starts things rolling */
    if ((msg = CreateRexxMsg(port, NULL, port->mp_Node.ln_Name)) == NULL)
	    dead("Can't create rexx message") ;

    /* Make one big command line to pass to the arexx script */
    for (len  = 1, i = 0; i < argc; i++) {
#ifdef DEBUG
	printf("Argv %d = %s\n", i, argv[i]);
#endif
	len += strlen(argv[i]) + 1;
    }

    cmdline = malloc(len);
    strcpy (cmdline, argv[0]);
    for (i = 1; i < argc; i++) {
	strcat(cmdline, " ");
	strcat(cmdline, argv[i]);
    }

#ifdef DEBUG
    printf("Cmdline is %s\n", cmdline);
#endif
    msg->rm_Args[0] = cmdline;
    if (!FillRexxMsg(msg, 1, 0)) {
	    free(cmdline);
	    DeleteRexxMsg(msg) ;
	    dead("Can't create rexx arguments") ;
    }
    free(cmdline);
    msg->rm_Action = RXCOMM;

    /* Since I was started by exec() my Output() is bogus but stdout
     * is the socket, I will set my Output() to a temp file
     * Also redirect stdin to a temp file since Input() is also the socket
     */
     fi = fopen(tempstdinname, "w");
     if (fi) {
	while(fgets(line, 1024, stdin))
	    fprintf(fi,"%s",line);
	fclose(fi);
    }

    tempstdout = Open(tempstdoutname, MODE_NEWFILE);
    tempstdin = Open(tempstdinname, MODE_OLDFILE);
    oldstdout = SelectOutput(tempstdout);
    oldstdin = SelectInput(tempstdin);

    /* Go! */
    Forbid();
    if ((rexxport = FindPort(RXSDIR)))
	    PutMsg(rexxport, (struct Message *) msg) ;
    Permit();

    if (!rexxport) {
	ClearRexxMsg(msg, 1);
	DeleteRexxMsg(msg);
	SelectOutput(oldstdout);
	SelectOutput(oldstdin);
	Close(tempstdout);
	Close(tempstdin);
	remove(tempstdinname);
	remove(tempstdoutname);
	dead("Can't find rexx port");
    }

    /* must redirect Arexx output to file because stdin is a socket */
    /* Ok, now we just wait for the message to come back... */
    for (;;) {
	WaitPort(port) ;
	msg = (struct RexxMsg *) GetMsg(port) ;
	if (msg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG) break ;

	/* That wasn't the reply, so we have a command */
	strcpy(command, msg->rm_Args[0]) ;
#ifdef DEBUG
	printf("Command %s\n", command);
#endif
	com = strtok(command, " \t") ;

	/* if it was getvar, handle it */
	if (!stricmp(com , "getvar")) {
	    char *tok;
	    /* Doing getvar */
	    tok = strtok(NULL, " \t");
	    if ((value = getenv(tok)) == NULL)
		value = "";
	    msg->rm_Result1 = 0 ;
	    if (!(msg->rm_Action & RXFF_RESULT))
		msg->rm_Result2 = strlen(value) ;
	    else msg->rm_Result2 =
		(LONG) CreateArgstring(value,
		strlen(value)) ;
	}
	else {
#ifdef DEBUG
	    printf("Bad token %s\n", com);
#endif
	    msg->rm_Result1 = 10 ;
	    msg->rm_Result2 = 11 ;
	    exit_status = 1;
	    sprintf(error_msg, "Rexx error: #%ld", msg->rm_Result2) ;
	}
	ReplyMsg((struct Message *) msg) ;
    }

    ClearRexxMsg(msg, 1) ;
    DeleteRexxMsg(msg) ;
    Close(tempstdout);
    Close(tempstdin);

    /* echo the contents of the file to the socket */
    SelectOutput(oldstdout);
    SelectInput(oldstdin);

    /* only print the contents of the file if the exit status is 0 */
    if (exit_status == 0) {
	fi = fopen(tempstdoutname, "r");
	if (fi) {
	    while(fgets(line, 1024, fi))
		printf("%s", line);
	    fclose(fi);
	}
	remove(tempstdinname);
	remove(tempstdoutname);
	dead(NULL);
    }
    else
	return corpse(error_msg, tempstdinname, tempstdoutname);
}

int
corpse(char *mesg, char *stdinfi, char *stdoutfi) {
    char *program_name ;

#ifdef DEBUG
    printf("mesg is %s, stdinfi is %s, stdoutfi is %s\n", mesg, stdinfi, stdoutfi);
#endif
    if (mesg) {
	if ((program_name = getenv("SERVER_NAME")) == NULL)
	    program_name = "NOT CONFIGURED" ;
	printf("Content-type: text/html\n\n"
	    "<HTML><HEAD><TITLE>Rexx Gateway error</TITLE></HEAD><BODY>\n"
	    "<h1>%s</h1>\nPlease contact the %s server admin.\n",
	    mesg, program_name) ;
    }

    if (stdinfi) {
	char line[1024];
	FILE *fi;

	fi = fopen(stdinfi, "r");
	if (fi) {
	    if (!mesg)
		puts("Content-type: text/html\n");

	    puts("<HR><h1>STDIN</h1>");
	    puts("<CODE>");
	    while(fgets(line,1024,fi))
		printf("%s",line);
	    fclose(fi);
	    puts("</CODE>");
	}
	remove(stdinfi);
    }

    if (stdoutfi) {
	char line[1024];
	FILE *fi;

	fi = fopen(stdoutfi, "r");
	if (fi) {
	    puts("<HR><h1>STDOUT</h1>");
	    puts("<CODE>");
	    while(fgets(line,1024,fi))
		printf("%s",line);
	    fclose(fi);
	    puts("</CODE>");
	}
	remove(stdoutfi);
    }

    if (mesg || stdinfi || stdoutfi)
	puts("</HTML>");

    if (port) {
	FreeSignal((long) (port->mp_SigBit)) ;
	RemPort(port) ;
	DeletePort(port) ;
    }
    if (RexxSysBase)
	CloseLibrary((struct Library *)RexxSysBase) ;

    return 0 ;
}
