#include "cygwin.h"
#include "donkey.h"
#include "sServer.h"
#include "sSource.h"
#include "sSocket.h"
#include "sGui.h"
#include "misc.h"
#include "protocol.h"
#include "sFile.h"
#include "sPacket.h"
#include "sTag.h"
#include "opcodes.h"
#include "db_macro.h"

#ifdef DIPL_LOG
#include "CentralLog.h"
extern class ServerLogClass *ServerLog;
#endif

unsigned int	sServer::to_me = 0;
unsigned int	sServer::count = 0;
class sServer *sServer::first_Server = NULL;

unsigned int	limit_sources	= 8;
unsigned int 	limit_server	= 8;

const char *sServer::Statusname (void) {{{
	switch(status) {
	case NEU 		: return "NEU";
	case NOT_LOGIN 		: return "NOT_LOGIN";
	case INVALID_ID 	: return "INVALID_ID";
	case SEND_FILE_LIST 	: return "SEND_FILE_LIST";
	case REQUEST_NEXT_200 	: return "REQUEST_NEXT_200";
	case REQ_SERVER 	: return "REQ_SERVER";
	case UNUSED_SERVER 	: return "UNUSED_SERVER";
	case ID_CHANGED 	: return "ID_CHANGED";
	case SEARCH 		: return "SEARCH";
	case READY_SERVER 	: return "READY_SERVER";
	case SERVER_CLOSED 	: return "SERVER_CLOSED";
	case WAIT_SERVER_LIST 	: return "WAIT_SERVER_LIST";
	case WAIT_SOURCES 	: return "WAIT_SOURCES";
	case SEARCH_USER 	: return "SEARCH_USER";
	case SEARCH_FILE 	: return "SEARCH_FILE";
	case WAIT_LOGIN 	: return "WAIT_LOGIN";
	case WAIT_REQUEST	: return "WAIT_REQUEST";
	default			: return "unknown";
		}
	}}}

uint32_t client_id = 0;
/// cDonkey server part: this constructor is used for creating new connections 
/// to other clients which connect to this server.
sServer:: sServer        (class cSocket *FD_new, struct in_addr, uint16_t) : sSocket(FD_new) {{{
	next_Server = first_Server;	// only add where we are the server
	first_Server = this;
	Type = tServer;
	count++;
	my_id = peer_ip.s_addr;
	last_file = NULL;
	last_code = 0;
	status = WAIT_LOGIN;
	to_me++;
	isServer = true;
	if (mask != 0) doWork(mask);
#ifdef DIPL_LOG
	SelfLog = new ClientLogClass(ServerLog, this);
#else
	SelfLog = NULL;
	memcpy(the_others_hash, ZERO, 16);
	the_others_name = NULL;
	the_others_version = 0;
	the_others_listening_port = 0;
	ASSERT(the_others_listening_port == 0 && the_others_version == 0 && the_others_name == NULL && SelfLog == NULL 
	       && 0 == memcmp(the_others_hash, ZERO, 16));
#endif
        }}}

sServer:: sServer        (in_addr ip, unsigned short PORT) : sSocket(ip, PORT) {{{
	next_Server = NULL;		// We only connect to one server
	next_search = 0;
	Type = tServer;
	count++;
	my_id = 0;
	isServer = false;
	last_file = NULL;
	last_code = 0;
	status = NOT_LOGIN;
	if (mask != 0) doWork(mask);

	SelfLog = NULL;
	memcpy(the_others_hash, ZERO, 16);
	the_others_name = NULL;
	the_others_version = 0;
	the_others_listening_port = 0;
	ASSERT(the_others_listening_port == 0 && the_others_version == 0 && the_others_name == NULL && SelfLog == NULL 
	       && 0 == memcmp(the_others_hash, ZERO, 16));
	}}}

sServer::~sServer        (void) {{{
#ifdef DIPL_LOG
	delete SelfLog;
	if (the_others_name) free(the_others_name);
#endif
	actionServer (peer_ip, 0); // Frühestens nach 10 min Wiederverwenden
	if (tcp_server == this) tcp_server = NULL;
	if (last_file != NULL) Free2 (last_file);
	count--;
	if (isServer) to_me--;
	// remove me from the file_list
	if (first_Server == NULL) return;
	if (first_Server == this) {
		first_Server = next_Server;
		return;
		}
	sServer *prev_file = first_Server;
	while (prev_file->next_Server != this && prev_file->next_Server != NULL) 
		prev_file = prev_file->next_Server;
	ASSERT (prev_file != NULL);
	if (prev_file->next_Server != this) return;
	prev_file->next_Server = prev_file->next_Server->next_Server;	
}}}

// (TM) 
/// Returns a pointer to the member variable the_others_name, which might be
/// "unclean", but works. 
char *sServer::GetUserName() {{{
	return the_others_name;
	}}}
