#include "cygwin.h"
#include "sGui.h"
#include "protocol.h"
#include "sServer.h"
#include "sCommand.h"
#include "sFile.h"
#include "donkey.h"
#include "cSocket.h"
#include "cServer_udp.h"
#include "kademlia.h"
#include "cSearchManager.h"
#include "opcodes.h"
#include "cHTTP.h"
#include "sPacket.h"
#include "sTag.h"
#include "sSource.h"

#ifdef DIPL_LOG
#include "CentralLog.h"
// object for gathering central logging data.
class ServerLogClass *ServerLog;
#endif

#define			 AKT_VERSION	(((0*256+0)*256+9)*256+0)

// listen client {{{
class cListen_client : public cSocket {
	public:
		cListen_client(unsigned short PORT) : cSocket(PORT, SOCK_STREAM) { }
virtual	void	Work(void);
virtual	int 	doRead_high(void) { printf("cListen_client::doRead\n"); return 0; }
	};
void cListen_client::Work(void) { // Only caled if fd_new != INVALID_SOCKET
	if (!IP_is_OK(peer_ip)) {	// Do not accept anything from bad IP's
		resetAccept();
		return;
		}
	if (tcp_server != NULL)		// Accept from server always
		if (tcp_server->peer_ip.s_addr == peer_ip.s_addr) {
			new sSource (this);
			return;
		}
	if(sSource::count > (TCP_CLIENT_COUNT * 11) / 10) {	// reject if we have to much connections
		resetAccept();
		return;
		}
	new sSource (this);
	}
class cListen_client *client_listen = NULL; 
void Client_listen (unsigned short port) {
	if (client_listen == NULL) client_listen = new cListen_client(port);
	if (!client_listen->validSocket()) {
		delete client_listen;
		client_listen = new cListen_client(port);
		}
	}

/*
  -----------------------------------------------------------------------------------
  this part handles incoming connections to the server part 
  (in cListen_server::Work()) and sets up the server (with 
  Server_listen)
*/


/// cDonkey server Part: class for listening for incoming connections to the server.
class cListen_server : public cSocket {
	public:
		cListen_server(unsigned short PORT) : cSocket(PORT, SOCK_STREAM) { }
virtual	void	Work(void);
	int 	doRead_high(void) { printf("cListen_server::doRead\n"); return 0; }
	};
/// Work() is called when a new client connects to the server. It creates a new 
/// sServer object for handling the connection
void cListen_server::Work(void) {	// Only called if fd_new != INVALID_SOCKET
        if (!IP_is_OK(peer_ip) || 
	   (	(TCP_SERVER_COUNT*13)/10 < sServer::count && 
		 peer_ip.s_addr!=tcp_server->peer_ip.s_addr)) {
		resetAccept();
		return;
		}
        new sServer (this, peer_ip, peer_port);
	}

class cListen_server *server_listen = NULL; 
/// Set up the cDonkey server socket listener.
void Server_listen (unsigned short port) {
#ifdef DIPL_LOG
	if (!ServerLog) ServerLog = new ServerLogClass();
#endif
	if (server_listen == NULL) server_listen = new cListen_server(port);
	if (!server_listen->validSocket()) {
		delete server_listen;
		server_listen = new cListen_server(port);
		}
	}


/* 
   -----------------------------------------------------------------------------------
*/

// }}}	
// listen gui {{{
class cListen_gui : public cSocket {
	public:
		cListen_gui(unsigned short PORT) : cSocket(PORT, SOCK_STREAM) { }
virtual void    Work(void);
	int 	doRead_high(void) { printf("cListen_gui   ::doRead\n"); return 0; }
	};
void cListen_gui::Work(void) {		// Only caled if fd_new != INVALID_SOCKET
	if (connected != 2) return;
	log(1, "donkey.cpp: Establishing Gui connection...\n");
        new sGui (this, peer_ip);
	}

class cListen_gui *gui_listen = NULL; 
void Gui_listen (unsigned short port) {
	if (gui_listen == NULL) gui_listen = new cListen_gui(port);
	if (!gui_listen->validSocket()) {
		delete gui_listen;
		gui_listen = new cListen_gui(port);
		}
	}
// }}}
// listen command {{{
class cListen_command : public cSocket {
	public:
		cListen_command(unsigned short PORT) : cSocket(PORT, SOCK_STREAM) { }
virtual void    Work(void);
	int 	doRead_high(void) { printf("cListen_command:doRead\n"); return 0;}
	};
void cListen_command::Work(void) {	// Only caled if fd_new != INVALID_SOCKET
	if (connected != 2) return;
        new sCommand (this, peer_ip, peer_port);
	}

class cListen_command *command_listen = NULL; 
void Command_listen (unsigned short port) {
	if (command_listen == NULL) command_listen = new cListen_command(port);
	if (!command_listen->validSocket()) {
		delete command_listen;
		command_listen = new cListen_command(port);
		}
	}
// }}}
// {{{ UDP_Server listen
// didn't check whether the other listeners would require constructs like
// this, too -hl
class cServer_udp *userver_listen = NULL; 
void uServer_destructed(cSocket *sock) {{{
	log(1, "DEBUG: userver callback called from %p!\n", sock);
	if (sock == userver_listen)
		userver_listen = NULL;
	else 
		log(1, "WARN: userver callback called from wrong object (%p).\n", sock);
	}}}
void UServer_listen (unsigned short port) {
	if (userver_listen == NULL) {
	  userver_listen = new cServer_udp(port);
	  userver_listen->setDestructorCallback(uServer_destructed);
	}
	if (!userver_listen->validSocket()) {
		delete userver_listen;
		userver_listen = NULL;
		}
        }
