#include "stdafx.h"

static int  file_write(BIO *h, const char *buf, int num);
static int  file_read(BIO *h, char *buf, int size);
static int  file_puts(BIO *h, const char *str);
static int  file_gets(BIO *h, char *str, int size);
static long file_ctrl(BIO *h, int cmd, long arg1, void *arg2);
static int  file_new(BIO *h);
static int  file_free(BIO *data);
static long nullf_callback_ctrl(BIO *h, int cmd, bio_info_cb *fp);
static BIO_METHOD methods_logger =
{
	BIO_TYPE_FILTER,
	"FILE logger",
	file_write,
	file_read,
	file_puts,
	file_gets,
	file_ctrl,
	file_new,
	file_free,
	nullf_callback_ctrl,
};

typedef struct nbio_logger
{
	HANDLE fp;
	enum {
		none,
		read,
		write,
	} direction;
} NBIO_LOGGER;

BIO_METHOD *BIO_f_logger(void)
{
	return(&methods_logger);
}

BIO *BIO_new_filelogger(TCHAR *filename)
{
	BIO  *ret;
	NBIO_LOGGER *buf;

	if (!(buf = new NBIO_LOGGER)) return(0);
	HANDLE fp = CreateFile(
		filename,
		GENERIC_WRITE,
		FILE_SHARE_READ,
		NULL,
		CREATE_ALWAYS,
		FILE_ATTRIBUTE_NORMAL,
		NULL);
	if (fp == INVALID_HANDLE_VALUE) {
		delete buf;
		return(0);
	}
	ret = BIO_new(BIO_f_logger());
	buf->fp = fp;
	ret->ptr = buf;

	return(ret);
}

static int file_new(BIO *bi)
{
	bi->init = 1;
	bi->ptr = NULL;
	bi->flags = 0;
	return(1);
}

static int file_free(BIO *a)
{
	if (a == NULL) return(0);
	if (a->ptr != NULL)
	{
		NBIO_LOGGER *buf = (NBIO_LOGGER *)a->ptr;
		CloseHandle(buf->fp);
		delete buf;
		a->ptr = NULL;
	}
	return(1);
}

static int file_read(BIO *b, char *out, int outl)
{
	int ret = 0;
	DWORD d;

	if (out == NULL) return(0);
	NBIO_LOGGER *buf = (nbio_logger *)b->ptr;
	if (buf->direction != NBIO_LOGGER::read) {
		buf->direction = NBIO_LOGGER::read;
		std::string str("\n<<<<read<<<<\n");
		WriteFile(buf->fp, str.c_str(), (int)str.length(), &d, NULL);
	}
	if (b->next_bio == NULL) return(0);
	ret = BIO_read(b->next_bio, out, outl);
	WriteFile(buf->fp, out, outl, &d, NULL);
	BIO_clear_retry_flags(b);
	BIO_copy_next_retry(b);
	return(ret);
}

static int file_write(BIO *b, const char *in, int inl)
{
	int ret = 0;
	DWORD d;

	if ((in == NULL) || (inl <= 0)) return(0);
	NBIO_LOGGER *buf = (nbio_logger *)b->ptr;
	if (buf->direction != NBIO_LOGGER::write) {
		buf->direction = NBIO_LOGGER::write;
		std::string str("\n>>>>write>>>>\n");
		WriteFile(buf->fp, str.c_str(), (int)str.length(), &d, NULL);
	}
	if (b->next_bio == NULL) return(0);
	ret = BIO_write(b->next_bio, in, inl);
	WriteFile(buf->fp, in, inl, &d, NULL);
	BIO_clear_retry_flags(b);
	BIO_copy_next_retry(b);
	return(ret);
}

static long file_ctrl(BIO *b, int cmd, long num, void *ptr)
{
	long ret;

	if (b->next_bio == NULL) return(0);
	switch (cmd)
	{
	case BIO_C_DO_STATE_MACHINE:
		BIO_clear_retry_flags(b);
		ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
		BIO_copy_next_retry(b);
		break;
	case BIO_CTRL_DUP:
		ret = 0L;
		break;
	default:
		ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
	}
	return(ret);
}

static long nullf_callback_ctrl(BIO *b, int cmd, bio_info_cb *fp)
{
	long ret = 1;

	if (b->next_bio == NULL) return(0);
	switch (cmd)
	{
	default:
		ret = BIO_callback_ctrl(b->next_bio, cmd, fp);
		break;
	}
	return(ret);
}

static int file_gets(BIO *bp, char *buf, int size)
{
	int ret;
	DWORD d;

	if (bp->next_bio == NULL) return(0);
	NBIO_LOGGER *p = (nbio_logger *)bp->ptr;
	if (p->direction != NBIO_LOGGER::read) {
		p->direction = NBIO_LOGGER::read;
		std::string str("\n<<<<read<<<<\n");
		WriteFile(p->fp, str.c_str(), (int)str.length(), &d, NULL);
	}
	ret = BIO_gets(bp->next_bio, buf, size);
	std::string str(buf);
	WriteFile(p->fp, str.c_str(), (int)str.length(), &d, NULL);
	return ret;
}


static int file_puts(BIO *bp, const char *str)
{
	int ret;
	DWORD d;

	if (bp->next_bio == NULL) return(0);
	NBIO_LOGGER *p = (nbio_logger *)bp->ptr;
	if (p->direction != NBIO_LOGGER::write) {
		p->direction = NBIO_LOGGER::write;
		std::string str("\n>>>>write>>>>\n");
		WriteFile(p->fp, str.c_str(), (int)str.length(), &d, NULL);
	}
	ret = BIO_puts(bp->next_bio, str);
	std::string strbuf(str);
	WriteFile(p->fp, strbuf.c_str(), (int)strbuf.length(), &d, NULL);
	return ret;
}