/// Returns a pointer to the member variable the_others_hash, which also might
/// be unclean, but - as well - works.
void sServer::GetUserHash(tHash *hash) {{{
	memcpy(hash, &the_others_hash, 16);
	}}}
/// Returns the client's version
int sServer::GetUserVersion() {{{
      	return the_others_version;
	}}}
/// Returns the port the other listens on for incoming connections. Might (!) be useful.
int sServer::GetUserListeningPort() {{{
	return the_others_listening_port;
        }}}

bool sServer::searchFile (const char *name) {{{
	if (name == NULL) return false;
	if (last_file != NULL) {
		if (0 == strcmp (last_file, name)) return false;
		Free2 (last_file);
		}
	last_file = STRDUP (name);
	return true;
	}}}
unsigned int   	 sServer::getID       (void) const { return my_id; }
void	       	 sServer::setID       (unsigned int id) {{{
	my_id = id;
	if (htonl(id) >= LOW_ID) {
		struct in_addr ip;
		ip.s_addr = id;
		cSocket::setLocalExternalIP(ip);
	}
}}}
const	char 	*sServer::lastFile (void ) const { return last_file; }

int  sServer::CMD_client_info_server (void              ) {{{ // (0x01)
	ASSERT (client_name != NULL);
	logSend ('>', this, OP_LOGINREQUEST, false);
	size_t len, LEN = len = 46 + strlen(client_name);
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca (LEN));
	ADD_U1     (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4     (&buf, &len, LEN - 5);
	ADD_U1     (&buf, &len, OP_LOGINREQUEST);
	ADD_HASH   (&buf, &len, client_hash);			// client_hash
	ADD_U4     (&buf, &len, 0);				// ??
	ADD_U2     (&buf, &len, pref.ports.client);			// port
	ADD_U4     (&buf, &len, 2);				// into_count
//	add_intTag (&buf, &len, CT_VERSION   , my_version);
	add_intTag (&buf, &len, CT_VERSION   , 61);
	add_strTag (&buf, &len, ST_SERVERNAME, client_name);	// size: 3+1+2+strlen(client_name)
//	add_intTag (&buf, &len, CT_PORT	     , pref.ports.client);
	ASSERT(len == 0);
	Write (BUF, LEN);
	return 1;
	}}}
int  sServer::CMD_req_server_list    (void              ) {{{ // (0x14)
	logSend ('>', this, OP_GETSERVERLIST, false);
	size_t len, LEN = len = 6;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca (LEN));
	ADD_U1 (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4 (&buf, &len, LEN - 5);
	ADD_U1 (&buf, &len, OP_GETSERVERLIST);
	ASSERT(len == 0);
	Write (BUF, LEN);
	return 1;
	}}}
int  sServer::CMD_files_offer        (void              ) {{{ // (0x15)
	logSend ('>', this, OP_OFFERFILES, false);
	unsigned int file_count = 0;
	size_t size = 0;
	 { FILE_LIST {				// some stupid compile make the for declaration global
		file_count ++;
		const char *name = akt->Name();
		size += 40 + strlen (name);
		} }
	size_t len, LEN = len = 6 + 4 + size;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca (LEN));
	ADD_U1     (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4     (&buf, &len, LEN - 5);
	ADD_U1     (&buf, &len, OP_OFFERFILES);
	ADD_U4     (&buf, &len, file_count);
	{ FILE_LIST {				// some stupid compile make the for declaration global
		ADD_HASH(&buf, &len, akt->hash);
		ADD_U4  (&buf, &len, 0);
		ADD_U2  (&buf, &len, 0);
		ADD_U4  (&buf, &len, 2);
		add_strTag (&buf, &len, FT_FILENAME, akt->Name());	// size: 3+1+2+strlen(client_name)
		add_intTag (&buf, &len, FT_FILESIZE, akt->Size());
		} }
	ASSERT(len == 0);
	Write (BUF, LEN);
	return 1;
	}}}
int  sServer::CMD_search4gui         (const class sGui *gui   ) {{{ // (0x16)
	ASSERT (gui!=NULL);
	logSend ('>', this, OP_SEARCHREQUEST, false);
	size_t len, LEN = len = 6 + gui->searchLen();
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca (LEN));
	ADD_U1     (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4     (&buf, &len, LEN - 5);
	ADD_U1     (&buf, &len, OP_SEARCHREQUEST);
	memcpy (buf, gui->searchTree(), len);
	Write (BUF, LEN);
	return 1;
	}}}
int  sServer::CMD_suche_filename     (const char *name  ) {{{ // (0x16)
	ASSERT (name!=NULL);
	logSend ('>', this, OP_SEARCHREQUEST, false);
	size_t len, LEN = len = 6 + 3 + strlen(name);
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca (LEN));
	ADD_U1     (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4     (&buf, &len, LEN - 5);
	ADD_U1     (&buf, &len, OP_SEARCHREQUEST);
	ADD_U1     (&buf, &len, 1);
	ADD_S2     (&buf, &len, name);
	ASSERT(len == 0);
	Write (BUF, LEN);
	return 1;
	}}}