// }}}
// {{{ UUKademlia_listen
extern class cKademlia 	*kademlia; // new cKademlia(kademlia_port);
void UKademlia_listen (unsigned short port) {
	if (kademlia == NULL) kademlia = new cKademlia(port);
	if (!kademlia->validSocket())  {
		delete kademlia;
		kademlia = NULL;
		return;
		}
	}
// }}}
// listen http {{{
class cListen_http : public cSocket {
	public:
		cListen_http (unsigned short PORT) : cSocket(PORT, SOCK_STREAM) { }
virtual	void	Work(void);
virtual	int 	doRead_high(void) { printf("cListen_httpt::doRead\n"); return 0; }
	};
void cListen_http::Work(void) { // Only caled if fd_new != INVALID_SOCKET
        new cHTTP (this, peer_ip, peer_port);
	}
class cListen_http *listen_http = NULL; 
void HTTP_listen (unsigned short port) {
	if (listen_http == NULL) listen_http = new cListen_http (port);
	if (!listen_http->validSocket()) {
		delete listen_http;
		listen_http = new cListen_http (port);
		}
	}
// }}}	

// Please do not change this values {{{
const int 		 UDP_SEARCH_TIMEOUT	=    5;
const int 		 UDP_SEARCH_COUNT	=   15;
unsigned int 		 CLIENT_COUNT   	=  140;
size_t			 TCP_CLIENT_COUNT   	= 4000;
unsigned int 		 TCP_SERVER_COUNT   	=  200;
bool 			 running		= true ;
bool			 eMule			= true ;
bool			 use_kademlia		= true ;	// please do not aktivate, i try to clear with Jed
#ifdef DIPL_LOG
bool			 run_server		= true;         // if you JUST WANT TO HAVE A RUNNING SERVER, *please*
bool			 res_server_sources	= true;         // DO NOT ENABLE LOGGING!! Changing the other values
#else                                                           // is sufficient! Really :-)
bool			 run_server		= false;        // <- change this
bool			 res_server_sources	= false;        // <- and this
#endif
bool			 isTerm			= false;
// }}}

void log (unsigned level, const char *msg, ...) {{{
	if (0 == (pref.verbose & level)) return;
	va_list ap;
	va_start(ap, msg);
	vprintf(msg, ap);
	va_end  (ap);
	}}}

struct sPref	pref;
char ZERO[16];

#define			PORT_BASE	4660
static void setDefaults_pref (void)  {{{
	pref.ports.server 	= PORT_BASE + 1;
	pref.ports.client 	= PORT_BASE + 2;
	pref.ports.gui	  	= PORT_BASE + 3;
	pref.ports.clc	  	= PORT_BASE + 4;
	pref.ports.emule  	= PORT_BASE + 5;
	pref.ports.kademlia	= PORT_BASE + 6;
	pref.ports.web		= PORT_BASE + 7;
	pref.verbose 	=   0;
	pref.ip_trash 	= 128;
	}}}

unsigned		 my_version     	= 1044;
long  			 server_hopping   	= 1200;
long  			 server_connect		= 5;
double			 akt_rate_weight	= 0.25;		// 1 == akt_rate_weight + akt_rate_weight^2 + akt_rate_weight^3
int			 protocol_logLevel  	= 9;
tHash			 client_hash		= "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
bool			 tcp_search		= false;
int			 udp_search		= 0;
time_t			 currentTime		= 0;
char			 currentTime_str	[32];
class sServer		*tcp_server		= NULL;
class cHTTP		*server_http		= NULL;
bool			go_to_background	= false;
// onExit free(*)
// {{{ String Parameter from config file
char 			*control   		= NULL;
char 			*incoming 		= NULL;
char 			*completed 		= NULL;
char    		*such_file		= NULL;
char 			*ctrl_user		= NULL;
char 			*bind_ip		= NULL;	// should we bind to specific adress ?
char 			*ctrl_pass		= NULL;
char 			*client_name		= NULL;
char			*db_client		= NULL;
char			*db_server		= NULL;
// }}}

static void CurrentTime (void) {{{
	currentTime = time (NULL);
//	strftime(currentTime_str, 31, "%Y.%m.%d %H:%M.%S", gmtime(&currentTime));
	strftime(currentTime_str, 31, "%H:%M.%S", gmtime(&currentTime));
	}}}

// Windows Only Startup Code
#ifdef _WIN32
// Windows Ctrl-C handler
BOOL WINAPI ConsoleHandler(DWORD event) {{{
	switch(event) {
	case CTRL_C_EVENT:
	case CTRL_BREAK_EVENT:
	case CTRL_CLOSE_EVENT:
	case CTRL_LOGOFF_EVENT:
	case CTRL_SHUTDOWN_EVENT:
		running = false;
		break;
	default:
		printf("donkey.cpp: ConsoleHandler( %X ): Unknown Console Event.\n",event);
		}
	return TRUE;
	}}}

class CApp {
public:
	CApp();
	} g_App;

CApp::CApp() {{{
	if( ::SetConsoleCtrlHandler((PHANDLER_ROUTINE)ConsoleHandler,TRUE) == FALSE ) {	// unable to install handler... 
		printf("SetConsoleCtrlHandler failed: Unable to install handler!\n");
		}
	}}}
#endif // Windows

