/* $Id: log.c,v 1.12 1999/06/24 18:52:07 levitte Exp $ */

#include "gnu_extras.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ssdef.h>
#include <lib$routines.h>
#include <starlet.h>
#include "fish.h"
#include "ssh.h"
#include "log.h"
#include "util.h"
#include "fishmsg.h"

/* I stole this from Dave Jones' SSH_SERVER */
/*
 * Define table of format strings for format and scan functions, used to
 * pack and unpack data into SSH protocol messages.  String encodes layout of 
 * message:
 *        i	32-bit integer, stored into next arg in host order.
 *	  m	multi-precision integer, stored into next 2 args.
 *	 nb	'n' bytes (e.g. 8b).
 *	  s	Counted string, stored into next 2 args: count and pointer.
 *	  p	Counted string containing password, stored into next 2 args:
 *		count and pointer.
 *        nx	Return current buffer pointer, advance by n if n not zero.
 *        .     Terminates format, REQUIRED.  Remainder of string is
 *		descriptive text.
 */
static char *msg_format[] = { 
    ".MSG_NONE",			/* 0 - none */
    "s.MSG_DISCONNECT",	       		/* 1 - disconnect */
    "8bimmimmiii.SMSG_PUBLIC_KEY",	/* 2 - public_key */
    "b8bmi.CMSG_SESSION_KEY",		/* 3 - sesion_key */
    "s.CMSG_USER",			/* 4 - user */
    "s.CMSG_AUTH_RHOSTS",		/* 5 - auth rhosts */
    "m.CMSG_AUTH_RSA",			/* 6 - auth rsa */
    "m.SMSG_AUTH_RSA_CHALLENGE",	/* 7 - auth rsa challenge */
    "16b.CMSG_AUTH_RSA_RESPONSE",	/* 8 - auth rsa response */
    "p.CMSG_AUTH_PASSWORD",		/* 9 - password */

    "siiiix.CMSG_REQUEST_PTY",		/* 10 - request pty */
    "iiii.CMSG_WINDOW_SIZE",		/* 11 - window size */
    ".CMSG_EXEC_SHELL",			/* 12 - exec shell */
    "s.CMSG_EXEC_CMD",			/* 13 - exec cmd */
    ".SMSG_SUCCESS",			/* 14 - success */
    ".SMSG_FAILURE",			/* 15 - failure */
    "s.CMSG_STDIN_DATA",		/* 16 - stdin data */
    "s.SMSG_STDOUT_DATA",		/* 17 - stdout data */
    "s.SMSG_STDERR_DATA",		/* 18 - stderr data */
    ".CMSG_EOF",			/* 19 - stdin EOF */

    "i.SMSG_EXITSTATUS",		/* 20 - exit status*/
    "ii.MSG_CHANNEL_OPEN_CONFIRMATION",	/* 21 - channel open confirm */
    "i.MSG_CHANNEL_OPEN_FAILURE",	/* 22 - channel open failure */
    "is.MSG_CHANNEL_DATA",		/* 23 - channel data */
    "i.MSG_CHANNEL_CLOSE",		/* 24 - channel close */
    "i.MSG_CHANNEL_CLOSE_CONFIRMATION",	/* 25 - channel close confirm */
    ".CMSG_X11_REQUEST_FORWARDING",	/* 26 - X11 request forwarding */
    "is.SMSG_X11_OPEN",			/* 27 - X11 open */
    "isi.CMSG_PORT_FORWARD_REQUEST",	/* 28 - port forward request */
    "isis.MSG_PORT_OPEN",		/* 29 - port open */

    ".CMSG_AGENT_REQUEST_FORWARDING",	/* 30 - agent request forwarding */
    "i.SMSG_AGENT_OPEN",		/* 31 - agent open */
    "s.MSG_IGNORE",			/* 32 - NOP message */
    ".CMSG_EXIT_CONFIRMATION",		/* 33 - exit confirmation */
    "ssi.CMSG_X11_REQUEST_FORWARDING",	/* 34 - x11 forward with auth spoof */
    "simm.CMSG_AUTH_RHOSTS_RSA",	/* 35 - auth rhosts rsa */
    "s.MSG_DEBUG",			/* 36 - debug message */
    "i.CMSG_REQUEST_COMPRESSION",	/* 37 - compression request */
    "i.CMSG_MAX_PACKET_SIZE",		/* 38 - max packet size */
    ".CMSG_AUTH_TIS",			/* 39 - start TIS authorization */

    "s.SMSG_AUTH_TIS_CHALLENGE",	/* 40 - server TIS challenge */
    "p.CMSG_AUTH_TIS_RESPONSE",		/* 41 - challenge response */
    "s.CMSG_AUTH_KERBEROS",		/* 42 - start Kerberos authorization */
    "s.SMSG_AUTH_KERBEROS_RESPONSE",	/* 43 - server response */
    "s.CMSG_HAVE_KERBEROS_TGT",		/* 43 - kerberos creadentials */
};

