/*
 * Invisible IRC proxy
 *
 * written by the Invisible Irc Dev Duo 0x90 and fish (phish)
 *<0x90@hushmail.com> & fish: <fish@artificial-stupidity.net> 
 */



#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <signal.h>

#include "misc/compat.h"
#ifdef _WINDOZE_
	#include "iip/wingui.h"
#else
//	#include <sys/time.h>
//	#include <sys/types.h>
//	#include <sys/wait.h>
//	#include <netinet/in.h>
//	#include <arpa/inet.h>
//	#include <unistd.h>
//	#include <netdb.h>
//	#include <strings.h>
//	#define recv(x,y,z,a) read(x,y,z)
//	#define send(x,y,z,a) write(x,y,z)
//	#define closesocket(s) close(s)
#endif
#include "iip/iip.h"
#include "base/thread.h"
#include "base/mem.h"

#include "crypt/random.h"
#include "crypt/salt.h"
#include "net/nodeserv.h"
#include "msgcore/core.h"
#include "iip/debug.h"
#include "base/bignum.h"
#include "crypt/dh.h"
#include "misc/global.h"

#include "base/logger.h"

#include "net/noderef.h"
#include "net/nodeserv.h"
#include "net/protocol.h"
#include "net/protocfg.h"
#include "pipe/pipe.h"
#include "pipeface/pipeface.h"
#include "pipeface/raw.h"
//#include "pf-virc.h"
#include "pipe/closdlay.h"
#include "pipe/crypt.h"
#include "pipe/c-stream.h"
#include "pipe/cs-iip1.h"
#include "pipe/cs-iip11.h"
#include "pipe/steady.h"
#include "pipe/spurt.h"
#include "pipe/backward.h"
//#include "pipe/masq.h"
#include "pipe/dummy.h"
#include "pipe/nodetran.h"
#include "base/cfg.h"

#include "ui/ui-dumb.h"
#include "iip/iip-ui.h"
#include "ui/ui.h"

#include "net/sockserv.h"

#include "file/file.h"

#include "misc/exitcode.h"

#ifdef _UNIX_
#include "misc/unix.h"
#endif

/* a flag: show the entropy gathering dialog on startup? */
int dorandom;
int ignoreentropy;

const char ident[] = "$Id: iip.c,v 1.37 2003/03/09 22:49:24 userxiip Exp $";



//todo: put this somewhere else
//int doFork=TRUE;
int nodetach = 0;
int showhelp = 0;

char *relayprotocol = NULL;

#ifdef _UNIX_
char *iippaths[] = {"~/.iip/", NULL};
#else
char *iippaths[] = {".\\", NULL};
#endif

CryptStreamProtocolEntry cspelist[] = {
	{"crypt", csiip1Make},
	{"cryptiip1", csiip1Make},
	{"cryptiip11", csiip11Make},
	{"cryptiip1.1", csiip11Make},
	BLANKCRYPTSTREAMPROTOCOLENTRY
};

PipeProtocolEntry ppelist[] = {
	{"backward", pipebackwardMake},
	{"closedelay", pipeclosedelayMake},
	{"crypt", pipecryptMake},
	{"cryptiip1", pipecryptMake},
	{"cryptiip11", pipecryptMake},
	{"cryptiip1.1", pipecryptMake},
	{"steady", pipesteadyMake},
	{"spurt", pipespurtMake},
	{"nodeget", pipenodetransMake},
	{"nodesend", pipenodetransMake},
	BLANKPIPEPROTOCOLENTRY
};

PipeFaceProtocolEntry pfpelist[] = {
	{"core", pfcoreMake},
	{"raw", pfcoreMake},
//	{"virc", pfvircMake},
	BLANKPIPEFACEPROTOCOLENTRY
};