#ifdef HAVE_ATEXIT
void exitHandler(void)
#else
#ifdef	HAVE_ON_EXIT
void exitHandler(int sig, void*)
#else
void exitHandler(void)
#endif
#endif
	{{{
	running = false;
	printf("Exiting cDonkey...\n");
	manager.stopAllSearches();
	while (sFile::first_file != NULL) delete sFile::first_file;
	if (control     != NULL) Free2 (control);
	if (incoming    != NULL) Free2 (incoming);
	if (completed   != NULL) Free2 (completed);
	if (such_file   != NULL) Free2 (such_file);
	if (ctrl_user   != NULL) Free2 (ctrl_user);
	if (bind_ip     != NULL) Free2 (bind_ip);
	if (ctrl_pass   != NULL) Free2 (ctrl_pass);
	if (client_name != NULL) Free2 (client_name);
	if (db_client   != NULL) Free2 (db_client);
	if (db_server   != NULL) Free2 (db_server);
	close_Sources ();
	close_Servers ();
	ende_IP_list  ();
	cSocket::Stop ();
	printf("sPacket::count => %i\n", sPacket::count);
	printf("sSocket::count => %i\n", sSocket::count);
	printf("sSource::count => %i\n", sSource::count);
	printf("sSource::count0=> %i\n", sSource::count0);
	printf("sSource::count1=> %i\n", sSource::count1);
	printf("cSocket::count => %i\n", cSocket::count);
	printf("sTag   ::count => %i\n", sTag::count);
	}}}

void sigTerm (int) {{{
	printf("Signal TERM\n");
	running = false; 
	}}}
void sigInt  (int) {{{
	printf("Signal INT\n");
	exit(0);
	running = false; 
	}}}
void sigHup  (int) {{{
	printf("Signal HUP\n");
	running = false; 
	}}}

static int  sFile_createParthash (const char *NAME, const char *dir,tHash *hash) {{{
	int ret = 0;
	// static_cast
	// reinterpret_cast	char* = <reinterpret_cast char*>
	// const_cast
	char *name = reinterpret_cast<char*>(alloca (strlen (dir) +  1 + strlen (NAME) + 1));
	sprintf(name, "%s%c%s", dir, dir_delim, NAME);
	size_t blocks = 0;
	int rest = 0;
	int  i = 0;
	int    fd     = FD_open (name, O_RDONLY|O_BINARY);
	if (fd == -1) return ret;
	int  size = FD_LSEEK (fd, 0, SEEK_END);
	FD_LSEEK (fd, 0, SEEK_SET);
	blocks = (size + PARTSIZE - 1) / PARTSIZE;
	unsigned char *hash_list = reinterpret_cast<unsigned char*>(alloca (16 * blocks));
	unsigned char *buf = reinterpret_cast<unsigned char*>(MALLOC (PARTSIZE));
	rest = size;
	while (rest > 0) {{{ // build Part hashlist
		int work = (rest<PARTSIZE)?rest:PARTSIZE;
		if (!FD_READ2 (fd, buf, work)) assert(0);
		memcpy (hash_list + (i * 16), md4 (reinterpret_cast<char*>(buf), work), 16);
		rest -= work;
		i++;
		printf("Part-Hash created\n");
		}}}
	if (buf) { Free2 (buf); }
	if (blocks > 1)  memcpy ( hash, md4(reinterpret_cast<char*>(hash_list), blocks * 16), 16 );
	else		 memcpy ( hash,     hash_list              , 16);
	char *buf1 = reinterpret_cast<char*>(alloca (strlen(control) + 1 + 32 + 5 +1));
	sprintf(buf1, "%s%c%32s.hash", control, dir_delim, hash_bin2hex(*hash) );
	FD_close (fd);
	fd = FD_create (reinterpret_cast<char*>(buf1), O_WRONLY|O_CREAT|O_BINARY , 0600);
	if (fd > 0) {
		FD_WRITE2 (fd, hash_list, blocks * 16);
		ret = size;
		}
	if (fd > 0) FD_close (fd);
	return ret;
	}}}
// Config File Handling {{{
struct sFileConfig {
	const char *name;
	unsigned  type;
	void	   *var;
	};
#define unsigned_short_int	1
#define unsigned_int		2
#define char_star   		3
#define size_type		4
#define boolean_value           5
struct sFileConfig listConfig[] = {
	{"server_port"		, unsigned_short_int	, &pref.ports.server  	},
	{"client_port"		, unsigned_short_int	, &pref.ports.client	},
	{"kademlia_port"	, unsigned_short_int	, &pref.ports.kademlia	},
	{"gui_port"		, unsigned_short_int	, &pref.ports.gui	},
	{"cmd_port"		, unsigned_short_int	, &pref.ports.clc	},
	{"my_version"		, unsigned_int		, &my_version		},
	{"verbose"   		, unsigned_int		, &pref.verbose		},
	{"protocol_logLevel"   	, unsigned_int		, &protocol_logLevel	},
	{"tcp_server_count"   	, unsigned_int		, &TCP_SERVER_COUNT	},
	{"tcp_client_count"   	, size_type   		, &TCP_CLIENT_COUNT	},
	{"upload_kb"   		, unsigned_int		, &ulSecond		},
	{"download_kb"   	, unsigned_int		, &dlSecond		},
	{"client_name"		, char_star		, &client_name		},
	{"control"		, char_star		, &control		},
	{"incoming"		, char_star		, &incoming		},
	{"completed"		, char_star		, &completed		},
	{"ctrl_user"		, char_star		, &ctrl_user		},
	{"ctrl_pass"		, char_star		, &ctrl_pass		},
	{"bind_ip"		, char_star		, &bind_ip		},
        {"enable_overnet"	, boolean_value		, &use_kademlia         },
	{"background"		, boolean_value		, &go_to_background	},
	{NULL			, 0			, NULL			}
	};