int  sServer::CMD_suche_download     (const tHash &hash ) {{{ // (0x19)
	ASSERT (hash!=NULL);
	if (logSend ('>', this, OP_GETSOURCES, true)) printf("%32s\n", hash_bin2hex(hash));
	printf("%32s\n", hash_bin2hex(hash));
	size_t len, LEN = len = 6 + 16;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca (LEN));
	ADD_U1     (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4     (&buf, &len, LEN - 5);
	ADD_U1     (&buf, &len, OP_GETSOURCES);
	ADD_HASH   (&buf, &len, hash);
	ASSERT(len == 0);
	Write (BUF, LEN);
	return 1;
	}}}
int  sServer::CMD_suche_user         (const char *name  ) {{{ // (0x1A) unused currently
	ASSERT (name!=NULL);
	logSend ('>', this, OP_SEARCH_USER, false);
	size_t len, LEN = len = 6 + strlen (name) + 3;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca (LEN));
	ADD_U1     (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4     (&buf, &len, LEN - 5);
	ADD_U1     (&buf, &len, OP_SEARCH_USER);
	ADD_U1     (&buf, &len, 1);
	ADD_S2     (&buf, &len, name);
	ASSERT(len == 0);
	Write (BUF, LEN);
	return 1;
	}}}
int  sServer::CMD_get_client_info    (uint32_t idx      ) {{{ // (0x1C) unused currently
	logSend ('>', this, OP_CALLBACKREQUEST, false);
	size_t len, LEN = len = 6 + 4;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca (LEN));
	ADD_U1     (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4     (&buf, &len, LEN - 5);
	ADD_U1     (&buf, &len, OP_CALLBACKREQUEST);
	ADD_U4     (&buf, &len, idx);
	ASSERT(len == 0);
	Write (BUF, LEN);
	return 0;
	}}}
int  sServer::CMD_req_next_200       (void              ) {{{ // (0x21)
	logSend ('>', this, OP_QUERY_MORE_RESULT, false);
	size_t len, LEN = len = 6;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca (LEN));
	ADD_U1 (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4 (&buf, &len, LEN - 5);
	ADD_U1 (&buf, &len, OP_QUERY_MORE_RESULT);
	ASSERT(len == 0);
	Write (BUF, LEN);
	return 1;
	}}}
/// cDonkey server part member function: sends a serverlist to the requesting
/// caller. 
/// DO NOT confuse this with sServer::res_server_list(class sPacket *)!
int  sServer::res_server_list        (void              ) {{{ // (0x32)
	struct in_addr ip;
	uint16_t port;
	if (limit_server > 255) limit_server = 255;
	uint8_t cnt = (limit_server);
	logSend ('>', this, OP_SERVERLIST, false);
	size_t len, LEN = len = 6 + 1 + cnt * 6;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca (LEN));
	ADD_U1     (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4     (&buf, &len, LEN - 5);
	ADD_U1     (&buf, &len, OP_SERVERLIST);
	ADD_U1     (&buf, &len, cnt);
	for (;cnt > 0;cnt--) {
		nextServer (&ip, &port);
		ADD_U4 (&buf, &len, ip.s_addr);
		ADD_U2 (&buf, &len, port);
		}
	ASSERT(len == 0);
#ifdef DIPL_LOG
	SelfLog->LogTcpOut(OP_SERVERLIST, LEN);
#endif
	Write (BUF, LEN);
	return 0;
	}}}
int  sServer::CMD_callback           (in_addr ip, uint16_t port) {{{ // (0x35)
	logSend ('>', this, OP_CALLBACKREQUESTED, false);
	size_t len, LEN = len = 6 + 6;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca (LEN));
	ADD_U1     (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4     (&buf, &len, LEN - 5);
	ADD_U1     (&buf, &len, OP_CALLBACKREQUESTED);
	ADD_U4     (&buf, &len, ip.s_addr);
	ADD_U2     (&buf, &len, port);
	ASSERT(len == 0);
	Write (BUF, LEN);
	return 1;
	}}}
int  sServer::CMD_callback_failed    (void              ) {{{ // (0x36)
	logSend ('>', this, OP_CALLBACK_FAIL, false);
	size_t len, LEN = len = 6;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca (LEN));
	ADD_U1 (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4 (&buf, &len, LEN - 5);
	ADD_U1 (&buf, &len, OP_CALLBACK_FAIL);
	ASSERT(len == 0);
	Write (BUF, LEN);
	return 1;
	}}}