//todo: validate functions
//todo: move the help descriptions else where
ConfigEntry ceinit[] = {
	{CETYPE_STRING, {"host", NULL}, CEIB, {&hostname, NULL}, CEFB, NULL, NULL, CEHB}, //{"Hostname to use for creating a public relay.", NULL}},
	{CETYPE_BOOL, {"ignoreself", NULL}, {&ignoreself, 1}, CESB, CEFB, NULL, NULL, CEHB}, //{"It will ignore noderef in 'node.ref' that match it's host name.", NULL}},
	{CETYPE_BOOL, {"publicrelay", NULL}, {&publicrelaynode, 0}, CESB, CEFB, NULL, NULL, CEHB}, //{"This node is a public relay.", NULL}},
	{CETYPE_BOOL, {"autogetnoderefs", NULL}, {&autogetnoderefs, 0}, CESB, CEFB, NULL, NULL, CEHB}, //{"Retrieve nodes by HTTP on start up.", NULL}},
	{CETYPE_INT, {"steadymin", NULL}, {&pipesteadyminimum, 10}, CESB, CEFB, NULL, NULL, CEHB}, //{"'steady' pipe minimum rate.", NULL}},
	{CETYPE_INT, {"steadymax", NULL}, {&pipesteadymaximum, 20}, CESB, CEFB, NULL, NULL, CEHB}, //{"'steady' pipe maximum rate.", NULL}},
	{CETYPE_FUNC, {"protocolgood", NULL}, CEIB, CESB, {protocolAddGood, protocolGetGood, protocolClearGood}, NULL, NULL, CEHB}, //{"A filter that noderefs must pass to be able to use them.", NULL}},
	{CETYPE_FUNC, {"protocolbad", NULL}, CEIB, CESB, {protocolAddBad, protocolGetBad, protocolClearBad}, NULL, NULL, CEHB}, //{"A filter that noderefs must not match to able to use them.", NULL}},
	{CETYPE_STRING, {"informhost", NULL}, CEIB, {&informhost, DEFAULT_INFORMHOST}, CEFB, NULL, NULL, CEHB}, //{"Hostname where public nodes announce themselves.", NULL}},
	{CETYPE_INT, {"informport", NULL}, {&informport, DEFAULT_INFORMPORT}, CESB, CEFB, NULL, NULL, CEHB}, //{"Port of the inform host", NULL}},
	{CETYPE_STRING, {"informpage", NULL}, CEIB, {&informpage, DEFAULT_INFORMPAGE}, CEFB, NULL, NULL, CEHB}, //{"Page of the inform host", NULL}},
	{CETYPE_STRING, {"refgethost", NULL}, CEIB, {&refgethost, DEFAULT_REFGETHOST}, CEFB, NULL, NULL, CEHB}, //{"Hostname where 'node.ref' can retrieved from by HTTP.", NULL}},
	{CETYPE_INT, {"refgetport", NULL}, {&refgetport, DEFAULT_REFGETPORT}, CESB, CEFB, NULL, NULL, CEHB}, //{"Port of the 'node.ref' server.", NULL}},
	{CETYPE_STRING, {"refgetpage", NULL}, CEIB, {&refgetpage, DEFAULT_REFGETPAGE}, CEFB, NULL, NULL, CEHB}, //{"Page of 'node.ref' for user nodes", NULL}},
	{CETYPE_STRING, {"refgetpublicrelaypage", NULL}, CEIB, {&refgetpublicrelaypage, DEFAULT_REFGETPUBLICRELAYPAGE}, CEFB, NULL, NULL, CEHB}, //{"Page of 'node.ref' for public relays.", NULL}},
	
	{CETYPE_BOOL, {"noderefreload", NULL}, {&noderefreload, 1}, CESB, CEFB, NULL, NULL, CEHB}, //{"'nodetrans' pipe accepts incoming node.ref on connection.", NULL}},
	{CETYPE_STRING, {"networkname", NULL}, CEIB, {&networkname, DEFAULT_NETWORKNAME}, CEFB, NULL, NULL, CEHB}, 
	{-CETYPE_STRING, {"networkversion", NULL}, CEIB, {&networkversion, DEFAULT_NETWORKVERSION}, CEFB, NULL, NULL, CEHB}, 

	{CETYPE_STRING, {"relayprotocol", NULL}, CEIB, {&relayprotocol, DEFAULT_RELAYPROTOCOL}, CEFB, NULL, NULL, CEHB}, 

	{CETYPE_BOOL, {"nodetransget", NULL}, {&nodetransget, 1}, CESB, CEFB, NULL, NULL, CEHB}, //{"'nodetrans' pipe accepts incoming node.ref on connection.", NULL}},
	{CETYPE_BOOL, {"nodetranssend", NULL}, {&nodetranssend, 0}, CESB, CEFB, NULL, NULL, CEHB}, //{"'nodetrans' pipe send special node.ref on connection.", NULL}},
	{CETYPE_STRING, {"nodetransfile", NULL}, CEIB, {&nodetransfile , ""}, CEFB, NULL, NULL, CEHB}, //{"'nodetrans' pipe sends this file if enabled.", NULL}},

	{CETYPE_STRING, {"winhelpfile", NULL}, CEIB, {&winhelpfile , "iip.chm"}, CEFB, NULL, NULL, CEHB}, //{"Where it should find the windows help file.", NULL}},
	{CETYPE_STRING, {"winhelpfile2", NULL}, CEIB, {&winhelpfile2 , "iip.htm"}, CEFB, NULL, NULL, CEHB}, //{"Where it should find the windows help file.", NULL}},

	{CETYPE_INT, {"retries", NULL}, {&retries, DEFAULT_RETRIES}, CESB, CEFB, NULL, NULL, CEHB}, //{"Numer of retries to make on outbound connections.", NULL}},

	{CETYPE_INT, {"closedelay", NULL}, {&closedelay, 30}, CESB, CEFB, NULL, NULL, CEHB}, //{"Seconds that 'closedelay' pipes delay closing by.", NULL}},

	{-CETYPE_FLAG, {"logfilesafemode", "F"}, {&logsafemode, 0}, CESB, CEFB, NULL, NULL, CEHB},

	{-CETYPE_FLAG, {"help", NULL}, {&showhelp, 0}, CESB, CEFB, NULL, NULL, {"Show command line options.", NULL}},
	{-CETYPE_FLAG, {NULL, "h"}, {&showhelp, 0}, CESB, CEFB, NULL, NULL, CEHB},
	{-CETYPE_FLAG, {NULL, "H"}, {&showhelp, 0}, CESB, CEFB, NULL, NULL, CEHB},
	{-CETYPE_FLAG, {NULL, "?"}, {&showhelp, 0}, CESB, CEFB, NULL, NULL, CEHB},
	{-CETYPE_FLAG, {"version", NULL}, {&showversion, 0}, CESB, CEFB, NULL, NULL, {"Show version.", NULL}},
	{-CETYPE_FLAG, {NULL, "d"}, {&nodetach, 0}, CESB, CEFB, NULL, NULL, {"Don't detach from console.", NULL}},
	{-CETYPE_FLAG, {NULL, "C"}, {&doconfig, 0}, CESB, CEFB, NULL, NULL, {"Run configuration screen.", NULL}},
	{-CETYPE_FLAG, {"randomdialog", "R"}, {&dorandom, 0}, CESB, CEFB, NULL, NULL, {"Run the entropy gathering dialog at startup.", NULL}},
	{-CETYPE_FLAG, {"ignoreemptyrandom", NULL}, {&ignoreentropy, 0}, CESB, CEFB, NULL, NULL, {"Let isproxy start up even if there isn't enough entropy. (THIS IS A SECURITY RISK: Don't do it unless you really need to.)", NULL}},
	{-CETYPE_INT, {NULL, "K"}, {&makeKeys, 0}, CESB, CEFB, NULL, NULL, {"Make key pair of # bits and exit.", NULL}},
	{-CETYPE_FLAG, {"quiet", "q"}, {&quiet, 0}, CESB, CEFB, NULL, NULL, {"Don't display some messages at startup.", NULL}},
	{-CETYPE_FLAG, {"quietconsole", "Q"}, {&quietconsole, 0}, CESB, CEFB, NULL, NULL, {"Don't output any logs to the console.", NULL}},
	{-CETYPE_FLAG, {"kill", NULL}, {&dokill, 0}, CESB, CEFB, NULL, NULL, {"Send SIGTERM to all running instances of isproxy.", NULL}},

	{CETYPE_INT, {"port", "p"}, {&localPort, -1}, CESB, CEFB, NULL, NULL, {"Default port number.", NULL}},

	{CETYPE_FUNC, {"filepath", "f"}, CEIB, CESB, {fileAddPath, fileGetPath, fileClearPath}, NULL, (void *)&iippaths, {"File path to search for files", NULL}},
	{-CETYPE_STRING, {"configfile", "c"}, CEIB, {&configfilename, DEFAULT_CONFIGNAME}, CEFB, NULL, NULL, {"Where to find the configuration file.", NULL}},
	{CETYPE_STRING, {"randomseedfile", NULL}, CEIB, {&randomseedfilename , DEFAULT_RANDOMSEEDFILENAME}, CEFB, NULL, NULL, {"The file for the random seed.", NULL}},
	{CETYPE_STRING, {"nodefile", NULL}, CEIB, {&nodereffilename, DEFAULT_NODEREFFILENAME}, CEFB, NULL, NULL, {"The file to use for 'node.ref'.", NULL}},
	{CETYPE_STRING, {"listenfile", NULL}, CEIB, {&listenreffilename, DEFAULT_LISTENFILENAME}, CEFB, NULL, NULL, {"The file to use for 'listen.ref'.", NULL}},
	{CETYPE_STRING, {"logfile", "l"}, CEIB, {&logfilename, DEFAULT_LOGFILE}, CEFB, NULL, NULL, {"Log file", NULL}},
	{CETYPE_INT, {"loglevel", "v"}, {&verbosity, 2}, CESB, CEFB, NULL, NULL, {"Verbosity of the logfile \n(1-errors, 2-normal, 3-minor, 4-debug, 5-memory, 6-traffic, 7-'spam', 8-keys)", NULL}},

	BLANKCONFIGENTRY
};