static void readConfig(char * p_sFilename) { /// Read the cDonkey config file
	FILE * piCfgFile = NULL;
        char sCmd  [128];
        char sValue[128];
        int i = 0;
	// Try to open the file
	piCfgFile = fopen(p_sFilename, "r");

	// Seems like there is a problem
	if (piCfgFile == NULL) {
		// Inform the user
		printf("Cannot read configuration file %s!\n", p_sFilename);
		printf("The file does not exist or it has the wrong permissions set.\n");
		// Try to create the file
		printf("Trying to create a new configuration file...\n");
		piCfgFile = fopen (p_sFilename, "w+");
		// Seems to be a permission problem
		if (piCfgFile == NULL) {
			printf("Couldn't create config file %s!\n", p_sFilename);
			printf("Check for wrong file or directory permissions.\n");
			#ifndef HAVE_ATEXIT
			#ifndef HAVE_ON_EXIT
				exitHandler ();
			#endif
			#endif
			exit(EXIT_FAILURE);
			}
		// Print out the client name as well as the random username/password combination
		fprintf(piCfgFile, "client_name = cDonkey_(http://cdonkey.suche.org)\n");
		fprintf(piCfgFile, "ctrl_user = %s\n", ctrl_user);
		fprintf(piCfgFile, "ctrl_pass = %s\n", ctrl_pass);
		// Set the file to the right permissions
		chmod(p_sFilename, 0600);
		// Jump to the beginning of the file
		fseek (piCfgFile, 0, SEEK_SET);
		printf("Configuration file %s created.\n", p_sFilename);
	}

	printf("Processing configuration file %s...\n", p_sFilename);
	
	// Analyze each line in the file
	while (fscanf(piCfgFile, "%128s = %128s", sCmd, sValue) == 2) {
		log (1, "Config: %s = %s\n", sCmd, sValue);

		// Try to find a matching setting for the current line
		for (i = 0; listConfig[i].name != NULL; i++) {

			// If a setting matches, set it to the value of the configuration file
			if (strcmp(sCmd, listConfig[i].name) == 0) {
			  int tmpValue;
				switch (listConfig[i].type)
				{
				case unsigned_short_int	: *(reinterpret_cast<unsigned short int*>(listConfig[i].var)) = atoi(sValue); break;
				case unsigned_int      	: *(reinterpret_cast<size_t            *>(listConfig[i].var)) = atoi(sValue); break;
				case size_type      	: *(reinterpret_cast<unsigned       int*>(listConfig[i].var)) = atoi(sValue); break;
				case char_star		: Free2 (*(reinterpret_cast<char**>(listConfig[i].var)));
							  *(reinterpret_cast<char**>(listConfig[i].var)) = STRDUP (sValue);
							  break;
				case boolean_value      : tmpValue = atoi(sValue);
				                          if (tmpValue == 0) {
				                                  *(reinterpret_cast<bool *>(listConfig[i].var)) = false;
				                                  break;
				                          }
				                          else if (tmpValue == 1) {
				                                  *(reinterpret_cast<bool *>(listConfig[i].var)) = true;
				                                  break;
				                          }
				                          else {
				                                  printf("An error occured while processing the configuration file: invalid boolean value!\n");
                                                                  #ifndef HAVE_ATEXIT
                                                                  #ifndef HAVE_ON_EXIT
				                                        exitHandler ();
                                                                  #endif
                                                                  #endif
				    
							          exit(EXIT_FAILURE);
				                          }
				default			: printf("An error occured while processing the configuration file!\n");
							  #ifndef HAVE_ATEXIT
							  #ifndef HAVE_ON_EXIT
								exitHandler ();
							  #endif
							  #endif

							  exit(EXIT_FAILURE);
				}

				// Output control information, but we don't want passwords in logfiles	
				if (strcmp(sCmd, "ctrl_pass") == 0)
					printf("  >> Setting \"%s\" is now set to some password. :)\n", sCmd);
				else
					printf("  >> Setting \"%s\" is now set to \"%s\".\n", sCmd, sValue);
				
				// Stop searching
				break;
			}
		}

		// Print out a message if we couldn't find a setting of that name
		if (listConfig[i].name != NULL) continue;
		if (strcmp(sCmd, "") == 0)      continue;
		printf("  !! Unknown setting \"%s\" in configuration file!\n", sCmd);
	}

	printf("Processed configuration file.\n");

	// Close the configuration file
	fclose(piCfgFile);
}
// }}}

typedef void (*file_load)(const char *name, const char *dir_ctl, const char *dir_dat);
static void sFile_part_met(const char *name, const char *dir, const char *dir_dat) {{{
	char dateiname[512];
	snprintf(dateiname, 512, "%s%c%s", dir, dir_delim, name);
	class sFile *file1 = new sFile(dateiname, dir_dat);
	class sFile *check = forHash(file1->hash);
	if (file1->size == 0  || file1 != check || file1->fd_in == -1) 
		delete file1;
	}}}
static void loadDir (const char *dir, const char* dir_dat,const char *postfix_val, file_load load_func) {{{
	struct dirent *entry;
	DIR *dir_hdl = opendir (dir);
	if (dir_hdl == NULL) return;
	size_t     postfix_len  = strlen(postfix_val);
	while (NULL != (entry = readdir (dir_hdl)) && running) {
		if (entry->d_name == NULL) continue;
		size_t name_len = strlen (entry->d_name);
		if (name_len < postfix_len) continue;
		if (0 != strcmp (entry->d_name + name_len - postfix_len, postfix_val)) continue;
		load_func (entry->d_name, dir, dir_dat);
		}
	closedir (dir_hdl);
	}}}