/// cDonkey server part function: sends new ID to the new client. 
/// DO NOT confuse this with sServer::res_change_id(class sPacket *)!
int  sServer::res_change_id          (void              ) {{{ // (0x40)
	logSend ('>', this, OP_IDCHANGE, false);
	size_t len, LEN = len = 6 + 4;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca (LEN));
	ADD_U1     (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4     (&buf, &len, LEN - 5);
	ADD_U1     (&buf, &len, OP_IDCHANGE);
	ADD_U4     (&buf, &len, getID ());
	ASSERT(len == 0);
	Write (BUF, LEN);
#ifdef DIPL_LOG	
	SelfLog->LogTcpOut(OP_IDCHANGE, LEN);
#endif
	return 1;
	}}}
/// cDonkey server part member function: searches for a given hash value in the server's
/// file database and sends the results back to the caller.
/// @param hash the hash value to be searched
int  sServer::CMD_res_sources        (const tHash &hash ) {{{ // (0x42)
	const struct eFiles *files = nextFile(hash, true);      // cursor_count is now set
#ifdef DIPL_LOG
	SelfLog->LogSrcSearch(hash, cursor_count);
#endif
	if (cursor_count == 0) return 1;
	uint32_t cnt = cursor_count;
	if (cnt > limit_sources ) cnt = limit_sources;
	if (cnt > 255 		) cnt = 255;

	if(logSend ('>', this, OP_FOUNDSOURCES, true))
		printf("%32s (%i)\n", hash_bin2hex(hash), cnt);

	size_t len, LEN = len = 6 + 16 + 1 + cnt * 6;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca (LEN));
	ADD_U1     (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4     (&buf, &len, LEN - 5);
	ADD_U1     (&buf, &len, OP_FOUNDSOURCES);
	ADD_HASH   (&buf, &len, hash);
	ADD_U1     (&buf, &len, (cnt));
	do {
		const struct eSources *s = Source_ip (files->ip);
	        uint16_t port = 4662;
	        if (s != NULL) port = s->port;
		cnt--;
		ADD_U4(&buf, &len, files->ip.s_addr);
		ADD_U2(&buf, &len, port);
		files = nextFile(hash, false);
		ASSERT(files != NULL || cnt == 0);
		} while(cnt>0);
	ASSERT(len == 0);
	Write (BUF, LEN);
	return 1;
}}}
/// cDonkey server part member function: handler for hello messages (0x01)
int  sServer::REQ_server_info        (sPacket *packet) {{{ // (0x01)
	logSend ('<', this, OP_LOGINREQUEST, false);
	unsigned int old_id;
	uint16_t port;
	Code(OP_LOGINREQUEST);
#ifndef DIPL_LOG
	tHash hash;
	packet->GET_HASH(hash);
#else
	packet->GET_HASH(the_others_hash);
#endif
	old_id = packet->GET_U4 ();					// user->idx_old
	port   = packet->GET_U2 ();					// user->port

	class sTag *tag = parseTag (packet);
	if (tag == NULL) return 0;
#ifdef DIPL_LOG
  	if (tag->name != NULL) 	the_others_name = strdup(tag->name);
	if (tag->version != 0)	the_others_version = tag->version;
	if (tag->port    != 0)  the_others_listening_port = tag->port;
#endif
	delete tag;
	res_change_id ();
//	old_id = packet->GET_U4 ();
	return 0;
}}}


int  sServer::RES_unwanted           (sPacket *      ) {{{ // (0x05) (log.info)
	logSend ('<', this, OP_REJECT, false);
	delServer (peer_ip, 0, -4);
	Close();
	return 0;
	}}}
///cDonkey server part member function: handler for request serverlist messages (0x14)
int  sServer::REQ_serverList         (sPacket *packet) {{{ // (0x14) (log.info)
	logSend ('<', this, OP_GETSERVERLIST, false);
	if (packet == NULL) return 0;
	res_server_list ();
	return 1;
	}}}
///cDonkey server part member function: handler for offer files messages (0x15)
int  sServer::RES_offerFiles         (sPacket *packet) {{{ // (0x15)
	logSend ('<', this, OP_OFFERFILES, false);
	ASSERT (packet!= NULL);
	int cnt = packet->GET_U4();
	while (--cnt >= 0) {
		tHash hash;
		packet->GET_HASH(hash);
		packet->GET_U4  ();
		packet->GET_U2  ();
		unsigned char *start = packet->DATA();
		class sTag *tag = parseTag (packet);
		// tag->name, tag->size holding filename/-size
		// Name speicher
		delete tag;
		unsigned char *ende   = packet->DATA();
		printf("Got file info_lei = %i\n", ende - start);
		insertFiles (hash, peer_ip, ende - start, start);
		}
	return 1;
	}}}