int log_verbose(int type, general_buffer_reader *data, ssh_state *state)
{
    char *msg_type = 0;
    char *fmt;
    int i;
    int count = 0;
    general_buffer *buf;
    general_buffer_reader br;

    /* We want to duplicate the reader, so the pointers needed by others
       will stay the same... */
    memcpy(&br, data, sizeof(general_buffer_reader));

    if (type == -1)
	return SS$_NORMAL;

    switch(type) {
    case SSH_CMSG_STDIN_DATA:
    case SSH_SMSG_STDOUT_DATA:
    case SSH_SMSG_STDERR_DATA:
    case SSH_CMSG_EOF:
    case SSH_MSG_CHANNEL_DATA:
	if (!(verbose & VERBOSE_M_IO))
	    return SS$_NORMAL;
	break;
    }

    buf = buf_init(1024);
    fmt = msg_format[type];

    for (i = count = 0; fmt[i] != '.'; i++)
	switch(fmt[i]) {
	case '0':
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	case '8':
	case '9':
	    count = count*10 + (fmt[i] - '0');
	    break;
	case '?':
	case 'b':
	case 'x': {
	    int j;
	    char *prefix = "";
	    int prefix_l = 0;
	    char tmpbuf[15];
	    int tmptotal;
	    char tmpc;

	    buf_append_chars_nocount(buf, ", ");

	    if (count == 0)
		count = 1;
	    else {
		sprintf(tmpbuf, "(%d)", count);
		buf_append_chars_nocount(buf, tmpbuf);
	    }

	    switch(fmt[i]) {
	    case '?':
		count = bufread_remaining(&br);;
		buf_append_chars_nocount(buf, "?");
		break;
	    case 'x':
		buf_append_chars_nocount(buf, "{Raw}");
		break;
	    }

	    tmpc = '[';
	    for (j = count; j-- > 0;) {
		unsigned char c;

		bufread_bytes(&br, &c, 1);
		sprintf(tmpbuf,"%02X", c);
		buf_append_bytes(buf, &tmpc, 1); tmpc = ' ';
		buf_append_chars_nocount(buf, tmpbuf);
	    }
	    buf_append_chars_nocount(buf, "]");
	    count = 0;
	    break;
	}
	case 'i': {
	    unsigned long j;
	    char tmpbuf[11];

	    bufread_int(&br, &j);

	    sprintf(tmpbuf, ", %08X", j);
	    buf_append_chars_nocount(buf, tmpbuf);
	    break;
	}
	case 'm': {
	    int j;
	    unsigned short size;
	    unsigned short bytes;
	    char tmpbuf[30];
	    int tmptotal;

	    bufread_word(&br, &size);
	    bytes = (size+7)/8;

	    sprintf(tmpbuf, ", (%d/%d)", size, bytes);
	    buf_append_chars_nocount(buf, tmpbuf);

	    for (j = bytes; j-- > 0;) {
		unsigned char c;

		bufread_bytes(&br, &c, 1);
		sprintf(tmpbuf,"%02X", c);
		buf_append_chars_nocount(buf, tmpbuf);
	    }
	    break;
	}
	case 's':
	case 'p': {
	    unsigned long size;
	    char *data_p;
	    char tmpbuf[30];
	    int tmptotal;

	    bufread_int(&br, &size);

	    sprintf(tmpbuf, ", (%d)", size);
	    buf_append_chars_nocount(buf, tmpbuf);

	    if (fmt[i] == 'p' && !(verbose & VERBOSE_M_PASSWORD)) {
		buf_append_chars_nocount(buf, "\"<password>\"");
	    } else {
		buf_append_chars_nocount(buf, "\"");
		buf_append_bytes(buf, bufread_chars_noadjust(&br), size);
		bufread_glob(&br, size);
		buf_append_chars_nocount(buf, "\"");
	    }
	    break;
	}
	}
    buf_append_bytes(buf, "\0", 1); /* To make sure it's NUL-terminated */
    i++;
{
    int oldast = sys$setast(0);
    unsigned int status, space;

    if (fmt[i] == 'C') {
	status = FISH_M_CLIENTMSG;
	space = 77-19;
    } else {
	status = FISH_M_SERVMSG;
	space = 77-17;
    }

    if (!(verbose & VERBOSE_M_LONGINFO)) {
	if (buf_amount(buf) > 80) {
	    buf_chop_at(buf, space - strlen(fmt+i));
	    buf_append_chars_nocount(buf, " ..");
	}
        lib$signal(status, 2, fmt+i, buf_chars_noadjust(buf));
    } else {
	/* kludge because $PUTMSG won't output more than 255 bytes */
	lib$signal(status, 2, fmt+i, "");
	if (buf_amount(buf) >= 2)
	    fprintf(stderr, "Data: %s\n", buf_chars_noadjust(buf) + 2);
    }
    
    sys$setast(oldast == SS$_WASSET);
    buf_destroy(buf);
}
return SS$_NORMAL;
}

/* Emacs local variables

Local variables:
eval: (set-c-style "BSD")
end:

*/