static int  Initialise    (void) {{{
	struct dirent *entry;
	DIR *dir;
	memcpy (client_hash, md4(client_name, strlen(client_name)), 16);
	if (eMule) {{{
		client_hash[ 5] = 14;   // 0x0E
		client_hash[14] =111;	// 0x6F Aktual eMule signature !!!
		}}}
	mkdir (incoming, 0700);
	mkdir (completed, 0700);
	mkdir (control , 0700);
	loadDir (control, completed, ".part.met", sFile_part_met);
	loadDir (control, incoming , ".part.met", sFile_part_met);
	if ( NULL == (dir = opendir (completed)) || !running) return 0;
	while (NULL != (entry = readdir (dir)) && running ) {{{
		if (0 == strcmp (entry->d_name, "." )) continue;
		if (0 == strcmp (entry->d_name, "..")) continue;
		bool alt = false;
		FILE_LIST
			if (alt || 0 == strcmp(akt->Name(), entry->d_name)) {
				alt = true;
				break;
				}
		if (alt) continue;
		tHash hash;
		int size = sFile_createParthash(entry->d_name, completed, &hash);
		if (size == 0) continue;
		printf("\nAdding shared file %s (%i) %32s\n", entry->d_name, size, hash_bin2hex(hash));
		class sFile *neu = new sFile (hash, entry->d_name, size, completed);
		neu->set_Completed() ;
		}}}
	closedir (dir);
	return 0;
	}}}

// Search
const class sFile *nextFile_search (void) {{{
	sFile *ret = NULL;
	unsigned int min = 999999;
	FILE_LIST {
		if (ALL_DONE == akt->status || akt->Pause() ) continue;
		nextFile (akt->hash, true);
		int src_count = cursor_count;
		if (ret == NULL) {
			ret = akt;
			min = src_count;
			continue;
			}
		if (ret->search_count + (min / 100) > akt->search_count) {
			min = src_count;
			ret = akt;
			}
		}
	if (ret == NULL) {
		log (1, "No search\n");
		return ret;
		}
	ret->search_count ++;
	return ret;
	}}}
static void        fetchSource_UDP(void) {{{ // aktivate UDP search
static  time_t	timeout = 0;
	if (sFile::count <= 0) return;
	if (timeout > currentTime) return;
	timeout = currentTime + UDP_SEARCH_TIMEOUT;
	for (int i = UDP_SEARCH_COUNT; --i >= 0;) {
		if (ul_left < 100 || dl_left < 100) break;
		in_addr ip;
		uint16_t port;
		if (!nextServer (&ip, &port)) break;
		if (tcp_server != NULL)
			if (tcp_server->peer_ip.s_addr == ip.s_addr) continue;
		if (port == 0) continue;
		const sFile *file1 = NULL;
		do {
			file1 = nextFile_search();
			if (file1 == NULL) break;
			} while (file1->Pause() || file1->status == ALL_DONE);
		if (file1 == NULL) break;
		userver_listen->UDP_req_sources (ip, port, file1->hash);
		}
	}}}
static void        nextSource     (void) {{{ // Holt eine verfügbare Quelle oder sucht nach Ihr
	struct in_addr ip;
	ip.s_addr = 0;
	unsigned short port = 0;
	if (!getSource (&ip, &port)) {		// the timeout save unwanted db access here
		if (userver_listen!=NULL) fetchSource_UDP ();
		if (kademlia != NULL) {
			if (kademlia->status == 8) {
				const sFile *file1 = nextFile_search();
				if (file1 != NULL) 
					if (!cSearchManager::alreadySearchingFor(file1->hash))
						kademlia->Search(file1->hash);
				}
			}
		return;
		}
	struct eSources *info = Source_ip(ip);
	if (info != NULL && userver_listen != NULL) {
		if (info->version &  0x01000000 && info->udp_port != 0) {
			sFile *dl_file = forHash(info->hash_dl);
			if (dl_file != NULL) {
				printf("UDP file reask send\n");
				userver_listen->req_file_ping (ip, info->udp_port, info->hash_dl);
				setSources_version (ip, info->version & 0xFEFFFFFF);
				}
//			FILE_LIST {
//				if (File_info_get(info->hash,akt->hash,akt->size, NULL)) {
//					printf("send file ping %s:%u\n", inet_ntoa(ip), info->udp_port);
//					userver_listen->req_file_ping (ip, info->udp_port, akt->hash);
//					setSources_version (ip, info->version & 0xFEFFFFFF);
//					}
				}
		}
	else 	new sSource (ip, port);
	}}}

static const char *print_Human(size_t bytes) {{{
static  char buf[64];
	double b = bytes;
	if (b<1024.0) { sprintf(buf,"(%10u) %8.3f B ", bytes, b); return buf; } b = b / 1024.0;
	if (b<1024.0) { sprintf(buf,"(%10u) %8.3f KB", bytes, b); return buf; } b = b / 1024.0;
	if (b<1024.0) { sprintf(buf,"(%10u) %8.3f MB", bytes, b); return buf; } b = b / 1024.0;
	if (b<1024.0) { sprintf(buf,"(%10u) %8.3f GB", bytes, b); return buf; } b = b / 1024.0;
	if (b<1024.0) { sprintf(buf,"(%10u) %8.3f TB", bytes, b); return buf; }
	sprintf(buf, "(%10u)",bytes);
	return buf;
	}}}
static void printfTraffic(void) {{{
	unsigned long dl_daten = 0;
	unsigned long ul_daten = 0;
	FILE_LIST {
		dl_daten += akt->dlCount();
		ul_daten += akt->ulCount();
		}
	printf("DL/UL abs %s/"		, print_Human(global_dl_count));
	printf(   "%s dat "   		, print_Human(global_ul_count));
	printf("%s/"			, print_Human(dl_daten));
	printf("%s left %7i/%7i\n"	, print_Human(ul_daten), dl_left, ul_left);
	}}}
static void info_dump(void) {{{
	log (32, "TIMESTAMP %s\n", currentTime_str);
	if(currentTime % 60 == 0) printfTraffic ();
		
	}}}