/// cDonkey server part member function: handler for query sources messages (0x19)
int  sServer::REQ_sources            (sPacket *packet) {{{ // (0x19)
	logSend ('<', this, OP_GETSOURCES, false);
	if (!res_server_sources) return 1;
	ASSERT (packet!= NULL);
	tHash hash;
	packet->GET_HASH(hash);
	printf("search for hash: %32s\n", hash_bin2hex(hash));
	CMD_res_sources (hash);
	return 1;
	}}}
int  sServer::RES_server_list        (sPacket *packet) {{{ // (0x32) (log.info)
	logSend ('<', this, OP_SERVERLIST, false);
	if (packet == NULL) return 0;
	Code (OP_SERVERLIST);
	int cnt = packet->GET_U1 ();
	while (--cnt >= 0) {
		struct in_addr ip;
		ip.s_addr = packet->GET_U4 ();
		uint16_t port = packet->GET_U2 ();
		if (port == 4665) port = 4661;
		if (htonl(ip.s_addr) <= 0x00FFFFFF) printf("zero Server at %i\n", cnt);
		else saveServer (ip, port, 0, 0, false);
		}
	return 0;
	}}}
int  sServer::RES_search             (sPacket *packet) {{{ // (0x33)
	logSend ('<', this, OP_SEARCHRESULT, false);
	if (packet == NULL) return 0;
	struct in_addr ip;
	GUI_LIST {{{
		if (akt->searchLen() > 0) {
			uint32_t len = packet->Length() - 1;
			if (len > 0) {
				char *tree = reinterpret_cast<char*>(MALLOC (len));
				memcpy (tree, packet->DATA(), len);
				akt->resultTree (tree, len);
				akt->TCP_Response   ();
				akt->GUI_result_tcp ();
				}
			}
		}}}
	int cnt = packet->GET_U4 ();
	unsigned int COUNT = cnt;
	Code((COUNT >= 200)?0x0133:0x0033);
	printf("Count %u\n", cnt);
	while (cnt-->0) {{{
		tHash hash;
		log (2,"Found file: (%i)\n", (COUNT-cnt));
		packet->GET_HASH(hash);
		ip.s_addr     =	packet->GET_U4 ();
		uint16_t port =	packet->GET_U2 ();
		log (2,"Found file: (%3i) - Client: %15s : %5u\n",(COUNT-cnt),inet_ntoa (ip), port);
		unsigned char *start = packet->DATA();
		class sTag *tag = parseTag (packet);
		if (tag == NULL) return 0;
//		if (tag->name != NULL && tag->size != 0)
//			printf ("ed2k://|file|%s|%u|%s|\n", tag->name, tag->size, hash_bin2hex (hash));
		delete tag;
		unsigned char *ende = packet->DATA();
		printf("Got file info_len = %i\n", ende - start);
		insertFiles (hash, peer_ip, ende - start, start);
		}}}
	return 1;
	}}}
int  sServer::RES_online_stat        (sPacket *packet) {{{ // (0x34) (log.statistik)
	logSend ('<', this, OP_SERVERSTATUS, false);
	if (packet == NULL) return 0;
	unsigned int user  = packet->GET_U4 ();
	unsigned int files = packet->GET_U4 ();
	saveServer (peer_ip, 0,  files, user, false);
	return 0;
	}}}
int  sServer::RES_connect_req        (sPacket *packet) {{{ // (0x35) // Solte in warteschleife
	if (packet == NULL) return 0;
	struct in_addr ip;
	uint16_t port;
	ip.s_addr = packet->GET_U4 ();
	port 	  = packet->GET_U2 ();
	if (!IP_is_OK (ip)) return true;
	if(logSend ('<', this, OP_CALLBACKREQUESTED, true)) printf("%15s:%5u\n", inet_ntoa(ip), port);
	if (ip.s_addr == htonl(0x7F000001) && port == pref.ports.client) return 1;
	if (htonl(ip.s_addr) < LOW_ID) {
		printf("LOW ID init callback\n");
		return 0;
		}
	tHash hash;
	bzero (hash, 16);
	new sSource (ip, port);
	addSource (ip, port, 0x00000000);
	return 1;
	}}}
int  sServer::RES_server_message     (sPacket *packet) {{{ // (0x38) (log.info) KEINE STATUS ÄNDERUNG
	logSend ('<', this, OP_SERVERMESSAGE, false);
	if (packet == NULL) return 0;
	char *msg = packet->GET_S2 ();
	size_t len = 21 + strlen(msg);
	char *buf = reinterpret_cast<char*>(alloca (len));
	snprintf(buf, len, "%15s => %s\n", inet_ntoa(peer_ip), msg);
	bool gui = false;
	GUI_LIST {
		gui = true;
		akt->GUI_statusMessage(buf);
		}
	if (!gui) if (logSend ('<', this, OP_SERVERMESSAGE, true)) printf("%s\n", msg);
	Free2 (msg);
	return 1;
	}}}