int initfirst(void) {
	memInit();

	fileInit();

	protocolInit();
	protocolAddPipeList(ppelist);
	protocolAddPipeFaceList(pfpelist);
	csInit();
	csAddList(cspelist);

	configInit();
	configAddList(ceinit);
	configReset();

	return 0;
}

int initsecond(void) {
	configReadAll();

	saltInit();
	randomInit();

	return 0;
}


int init() {
	BigNum *privkey;
	BigNum *pubkey;
#ifndef _WINDOZE_
	int i;
#endif

	/* Make sure that seed.rnd exists and that there is enough entropy */
	if(randomentropyneeded != 0 && dorandom == 0 && doconfig == 0 &&
	   ignoreentropy == 0) {
		LOGERROR("IIP has insufficient random data to start with. Rerun isproxy with the -R switch on.");
		exit(1);
	}

	//randomInit() is called twice but done just in case another random seed is found/pointed to
	randomInit();

	doTests();

	if(showversion != 0) {
		LOGNONE(VERSIONTEXT);
		exit(0);
	}
	if(makeKeys != 0) {
		privkey = dhGeneratePrivKey(dhGetExponentLength(makeKeys));
		if(privkey == NULL) {
			LOGNONE(_("Bad keylength valid lengths: 1024, 1536, 2048, 3072, 4096, 6144, 8192"));
			exit(1);
		}
		pubkey = dhGeneratePubKey(makeKeys, privkey);
		
		LOGNONE(stringJoin("Private key:", bignumToHex(privkey)));
		LOGNONE(stringJoin("Public  key:", bignumToHex(pubkey)));
		exit(0);
	}
	SocketAtStart();

	//todo: display something for windows
	if(autogetnoderefs != 0) {
		LOGNORMAL("Retrieving node.ref");
		if(nodeservGetNodeRefs(refgetpage, refgethost, refgetport) != 0) {
			LOGERROR("Error when automatically retrieving node.ref");
		}
	}

	nodeservClearNodeRefs();
	nodeservReadNodeRefFile();
	nodeservClearListenRefs();
	nodeservReadListenRefFile();

	if(dorandom != 0) {
		/* ask the user for entropy */
		uiGetEntropy(_("IIP - Entropy needed"), N_("IIP has insufficient random data to start with."), 650);
	}
	if(doconfig != 0) {
		iipcsScreen();
#ifdef _UNIX_
		//Send SIGHUP to all running copies.
		//Exit if one 1 or more instance was successfully SIGHUPed
		if(unixSignalKin(SIGHUP, "/var/tmp/", "isproxy.lock.") != 0) {
			return 0;
		}
#endif
	}
	if(dokill != 0) {
#ifdef _UNIX_
		unixSignalKin(SIGTERM, "/var/tmp/", "isproxy.lock.");
		return 0;
#endif
	}
	if(quiet == 0) {
		logstring(LOG_NORMAL, stringAppend2(
				"Accepting IRC connections on port:", intToString(localPort)
			));
		logstring(LOG_NORMAL,stringAppend3(
				"Type /server localhost:", 
				intToString(localPort),
				" to connect, and then /join #anonymous"
			));
	}
	/* fork off into the background. */
#ifdef _UNIX_
	if(nodetach == 0) {
		if ((i = fork()) == -1) {
			perror("fork");
			return 20;
		}
		if (i > 0) {
			return 0;
		}
		setsid();
	}
	unixAddLockFile("/var/tmp/", "isproxy.lock.");
#endif

	// enter the main loop
	return !coreMainLoop() ? 0 : EXIT_LISTEN_ERROR;

}


int main(int argc, char **argv)
{ 
	int i;

	initfirst();

	uiSetFuncs(uidumbMake, uidDialog, uidGetEntropy);

	configSetCL(argc, argv);

	initsecond();

	if(showhelp != 0) {
		configShowHelp();
		return 0;
	}

	if(makeKeys == 0) {
		if(localPort == -1) {
			//localPort = getLocalPort();
			nodeservClearListenRefs();
			nodeservReadListenRefFile();
			iipcsScreen();
			doconfig = 0;
		}
		if(localPort == -1) {
			return 10;//todo: choose a better exit code
		}
	}
#ifdef _UNIX_
	signal(SIGHUP, unixSignalHUP);
#endif
	i = init();  
	return i;

}

void iipReload(void) {
	LOGNORMAL("iipReload: Reloading settings.");
	configReadAll();
	nodeservClearNodeRefs();
	nodeservReadNodeRefFile();
	nodeservClearListenRefs();
	nodeservReadListenRefFile();
	reloadsettings = 0;
	sockservStartListen();
}