#ifdef DIPL_LOG
/// regular log data is written here.
static void log_actions() {
	if (currentTime % 300 != 0) return;
	ServerLog->write_event_regular();
	}
#endif

static void check_lib (void) {{{
#if 	defined(_MSC_VER)
const	char	compiler[] = "MS-CPP";
#elif	defined(__CYGWIN__)
const	char	compiler[] = "Cygwin";
#else
const	char	compiler[] = "Gcc";
#endif

	printf("Author : thomas@suche.org\n");
	printf("License: GPL 2.0 (with modifications, MD5: 36B241914312DF86A9B68D56682CA44B)\n");
	printf("Build  : %s %s with %s\n", __DATE__, __TIME__, compiler);
#ifdef DIPL_LOG
	printf("Notice : logging extensions are *enabled* in this build!\n");
#endif
	cSocket::Start ();
	int   major   = 0;
	int   minor   = 0;
	int   patch   = 0;
	char *version = db_version (&major, &minor, &patch);
	if ((major != 4 && (minor != 0 || minor != 1)) && (major != 3 && minor != 3)) {{{
		printf ("Wrong Version of Berkley DB\n");
		printf ("Used Version => %s\n", version);
		printf ("DB source can found at http://www.sleepycat.com/\n");
		exit(0);
		}}}
	if (major != DB_VERSION_MAJOR || minor != DB_VERSION_MINOR) {{{
		printf ("Wrong Version of Berkley DB\n");
		printf ("Compiled Version is %i.%i.%i\n", DB_VERSION_MAJOR, DB_VERSION_MINOR, DB_VERSION_PATCH);
		printf ("Linked   Version is %i.%i.%i\n", major, minor, patch);
		printf ("ERROR: Major and/or minor Number changed. Please recompile cDonkey!\n");
		exit (0);
		}}}
	if (0 != strcmp(zlibVersion(), ZLIB_VERSION)) {{{
		printf ("WARNING zlib version changed since last compile.\n");
		printf ("Compiled Version is %s\n", ZLIB_VERSION);
		printf ("Linked   Version is %s\n", zlibVersion());
		}}}
	int z0 = atoi (ZLIB_VERSION);
	int z1 = atoi (zlibVersion());
	if (z0 != z1) {{{
		printf ("ERROR: zlib is not compatible!\n");
		printf ("zlib source can found at http://www.gzip.org/zlib/\n");
		exit (0);
		}}}
#ifndef FASTHASH	
	bool uptodate = true;
	struct in_addr IP;
	IP.s_addr = htonl(AKT_VERSION);
	struct in_addr va;
	va.s_addr = IP4Name("cDonkey-Version.suche.org");
	if (va.s_addr != 0) {{{
		if (htonl(va.s_addr) <= AKT_VERSION) printf("Version: Your cDonkey is up to date.\n");
		else {
			printf ("Version: There is a newer version of cDonkey!\n");
			printf ("- Current: %s\n", inet_ntoa(IP));
			printf ("- Newest : %s\n", inet_ntoa(va));
			printf ("- Update : http://cDonkey.suche.org\n");
			uptodate = false;
			}
		}}}
	va.s_addr = IP4Name("cDonkey-Expired.suche.org");
	if (va.s_addr != 0) {{{
		if (htonl(va.s_addr) >= AKT_VERSION) {
			printf ("ERROR: This cDonkey version is disabled.\n");
			printf ("Please get a new version of cDonkey!\n");
			printf ("- Current : %s\n", inet_ntoa(IP));
			printf ("- Expired : %s\n", inet_ntoa(va));
			printf ("- Update  : http://cDonkey.suche.org\n");
			printf ("- Complain: mailto:thomas@suche.org\n");
			exit(0);
			}
		}}}
#endif
	}}}

static void check_ServerCount(void) {{{
	if (tcp_server != NULL) return;
	if (client_listen == NULL) return;
	// always stay connected (even if line is full)
	// Make sure we have an TCP_Server connection
	if (!use_kademlia)
		GUI_LIST akt->GUI_disconnected();	// Make sure we have an TCP_Server connection
	struct in_addr  ip;
	uint16_t        port;
	char logbuf[256];
	if (!nextServer (&ip, &port)) {
		log (1, "No nextServer() found !\n");
		return;
		}
	tcp_server = new sServer (ip, port);
	snprintf(logbuf,255, "Trying Edonkey Server: %15s\n", inet_ntoa(ip) );
	log (1, logbuf);
	GUI_LIST akt->GUI_statusMessage(logbuf);
	}}}
static void check_ClientCount(void) {{{
static time_t log_time = currentTime; // only needed to not flood logging output
	if (sSource::count >= TCP_CLIENT_COUNT) return;
	if (ul_left <= 1024) return;
	if (dl_left <= 1024) return;
	// Make sure we have enaught clients Open (only if we have the bandwith for them)
	int ftp_aktive = 0;
	bool hit = false ;
	SOURCE_LIST {{{				// remove current dl sources
		if (akt->Status() == FTP_AKTIVE) ftp_aktive++;
		if ( 0 == (pref.verbose & 1)) continue;
		// debugging output . Change to other verbose lvl if too slow!
		if ( currentTime - log_time > 40 )  { // every 40sec
			hit = true;
			bool slot_info = (akt->Status() == WAIT_SLOT || (akt->dlSlot() != -1 && akt->Status() == P_C5060));
			printf("%s Stats: IP %15s ",(slot_info)?"Queue     ":"Downloader",inet_ntoa(akt->peer_ip));
        		printf("Download %s ", print_Human(akt->dl_count));
        		printf("Upload %s "  , print_Human(akt->ul_count)) ;
        		printf("Ratio %li Rank %hi eMule: %d Overnet: %d\n", akt->Ratio(), akt->dlSlot(),
				akt->is_eMule(), akt->is_Overnet() );
			}
		}}}
	if ( hit == true ) { 
		log_time = currentTime ; 
		printf(" Stats ---------\n"); 
		}
	int con_new = (CLIENT_COUNT - ftp_aktive) * 5;
	log (16, "FTP_AKTIVE %3i NEUE_CON %3i\n", ftp_aktive, con_new);
	if (con_new <=   0) return;
	int max1 = TCP_CLIENT_COUNT - sSource::count;
	if (max1 > 0) {
		if (max1 < con_new) con_new = max1;
		if (con_new> 20) con_new = 20;
		while (con_new-- > 0) nextSource (); // get an constant Amount of Source
		}
	}}}