int  sServer::RES_change_id          (sPacket *packet) {{{ // (0x40)
	logSend ('<', this, OP_IDCHANGE, false);
	if (packet == NULL) return 0;
	setID (packet->GET_U4 ());
	Code (OP_IDCHANGE);
	return 1;
	}}}
int  sServer::RES_version            (sPacket *packet) {{{ // (0x41) KEINE STATUS ÄNDERUNG
	logSend ('<', this, OP_SERVERIDENT, false);
	ASSERT (packet!= NULL);
	tHash hash;
	struct in_addr ip;
	uint16_t port;
	packet->GET_HASH(hash);
	ip.s_addr = htonl (packet->GET_U4 ());
	port      = htons (packet->GET_U2 ());
	class sTag *tag = parseTag (packet);
	if (tag == NULL) {
		packet->Dump();
		return 0;
		}
	if (tag->name != NULL) {
		setServer_name (ip, port, tag->name);
		GUI_LIST akt->GUI_connected_to (tag->name);
		printf("Successfull TCP-Server Connection to %s\n", tag->name );
		}
	if (tag->desc != NULL) setServer_desc (ip, port, tag->desc);
	delete tag;
	return 1;
	}}}
int  sServer::RES_sources            (sPacket *packet) {{{ // (0x42) wird in die auptschleife verschoen (sSource)
	tHash hash;
	packet->GET_HASH(hash);
	int cnt = packet->GET_U1 ();
	if (logSend ('<', this, OP_FOUNDSOURCES, true)) printf ("%32s (%3i)\n", hash_bin2hex(hash), cnt);
	sFile *akt_file = forHash(hash);
	// ONLY if the response are for the wrong file !!!
	while (--cnt >= 0) {
		unsigned int   IDX  = packet->GET_U4 ();
		unsigned short PORT = packet->GET_U2 ();
		struct in_addr ip;
		ip.s_addr = IDX;
		if (htonl(IDX) > LOW_ID)  {
			ip.s_addr = IDX;
			addSource(ip, PORT, 0x00000000, true);
			insertFiles (hash, ip, 0, NULL);
			}
		else 	CMD_callback(ip, PORT);
		}
	if (akt_file != NULL)
		if ( akt_file->status != ALL_DONE ) 
			akt_file->Status (SOURCE_KNOWN);
	return 0;
	}}}
int  sServer::RES_search_user        (sPacket *packet) {{{ // (0x43) Erzeugt neues sfile Object !!!
	logSend ('<', this, OP_USERS_LIST, false);
	ASSERT (packet!= NULL);
	struct in_addr ip;
	tHash hash;
	int cnt  = packet->GET_U4 ();
	unsigned int COUNT = cnt;
	printf("Count %u\n", cnt);
	while (--cnt >= 0) {
		printf("Found file: (%i)\n", (COUNT-cnt));
		packet->GET_HASH(hash);
		printf("- Hash : %s\n", hash_bin2hex (hash));	// Server hash
		ip.s_addr = packet->GET_U4 ();
		printf("- Client: %s\n", inet_ntoa (ip));	// server ip
		printf("- Port: %u\n", packet->GET_U2 () );	// server port
		class sTag *tag = parseTag (packet);
		if (tag == NULL) return 0;
		if (tag->name != NULL) {
			printf("User %s , Port %5u , Version %4u\n",tag->name, tag->port, tag->version);
			}
		delete tag;
		}
	Code (OP_USERS_LIST);
	return 1;
	}}}

class cTreeSearch {
	public:
	int 		 op;
	class cTreeSearch 	*A;
	class cTreeSearch 	*B;
	char		*val_str;
	int	 	 val_int;
	char		*key;
			 cTreeSearch(void);
			~cTreeSearch(void);
	bool		 Parse (unsigned char *data, unsigned len);
	bool		 Match (void) { return false; }
	};
cTreeSearch:: cTreeSearch(void) {{{
	op      = 0xFF;
	A       = NULL;
	B       = NULL;
	val_str = NULL;
	val_int = 0;
	key	= NULL;
	}}}
cTreeSearch::~cTreeSearch(void) {{{
	if (A 	    != NULL) delete A;
	if (B 	    != NULL) delete B;
	if (val_str != NULL) Free2 (val_str);
	if (key     != NULL) Free2 (key    );
	}}}
bool cTreeSearch::Parse (unsigned char *data, unsigned len) {{{
	while (len>0) {
		unsigned new_op = GET_U1 (&data, &len);
		switch (new_op) {
			case 0x01: op = new_op;
				   val_str = GET_S2_ (&data, &len);
				   printf("Match String %s\n", val_str);
				   free (val_str);
				   break;
			default  : return false;
			}
		}
	return true;
	}}}