void every_sec (void) {{{	///< this function is called every once a second is gone
static time_t next_bind =0;
static time_t last_run = 0;
	CurrentTime ();
	time_t diff = currentTime - last_run;
	if (diff == 0) return;
	last_run = currentTime;
	if (diff <= 0 || diff > 3)  diff = 1;
	if (!next_bind + 3 < currentTime) {{{ // only as long as not all port are bound
		pref.ports.emule = pref.ports.server + 4;
		if (client_listen       == NULL)      Client_listen 	(pref.ports.client  );
		if (gui_listen          == NULL)      Gui_listen    	(pref.ports.gui     );
		if (command_listen      == NULL)      Command_listen  	(pref.ports.clc     );
		if (userver_listen      == NULL)      UServer_listen  	(pref.ports.emule   );
		if (listen_http         == NULL)      HTTP_listen 	(pref.ports.web     );
		if (use_kademlia && kademlia == NULL) UKademlia_listen	(pref.ports.kademlia);
		if (run_server &&server_listen==NULL) Server_listen	(pref.ports.server  );
		next_bind = currentTime;
		}}}
	info_dump();
#ifdef DIPL_LOG
	log_actions();
#endif       
	if (userver_listen!= NULL && userver_listen->validSocket()) {
		for (int j=3; --j > 0 && ul_left > 1024 && dl_left > 1024;) {
			userver_listen->UDP_next_ping ();
			}
		}
	size_t ul_max = (ul_left + ulSecond * diff);
	size_t dl_max = (ul_left + ulSecond * diff);
	ul_left = ((ulSecond<ul_max)?ulSecond:ul_max); // <-- Blockiert die leitung für Ack Packete :-(
	dl_left = ((dlSecond<dl_max)?dlSecond:dl_max); // <-- Kann nur durchgepackte packete mehr werden
	{ FILE_LIST akt->calcRate (diff); }
	fflush(stdout);
	int work_count = 0;
static unsigned long count_a = 0;
static unsigned long count_b = 0;
	count_a ++;
	class cSocket *akt_sock = NULL;
	while (NULL != (akt_sock = timer_list.peek())) {{{
		cDLL<class cSocket>::remove(&akt_sock->timer);
		akt_sock->timer = timer_list.add_back(akt_sock);
		if (akt_sock->work_last + 1 >= currentTime) break;
		akt_sock->work_last = currentTime + 1;
		count_b++;
		if (16 == (16 & akt_sock->mask)) {{{ // should not realy happen
		        printf("donkey.cpp: deleting1 %p\n", akt_sock);
			delete akt_sock;
			continue;
			}}}
		if (!akt_sock->connected &&  currentTime - akt_sock->since > 66) {{{ // destroy after connect timeout
		        printf("donkey.cpp: deleting2 %p\n", akt_sock);
			delete akt_sock;
			continue;
			}}}
		if (akt_sock->Type == tServer) {{{ // Server Timeout and hopping
			if (!akt_sock->connected && currentTime - akt_sock->since > server_connect) {{{ // destroy
				log (2, "1. delete server timeout %15s \n", inet_ntoa (akt_sock->peer_ip));
				printf("donkey.cpp: deleting3 %p\n", akt_sock);
				delete akt_sock;
				continue;
				}}}
			if (akt_sock->connected && currentTime - akt_sock->last_conn > server_hopping) { // destroy
				log (2, "2. delete server timeout (hopping) %15s \n", inet_ntoa (akt_sock->peer_ip));
				printf("donkey.cpp: deleting4 %p\n", akt_sock);
				delete akt_sock;
				GUI_LIST akt->GUI_statusMessage("Hopping to new Server\n");
				continue;
				}
			}}}
		if (akt_sock->connected != 1 && akt_sock->connected != 3) continue;
		work_count ++;
		akt_sock->addEvent (9);
		}}}
//	printf("Counter %lu / %lu (%lu)\n", count_a, count_b, count_b/count_a);
	cSocket::hdlEvent ();
	check_ServerCount ();
	check_ClientCount ();
	}}}

void fork_to_background(void); // located below main

int main (int argc, char **argv) {{{
	bzero (ZERO, 16);
	mtrace ();	// MALLOC_TRACE
	struct stat dStatBuf;
	int j = 0;

	setDefaults_pref ();
	// 1.100.292.227 => sec bei 1.1 GHZ
	printf("WARNING: server.db / files.db / source.db files were changed\n");
	CurrentTime ();
	ASSERT (sizeof (float   ) ==  4);
	ASSERT (sizeof (tHash   ) == 16);
	ASSERT (sizeof (uint32_t) ==  4);
	ASSERT (sizeof (uint16_t) ==  2);
	ASSERT (sizeof (uint8_t ) ==  1);
	// {{{ Init Block
	srand (currentTime);
#if !defined(WIN32) && !defined(__CYGWIN__)
	struct winsize wsz;
	isTerm = (ioctl (fileno (stdout), TIOCGWINSZ, &wsz) >= 0);
#endif
#ifdef HAVE_ATEXIT
	atexit(exitHandler);
#else
#ifdef	HAVE_ON_EXIT
	on_exit (exitHandler, NULL);
#endif
#endif
	signal  (SIGTERM, sigTerm);
#ifdef HAVE_SIGHUP
	signal  (SIGHUP , sigHup );
#endif	
#ifdef HAVE_SIGINT
	signal  (SIGINT , sigInt );
#endif	
	// Generate a random client name and password {{{
	char sClientRandom[] = "01234567";
	char sPasswdRandom[] = "0123456789";
	for (j = 0; j < 8; j++)  sClientRandom[j] = ('A' + rand() % 24);
	for (j = 0; j < 10; j++) sPasswdRandom[j] = ('A' + rand() % 24);
	if (client_name == NULL) client_name = STRDUP (sClientRandom);
	if (ctrl_pass   == NULL) ctrl_pass   = STRDUP (sPasswdRandom);
	// }}}
	// Set important settings to default values
	if (control     == NULL) control     = STRDUP ("control");
	if (incoming    == NULL) incoming    = STRDUP ("incoming");
	if (completed   == NULL) completed   = STRDUP ("completed");
	if (ctrl_user   == NULL) ctrl_user   = STRDUP ("cdonkey");
	if (bind_ip     == NULL) bind_ip     = STRDUP ("0.0.0.0");
	// If there is a valid config file in the users home directory, use it instead of the global one
	const char * sHomeDir = getenv("HOME");
	char * sConfigFile = NULL;
	if (sHomeDir == NULL || sHomeDir == "" || sHomeDir == "/") {
		// Something is horrible wrong with users home directory, use the global config file
		sConfigFile = reinterpret_cast<char*>(alloca (strlen(GLOBALCONFIG) + 1));
		sprintf(sConfigFile, GLOBALCONFIG);
		} 
	else {
		// We got a directory, but does it contain a config file?
		sConfigFile = reinterpret_cast<char*>(alloca (strlen(sHomeDir) + strlen(".cdonkey.conf") + 2));
		sprintf(sConfigFile, "%s%c.cdonkey.conf", sHomeDir, dir_delim);
		if (stat(sConfigFile, &dStatBuf) == -1) {	// If not, use the global config file
			sConfigFile = reinterpret_cast<char*>(alloca (strlen(GLOBALCONFIG) + 1));
			sprintf(sConfigFile, "/etc/cdonkey.conf");
			}
		}
	readConfig(sConfigFile);				// Read or create the config file
	db_client  = reinterpret_cast<char*>(MALLOC (strlen(control) + 1 + strlen("source.db") + 1));
	db_server  = reinterpret_cast<char*>(MALLOC (strlen(control) + 1 + strlen("server.db") + 1));
	sprintf(db_client, "%s%c%s", control, dir_delim, "source.db");
	sprintf(db_server, "%s%c%s", control, dir_delim, "server.db");
	cSocket::setLocalInternalIP(cSocket::hostIP(bind_ip));
	//printf("MYHOSTIP: %s\n", inet_ntoa(cSocket::getLocalInternalIP()));
	pref.ports.emule = pref.ports.server + 4;
	printf("Client Name %s\n" , client_name);
	printf("Server  Port %5u\n", pref.ports.server  );
	printf("Command Port %5u\n", pref.ports.clc     );
	printf("Gui     Port %5u\n", pref.ports.gui     );
	printf("eMule   Port %5u\n", pref.ports.emule   );
	printf("Overnet Port %5u\n", pref.ports.kademlia);
	printf("Web     Port %5u\n", pref.ports.web     );
	check_lib  ();
	Initialise ();
	misc_init  ();
	printf("Login: %s - %s\n", ctrl_user, ctrl_pass);
	// }}}
	for (int i0 = 1; i0 < argc; i0++) split_link (argv[i0]);
	CurrentTime ();
	cServer_list::Load ();
	in_addr tmp_ip;
	tmp_ip.s_addr = 0;
//	struct in_addr sadr;
//	sadr.s_addr = inet_addr("217.172.178.34");
//	sadr.s_addr = inet_addr("213.158.125.46");
//	new sServer (sadr, 4661);
/* {{{
	struct in_addr sadr;
	sadr.s_addr = inet_addr("127.0.0.1");
	tHash dh;
	memcpy (dh, hash_hex2bin("f400c95462f8dedfc8e81f79b279ea2b"), 16);
	insertFiles(dh, sadr, 0, NULL);
	new sSource (sadr, 4662);
}}} */
	if (go_to_background)
		fork_to_background();
	
	time_t nextLeftRefresh	= 0;
	do {
		every_sec ();
		nextLeftRefresh = currentTime;
		do {
			cSocket::waitEvent(1000000);
			cSocket::hdlEvent ();
			CurrentTime ();
			} while (nextLeftRefresh == currentTime && running);
		} while (running);	// Erst mit timeout raus *g*
#ifndef	HAVE_ATEXIT
#ifndef	HAVE_ON_EXIT
	exitHandler ();
#endif
#endif
	return 0;
	}}}

//
// Fork a process to background (swiped from wget)
// 
void fork_to_background(void) {{{
	pid_t pid;

	pid = fork ();
	if (pid < 0) { // parent, error
		perror("fork");
		exit (1);
		}
	else if (pid != 0) { // parent, no error
		printf("Continuing in background, pid %d.\n", (int)pid);

		exit(0);
		}

	// child: give up the privileges and keep running.
	setsid();
	freopen("/dev/null", "r", stdin);
	freopen("/dev/null", "w", stdout);
	freopen("/dev/null", "w", stderr);
	}}}

/*
 * vim600: fdm=marker
 */