int  sServer::REQ_file_search (sPacket *packet) {{{ // (0x16)
	int MAX_SEARCH = 14000;
	cTreeSearch *search = new cTreeSearch();
	bool OK = search->Parse (packet->DATA(), packet->Unused());
	if (!OK) {
		delete search;
		packet->Dump ();
		return 0;
		}
	int err = 0;
	DBT key;
	DBT val;
	bzero (&key, sizeof(key));
	bzero (&val, sizeof(val));
	DBC *cur = NULL;
	DB_cursor2 (dat_idx, cur);
	unsigned char *buffer, *BUFFER = buffer = reinterpret_cast<unsigned char*>(alloca (MAX_SEARCH));
	size_t size = MAX_SEARCH;
	unsigned cnt = 0;
	while (0 == cur->c_get (cur, &key, &val, DB_NEXT)) {{{
		struct eFiles *f = reinterpret_cast<struct eFiles *>(val.data);
		if (f->info_len == 0) continue;
		if (buffer + 22 + f->info_len > BUFFER + MAX_SEARCH) break;
		size_t ilen = f->info_len;
		class sTag *tag = parse_tag (f->info, &ilen);
		if (tag == NULL) continue;
		if (search->op == 1 && tag->name != NULL)
			if (NULL != strstr(tag->name, search->val_str)) {
				tHash hash;
				memcpy (hash, f->hash, 16);
				printf("MATCH (%3i) for Search %32s - %9u - %s\n", f->info_len, hash_bin2hex(hash), tag->size, tag->name);

				ADD_HASH(&buffer, &size, hash);
				ADD_U4  (&buffer, &size, 0);
				ADD_U2  (&buffer, &size, 0);
				memcpy  (buffer, f->info, f->info_len);
				buffer += f->info_len;
				size   -= f->info_len;
				cnt++;
				}
		delete tag;
		}}}

	
	size_t len, LEN = len = 6 + MAX_SEARCH - size + 5;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca (LEN));
	ADD_U1     (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4     (&buf, &len, LEN - 5);
	ADD_U1     (&buf, &len, OP_SEARCHRESULT);
	ADD_U4     (&buf, &len, cnt);
	memcpy(buf, BUFFER, MAX_SEARCH - len);
	buf += (MAX_SEARCH - len);
	len -= (MAX_SEARCH - len);
	ADD_U1     (&buf, &len, 0);
	ASSERT(len == 0);
	Write (BUF, LEN);
	delete search;
	return 1;
	}}}

int  sServer::Donkey_server (sPacket *packet) {{{
	ASSERT (packet != NULL);
	ASSERT (packet->Proto() == OP_EDONKEYHEADER);
	switch (packet->Command ()) {
		case 0x1D : return 1; 	// OP_QUERY_CHATS	(depricated)
		case 0x1E :	// OP_CHAT_MESSAGE	(depricated)
		case 0x1F : 	// OP_JOIN_ROOM		(depricated)
		case 0x39 :	// OP_CHAT_ROOM_REQUEST	(depricated)
		case 0x3A :	// OP_CHAT_BROADCAST	(depricated)
		case 0x3B :	// OP_CHAT_USER_JOIN	(depricated)
		case 0x3C :	// OP_CHAT_USER_LEAVE	(depricated)
		case 0x3D :	// OP_CHAT_USER		(depricated)
			return 1;
#ifdef DIPL_LOG
	}
	// only log client requests, NOT server commands to cDonkey as a client
	// further do not log deprecated messages.
	if (packet->Command() < 0x25)
	   SelfLog->LogTcpIn(packet);
	switch (packet->Command()) {
#endif       
		case 0x01 : return REQ_server_info   	(packet);
		case 0x05 : return RES_unwanted		(packet);
		case 0x14 : return REQ_serverList	(packet);
		case 0x15 : return RES_offerFiles	(packet);
		case 0x16 : return REQ_file_search 	(packet);
		case 0x19 : return REQ_sources		(packet);
		case 0x32 : return RES_server_list	(packet);
		case 0x33 : return RES_search		(packet);
		case 0x34 : return RES_online_stat	(packet);
		case 0x35 : return RES_connect_req	(packet);
		case 0x38 : return RES_server_message	(packet);
		case 0x40 : return RES_change_id	(packet);
		case 0x41 : return RES_version		(packet);
		case 0x42 : return RES_sources		(packet);
		case 0x43 : return RES_search_user	(packet);
		default   : break;
	}
	packet->Dump ();
	return 0;
}}}

void sServer::readWork      (void) {{{
	ASSERT(Type == tServer);
	if (READ->Proto() == OP_EDONKEYHEADER) Donkey_server (READ);
	else {
		READ->Dump ();
		ASSERT(0);
		}
	READ->readReset();
	if (last_code != 0) Work();
	}}}
void sServer::Work          (void) {{{	// only called if fd != INVALID socket
	static bool first_connect = true ;
	static time_t timeout_event = time(NULL) ;// timeout if no packet is received
	if (!connected) return;
	if (status == SERVER_CLOSED) return;
	if (last_code != 0) {{{
		switch (status) {
		case ID_CHANGED		: if (0x40 == last_code) last_code = 0; break;
		case INVALID_ID		: if (0x40 == last_code) { last_code = 0; status = ID_CHANGED;    } // Warte auf ID Change
					  else {
                                		if  ( currentTime - timeout_event > 120 ) { // 120 secs
                                    		last_code = 0; status = SERVER_CLOSED;
                                    			timeout_event = currentTime;
                                			}
                              			}
                              		  break;
		case WAIT_SERVER_LIST	: // Warte auf Server Liste
					  first_connect = true; 
					  if (0x32 == last_code) { last_code = 0; status = SEND_FILE_LIST;} break;
		case WAIT_SOURCES	: if (0x42 == last_code) { last_code = 0; status = READY_SERVER;  } // Warte auf Source Liste
					  else {
						if  ( currentTime - timeout_event > 50 ) {
							last_code = 0; 
							status = READY_SERVER;
							timeout_event = currentTime;
							}
						else printf("waiting %ld sec\n", (currentTime - timeout_event ) );
						}
					  break;
		case SEARCH_USER	: if (0x43 == last_code) { last_code = 0; status = READY_SERVER;  } break; // warte auf User   Liste
		case SEARCH_FILE	: printf("Search file last_status %2X\n", last_code);
					  if (0x33 == last_code || 0x133 == last_code ) { 
						last_code = 0; 
					  	status = READY_SERVER;  } 
					  else { 
						printf("Waiting for search results\n"); 
   			                	  if  ( currentTime - timeout_event > 50 ) {
							printf("XXXX timeout. Perhaps send empty reply to GUI !\n");
                				       	last_code = 0; status = READY_SERVER;
       		                			timeout_event = currentTime;
                        				}
					  	}
					  break ;
		case WAIT_LOGIN		: if (0x40 == last_code) { last_code = 0; status = WAIT_REQUEST;  } break;
		default			: break;
			}
		if (last_code != 0) log (1, "1 Server status %i %2X\n", status, last_code);
		last_code = 0;
		}}}

	if ( status != NEU && status != NOT_LOGIN && status != ID_CHANGED && status != SEND_FILE_LIST 
		 && status != READY_SERVER && status != REQUEST_NEXT_200 && status != SERVER_CLOSED
		 && currentTime - timeout_event > 90 ) // 90 secs timeout on all status types before we do special handling
		last_code = 18; // TIMEOUT eServer_status
	switch (status) {
	case NEU:
	case NOT_LOGIN     : 	if (!CMD_client_info_server	()) {
					status = SERVER_CLOSED;
					break;
					}
			     	status = INVALID_ID ;
					timeout_event = currentTime;
				break;
	case ID_CHANGED	   : 	CMD_files_offer  ();
				if (CMD_req_server_list	()) {
					timeout_event = currentTime;
					status = WAIT_SERVER_LIST;
					}
				else 	status = SERVER_CLOSED;
				break;
	case SEND_FILE_LIST: 	status = READY_SERVER;
				break;
	case READY_SERVER:  
#if 0
					// work "suche" links
			    	if (searchFile (such_file)) {{{
					if (CMD_suche_filename  (such_file)) status = SEARCH_FILE;
					else	status = SERVER_CLOSED;
					break;
					}}}
#endif
					// get tcp_server sourcen for all files
				if ( first_connect || next_search <  currentTime) {
					if (sFile::count == 0) break;
					if (first_connect) {
						first_connect = false;
						}
					next_search = currentTime + 60;
				  	log (2,"TCP Server suche\n");
					FILE_LIST {
						if (akt->get_Completed() ) continue;	// looking for not completed files
						if (akt->Pause    () ) continue;
						// should we delay here ? (flood)
						CMD_suche_download(akt->hash);
						status = WAIT_SOURCES;
						timeout_event = currentTime;
						}	
					}

					// prevent overwrite of status flag
			     	if (tcp_search && status == READY_SERVER ) {{{
					const sGui *gui = NULL;
					GUI_LIST
						if (akt->searchTree()!=NULL && akt->searchLen()>0) {
							gui = akt;
							break;
							}
					tcp_search = false;
					if (gui == NULL) break;
					if (gui->searchLen() > 0) {
						if (1 == CMD_search4gui (gui)) {
							status = SEARCH_FILE;
							timeout_event = currentTime;
							break;
							}
						}
					}}}

				break;
	case REQUEST_NEXT_200:	CMD_req_next_200 ();
				status = SEARCH;
				break;
	case SERVER_CLOSED:
	default :		break;
		}
	}}}

/*
 * vim600: fdm=marker
 */

