#include "cygwin.h"
#include "kademlia.h"
#include "misc.h"
#include "sTag.h"
#include "protocol.h"
#include "opcodes.h"
#include "donkey.h"
#include "sGui.h"
#include "db_macro.h"
#include "cZone.h"
#include "cSearchManager.h"

#define ctol32(x)       (x)
#define ctol16(x)       (x) 
extern time_t 	currentTime;
extern bool     running;
extern int 	protocol_logLevel;
uint8_t this_hash[HASH_LEN];

class cZone 		overnet;
class cSearchManager	manager;
class cKademlia	       *kademlia = NULL;

const char *op2str(unsigned op) {{{
	switch(op) {
		case 0x0A : return "CONNECT    ";
		case 0x0B : return "CONNECT_res";
		case 0x0C : return "PUBLIZE    ";
		case 0x0D : return "PUBLIZE_ack";
		case 0x0E : return "NEXT       ";
		case 0x0F : return "NEXT_res   ";
		case 0x10 : return "QUERY      ";
		case 0x11 : return "QUERY_res  ";
		case 0x12 : return "QUERY_end  ";
		case 0x13 : return "PUBLISH    ";
		case 0x14 : return "PUBLISH_res";
		case 0x15 : return "ident_res  ";
		case 0x16 : return "ident_ack  ";
		case 0x18 : return "FIRE       ";
		case 0x19 : return "FIRE_ack   ";
		case 0x1A : return "FIRE_nack  ";
		case 0x1B : return "CHECK      ";
		case 0x1C : return "CHECK_ack  ";
		case 0x1D : return "CHECK_end  ";
		case 0x1E : return "Identify   ";
		default   : break;
		}
	return " (unknown) ";
	}}}
unsigned XOR_distance (const uint8_t *a, const uint8_t *b) {{{
	uint8_t XOR[KAD_HASH_LEN];
	for (unsigned j=0; j < KAD_HASH_LEN; j++) XOR[j] = a[j] ^ b[j];
	unsigned idx = 0;
	uint8_t check = 0x80;
	unsigned cnt = 0;
	for(unsigned i = 0; i < KAD_HASH_LEN*8; i++) {
		if (idx >= KAD_HASH_LEN) return 128;
		if (check & XOR[idx]) cnt++;
		check >>= 1;
		if (check == 0) { 
			check = 0x80; 
			idx ++; 
			}
		}
	return cnt;
	}}}

cKademlia:: cKademlia (len2 my_port):cSocket(my_port,SOCK_DGRAM) {{{
	Type = tKademlia;
	boot_file = NULL;
	boot_done = false;
	next_ip_query = 0;
	bzero(last_publish, KAD_HASH_LEN);
	status = 0;	// NEED_BOOTING
	for (unsigned i = 0; i < KAD_HASH_LEN    ; i++) this_hash[i] = rand();
	bzero  (seek_hash, KAD_HASH_LEN);
	printf ("my_hash     %32s\n", hash_bin2hex(this_hash));
	best_publize = 128;
	last_query = 0;
	if (mask != 0) doWork(mask);
	kademlia = this;
	}}}
cKademlia::~cKademlia () {{{
	kademlia = NULL;
	}}}
void     cKademlia::load_Boot   (const char *boot) {{{
	if (boot_done) return;
	size_t cnt = overnet.getNumContacts();
	if (cnt > 0) {
		printf("Read %u from contact.dat\n", cnt);
		boot_done = true;
		return;
		}
	if (boot_file == NULL) {{{
		printf("boot = %s\n", boot);
		boot_file = fopen (boot, "r");
		if (boot_file == NULL) {
			printf ("errno %i => \n", errno);
			printf( "cKademlia::load_Boot: failed to open %s\n", boot ); 
			boot_done = true;
			return;
			}
		}}}
	char BUF[512];
	cnt = 10;
	while (NULL != fgets(BUF, 512, boot_file) && running) {{{
		struct in_addr p_ip;
		unsigned  p_port;
		char Hash[33], *hash=Hash;
		char IPS[512], *ips=IPS;
		ips = IPS;
		if ( ( sscanf(BUF, "ed2k://|kademlia|%32s|%s %u|/\n", hash, ips, &p_port) ) != 3 ) continue;
		p_ip.s_addr = IP4Name(IPS);
		uint8_t p_hash[KAD_HASH_LEN];
		memcpy (p_hash, hash_hex2bin(Hash), KAD_HASH_LEN);
		overnet.add(p_hash, p_ip.s_addr, p_port, 0);
		cnt --;
		if (cnt <= 0) return;
//		printf ("bcp://%s|%s|%u|\n", hash_bin2hex(p_hash), inet_ntoa(p_ip), p_port);
		}}}
	boot_done = true;
	fclose(boot_file);
	boot_file = NULL;
	}}}
void     cKademlia::Init_Search (void) {{{
	}}}
bool     cKademlia::Search      (const uint8_t *hash) {{{
	log (1, "OVERNET search %s\n", hash_bin2hex(hash));
	manager.findFile(hash);
	if (status == 8) memcpy(seek_hash, hash, KAD_HASH_LEN);
	return true;
	}}}

// send {{{
bool     cKademlia::send_Connect           (void) {{{ // (0x0A) request boot list
	if (!IP_is_OK(peer_ip) || peer_port == 0) return false;
	logSend('>', OVERNET_CONNECT, 24 );
	unsigned char BUF[24];
	unsigned char *buf = BUF;
	unsigned int  len = 24;
	ADD_U1	(&buf, &len, OP_EDONKEYHEADER);
	ADD_U1	(&buf, &len, OVERNET_CONNECT);
	ADD_HASH(&buf, &len, seek_hash);
	ADD_U4  (&buf, &len, this_ip.s_addr);
	ADD_U2	(&buf, &len, this_port);
	Write(BUF, 24);
	return true;
	}}}
bool     cKademlia::send_Connect_reply     (void) {{{ // (0x0B) return list of other clients
	cPeerMap map;
	size_t RET = overnet.getClosestTo (this_hash, OVERNET_BUCKET, &map);
	RET = map.size();
	if(RET == 0) return false;
	size_t len, LEN = len = 4 + 23 * RET;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca(LEN));
	logSend('>', OVERNET_CONNECT_RES, LEN);
	ADD_U1 (&buf, &len, OP_EDONKEYHEADER);
	ADD_U1 (&buf, &len, OVERNET_CONNECT_RES);
	ADD_U2 (&buf, &len, RET);
	cPeerMap::iterator it;
	for (size_t i=0; i<RET; i++) {
		it = map.begin();
		class cPeer *c = it->second;
		map.erase(it);
		ADD_HASH (&buf, &len, c->get_Hash()->hash);
		ADD_U4	 (&buf, &len, c->ip);
		ADD_U2	 (&buf, &len, c->udp);
		ADD_U1	 (&buf, &len, c->type);
		if (len == 0) break;
		}
	ASSERT(len == 0);
	Write(BUF, LEN);
	return true;
	}}}
bool     cKademlia::send_Publicize         (void) {{{ // (0x0C) publize my client
	logSend('>', OVERNET_HELLO_REQUEST, 25 );
	unsigned char BUF[25];
	unsigned char *buf = BUF;
	size_t len, LEN = len = 25;
	ADD_U1	(&buf, &len, OP_EDONKEYHEADER);
	ADD_U1	(&buf, &len, OVERNET_HELLO_REQUEST);
	ADD_HASH(&buf, &len, this_hash);
	ADD_U4  (&buf, &len, this_ip.s_addr);
	ADD_U2	(&buf, &len, this_port);
	ADD_U1	(&buf, &len, 0);
	ASSERT(len == 0);
	Write(BUF, LEN);
	return true;
	}}}
bool     cKademlia::send_Publicize_ack     (void) {{{ // (0x0D) ack the publize of my client
	logSend('>', OVERNET_HELLO_ACK,  2 );
	unsigned char BUF[2];
	unsigned char *buf = BUF;
	unsigned len = 2;
	ADD_U1 (&buf, &len, OP_EDONKEYHEADER);
	ADD_U1 (&buf, &len, OVERNET_HELLO_ACK);
	Write  (BUF, 2);
	return true;
	}}}
bool     cKademlia::send_Search            (uint8_t for_type, uint8_t *for_hash) {{{ // (0x0E) search after an HASH (user/file)
	logSend('>', OVERNET_REQUEST, 19 );
	unsigned char BUF[19];
	unsigned char *buf = BUF;
	unsigned int  len, LEN = len = 19;
	ADD_U1	(&buf, &len, OP_EDONKEYHEADER);
	ADD_U1	(&buf, &len, OVERNET_REQUEST);
	ADD_U1	(&buf, &len, for_type);
	ADD_HASH(&buf, &len, for_hash);
	ASSERT(len == 0);
	Write (BUF, LEN);;
	return true;
	}}}
bool     cKademlia::send_Search_next       (unsigned cnt, const uint8_t *p_hash) {{{ // (0x0F) List of peers to try next time
	cPeerMap map;
	size_t RET = overnet.getClosestTo (p_hash, cnt, &map);
	RET = map.size();
	if (RET == 0) return false;
	size_t len, LEN = len = 20 + 23 * RET;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca(LEN));
	logSend('>', OVERNET_REQUEST_RES, LEN);
	ADD_U1   (&buf, &len, OP_EDONKEYHEADER);
	ADD_U1   (&buf, &len, OVERNET_REQUEST_RES);
	ADD_HASH (&buf, &len, p_hash);
	ADD_U2   (&buf, &len, RET);
	cPeerMap::iterator it;
	for (size_t i=0; i<RET; i++) {
		it = map.begin();
		class cPeer *c = it->second;
		map.erase(it);
		ADD_HASH (&buf, &len, c->get_Hash()->hash);
		ADD_U4	 (&buf, &len, c->ip);
		ADD_U2	 (&buf, &len, c->udp);
		ADD_U1	 (&buf, &len, c->type);
		if (len == 0) break;
		}
	ASSERT(len == 0);
	Write( BUF, LEN);
	return true;
	}}}
/* now in cSearch {{{
bool     cKademlia::send_Search_info       (void                      ) {{{ // (0x10) req_result
	logSend('>', OVERNET_SEARCH_REQUEST, 23);
	char BUF[23];
	char *buf = BUF;
	unsigned len = 23;
	ADD_U1   (&buf, &len, OP_EDONKEYHEADER);
	ADD_U1   (&buf, &len, OVERNET_SEARCH_REQUEST);
	ADD_HASH (&buf, &len, seek_hash);
	ADD_U1   (&buf, &len, 0);
	ADD_U2	 (&buf, &len, 0);
	ADD_U2	 (&buf, &len, 100);
	ASSERT(len == 0);
	Write (BUF, 23);
	return true;
	}}}
	}}} */
bool     cKademlia::send_Search_result     (uint8_t *p_hash, unsigned min, unsigned max) {{{ // (0x11)
	int err = 0;
	logSend('>', OVERNET_SEARCH_RESULT, 0);
	DBC *cur;
	DB_cursor2 (dat_hash, cur);
	INIT_DBT(key, p_hash, HASH_LEN);
	DBT val;
	bzero(&val, sizeof(val));
	log (1, "SEEK %s - %u - %u\n", hash_bin2hex(p_hash), min, max);
	if (0 != (err = cur->c_get (cur, &key, &val, DB_SET))) {{{
		cursor_count = 0;
		if (err != DB_NOTFOUND) DB_error();
		return false;
		}}}
	unsigned cnt = 0;
	do {
		struct eFiles *file = reinterpret_cast<struct eFiles *>(val.data);
		if (file->info_len == 0) continue;
		struct eSources *source = Source_ip(file->ip);
		if (source == NULL) continue;
		if (0 == (source->version & 0x02000000)) continue;
		size_t len, LEN = len = 34 + file->info_len;
		unsigned char *buf , *BUF = buf = reinterpret_cast<unsigned char*>(alloca (LEN));
		ADD_U1   (&buf, &len, OP_EDONKEYHEADER);
		ADD_U1   (&buf, &len, OVERNET_SEARCH_RESULT);
		ADD_HASH (&buf, &len, p_hash);
		ADD_HASH (&buf, &len, reinterpret_cast<uint8_t*>(file->hash));
		memcpy   ( buf, file->info , len);
		Write (BUF, LEN);
		cnt ++;
		if (cnt == max) break;
		} while (0 == (err = cur->c_get (cur, &key, &val, DB_NEXT_DUP)));
	DB_c_close(cur);
	return true;
	}}}
bool     cKademlia::send_search_end        (uint8_t *p_hash          ) {{{ // (0x12)
	logSend('>', OVERNET_SEARCH_END, 22);
	unsigned char BUF[22];
	unsigned char *buf = BUF;
	unsigned len = 22;
	ADD_U1   (&buf, &len, OP_EDONKEYHEADER);
	ADD_U1   (&buf, &len, OVERNET_SEARCH_END);
	ADD_HASH (&buf, &len, p_hash);
	ADD_U4   (&buf, &len, 0);
	Write(BUF, 22);
	printf("Request result %15s:%5u\n", inet_ntoa(peer_ip), peer_port);
	return true;
	}}}
bool     cKademlia::send_Publish           (const uint8_t *key, size_t size, const char* name) {{{ // (0x13) Publish an meta description
	ASSERT(name != NULL);
	ASSERT(key  != NULL);
	unsigned len, LEN = len = 52+strlen(name);
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca(LEN));
	logSend('>', OVERNET_PUBLISH_REQ, len);
	ADD_U1     (&buf, &len, OP_EDONKEYHEADER);
	ADD_U1     (&buf, &len, OVERNET_PUBLISH_REQ);
	ADD_HASH   (&buf, &len, key);
	ADD_HASH   (&buf, &len, this_hash);
	ADD_U4	   (&buf, &len, 2);
	add_strTag (&buf, &len, FT_FILENAME, name);
	add_intTag (&buf, &len, FT_FILESIZE, size);
	ASSERT(len == 0);
	Write(BUF, LEN);
	return true;
	}}}
bool     cKademlia::send_Publish_ack       (const uint8_t * p_hash   ) {{{ // (0x14) acknowlegde the send_Publish
	unsigned len, LEN = len = 18;
	unsigned char BUF[18], *buf = BUF;
	ADD_U1     (&buf, &len, OP_EDONKEYHEADER);
	ADD_U1     (&buf, &len, OVERNET_PUBLISH_ACK);
	ADD_HASH   (&buf, &len, p_hash);
	ASSERT(len == 0);
	Write(BUF, LEN);
	logSend('>', OVERNET_PUBLISH_ACK, 0);
	return true;
	}}}
bool     cKademlia::send_Identify_reply    (void) {{{   // (0x15)
	logSend('>', OVERNET_IDENTIFY_RES, 25);
	unsigned int  len = 25;
	unsigned char BUF[25];
	unsigned char *buf = BUF;
	ADD_U1	(&buf, &len, OP_EDONKEYHEADER);
	ADD_U1	(&buf, &len, OVERNET_IDENTIFY_RES);
	ADD_HASH(&buf, &len, this_hash);
	ADD_U4  (&buf, &len, this_ip.s_addr);
	ADD_U2	(&buf, &len, this_port);
	Write(BUF, 25);
	return true;
	}}}
bool     cKademlia::send_Identify_ack      (void) {{{   // (0x16)
	logSend('>',OVERNET_IDENTIFY_ACK,  4);
	unsigned int  len = 4;
	unsigned char BUF[4];
	unsigned char *buf = BUF;
	ADD_U1	(&buf, &len, OP_EDONKEYHEADER);
	ADD_U1	(&buf, &len, OVERNET_IDENTIFY_ACK);
	ADD_U2	(&buf, &len, pref.ports.client);
	Write(BUF, 4);
	return true;
	}}}
bool     cKademlia::send_FireCon           (void) {{{ // (0x18) tODO
	logSend('>', OVERNET_FIREWALL_REQ,  0);
	return true;
	}}}
bool     cKademlia::send_FireCon_ack       (void) {{{ // (0x19) tODO
	logSend('>', OVERNET_FIREWALL_ACK,  0);
	return true;
	}}}
bool     cKademlia::send_FireCon_nack      (void) {{{ // (0x1A) tODO
	logSend('>', OVERNET_FIREWALL_NACK,  0);
	return true;
	}}}
bool     cKademlia::send_IP_query          (void) {{{ // (0x1B) Ask after BOOT what the other see as my ip
	unsigned char BUF[4];
	unsigned char *buf = BUF;
	unsigned len, LEN = len = 4;
	ADD_U1	(&buf, &len, OP_EDONKEYHEADER);
	ADD_U1	(&buf, &len, OVERNET_IP_REQ);
	ADD_U2	(&buf, &len, pref.ports.client);
	logSend('>', OVERNET_IP_REQ,  LEN);
	Write (BUF, LEN);
	return true;
	}}}
bool     cKademlia::send_IP_answer         (void) {{{ // (0x1C) Answer for send_IP_query
	unsigned char BUF[6];
	unsigned char *buf = BUF;
	unsigned len, LEN = len = 6;
	ADD_U1 (&buf, &len, OP_EDONKEYHEADER);
	ADD_U1 (&buf, &len, OVERNET_IP_RES);
	ADD_U4 (&buf, &len, peer_ip.s_addr);
	logSend('>', OVERNET_IP_RES,  LEN);
	Write(BUF, LEN);
	return true;
	}}}
bool     cKademlia::send_IP_end            (void) {{{ // (0x1D) returned after send_IP_answer
	unsigned char BUF[2];
	unsigned char *buf = BUF;
	unsigned len, LEN = len = 2;
	ADD_U1 (&buf, &len, OP_EDONKEYHEADER);
	ADD_U1 (&buf, &len, OVERNET_IP_ACK);
	logSend('>', OVERNET_IP_ACK,  LEN);
	Write(BUF, LEN);
	return true;
	}}}
bool     cKademlia::send_Identify          (void) {{{ // (0x1E) 
	unsigned char BUF[2];
	unsigned char *buf = BUF;
	unsigned len, LEN = len = 2;
	ADD_U1 (&buf, &len, OP_EDONKEYHEADER);
	ADD_U1 (&buf, &len, OVERNET_SEARCH_END);
//	logSend('>', 0x1E,  LEN);
	Write(BUF, LEN);
	return true;
	}}}
bool     cKademlia::send_Raw(struct in_addr ip, uint16_t port, const unsigned char *data, size_t len) {{{
	struct in_addr old_ip   = peer_ip;
	uint16_t       old_port = peer_port;
	peer_ip   = ip;
	peer_port = port;
	unsigned char cmd = data[1];
	if (cmd != 0x0E)
		if (logSend('>', cmd, len, true)) printf("RAW\n");
	Write(data, len);
	peer_ip   = old_ip;
	peer_port = old_port;
	return true;
	}}}
// }}}
// recv {{{
void	    cKademlia::parsePeer_lst          (unsigned char **buf , unsigned *len, unsigned cnt) {{{
	for (unsigned i = 0; i < cnt && *len >= 23; i++) {
		uint8_t  p_hash[KAD_HASH_LEN];
		GET_HASH (buf, len, p_hash);
		struct in_addr p_ip;
		p_ip.s_addr     = GET_U4 (buf, len);
		uint16_t p_port = GET_U2 (buf, len);
		uint8_t  p_kind = GET_U1 (buf, len);
		overnet.add(p_hash, p_ip.s_addr, p_port, p_kind);
		}
	}}}

bool     cKademlia::recv_Connect           (unsigned char *buf, size_t len) {{{ // (0x0A) request boot list
	uint8_t p_hash[KAD_HASH_LEN];
	GET_HASH(&buf, &len, p_hash);
	uint32_t p_ip   = GET_U4(&buf, &len);
	uint16_t p_udp  = GET_U2(&buf, &len);
	uint8_t  p_type = GET_U1(&buf, &len);
	if (peer_ip.s_addr   != p_ip ) printf("WARNING sender ip dos not match %15s:%5u\n", inet_ntoa(peer_ip), peer_port);
	if (peer_port != p_udp) printf("WARING sender port does not match %15s:%5u\n", inet_ntoa(peer_ip), peer_port);
	overnet.add(p_hash, peer_ip.s_addr, peer_port, p_type);
	send_Connect_reply ();
	return true;
	}}}
bool     cKademlia::recv_Connect_reply     (unsigned char *buf, size_t len) {{{ // (0x0B) return list of other clients
	unsigned cnt = GET_U2 (&buf, &len);
	parsePeer_lst (&buf, &len, cnt);
	status = 8; 
	return true;
	}}}
bool     cKademlia::recv_Publicize         (unsigned char *buf, size_t len) {{{ // (0x0C) publize my client
	uint8_t p_hash [HASH_LEN];
	GET_HASH(&buf, &len, p_hash);
	uint32_t p_ip   = GET_U4(&buf, &len);
	uint16_t p_udp  = GET_U2(&buf, &len);
	uint8_t  p_type = GET_U1(&buf, &len);
	if (pref.verbose & 1) {
		if (peer_ip.s_addr != p_ip ) printf("467 UGLY %15s:%5u\n", inet_ntoa(peer_ip), peer_port);
		if (peer_port 	   != p_udp) printf("468 UGLY %15s:%5u\n", inet_ntoa(peer_ip), peer_port);
		}
	overnet.add(p_hash, peer_ip.s_addr, peer_port, p_type);
	send_Publicize_ack();
	if (status == 0) status = 1;
	return true;
	}}}
bool     cKademlia::recv_Publicize_ack     (unsigned char *   , size_t    ) {{{ // (0x0D) ack the publize of my client
	if (status == 5) status = 8; 
	return true;
	}}}
bool     cKademlia::recv_Search            (unsigned char *buf, size_t len) {{{ // (0x0E) search after an HASH (user/file)
	uint8_t	 p_hash[KAD_HASH_LEN];
	unsigned cnt = GET_U1(&buf, &len);
	GET_HASH(&buf, &len, p_hash);
	if (cnt > OVERNET_BUCKET) cnt = OVERNET_BUCKET;
	send_Search_next(cnt, p_hash);
	return true;
	}}}
unsigned cKademlia::recv_Search_next       (unsigned char *buf, size_t len) {{{ // (0x0F) List of peers to try next time
	if (len == 0) return 0;
	result_countdown--;
	uint8_t info_hash[KAD_HASH_LEN];
	GET_HASH(&buf, &len, info_hash);	// <-- Move UP in bucket ?
	if(logSend('<', 0x0F, len, true)) printf(" %32s\n", hash_bin2hex(info_hash));
	unsigned cnt = GET_U1(&buf, &len);
	cPeerList *results = new cPeerList();
	for (unsigned i = 0; i < cnt && len >= 23; i++) {
		uint8_t  p_hash[KAD_HASH_LEN];
		GET_HASH (&buf, &len, p_hash);
		struct in_addr p_ip;
		p_ip.s_addr     = GET_U4 (&buf, &len);
		uint16_t p_port = GET_U2 (&buf, &len);
		uint8_t  p_kind = GET_U1 (&buf, &len);
		results->push_back(new cPeer(p_hash, p_ip.s_addr, p_port, p_kind));
		}
	uint8_t *key = info_hash;
	cSearchManager::processResponse(key, peer_ip.s_addr, peer_port, results);
	cPeerList::const_iterator itc;
	for(itc = results->begin(); itc != results->end(); itc++) delete (*itc);
	results->clear();
	delete results;
	return true;
	}}}
bool     cKademlia::recv_Search_info       (unsigned char *buf, size_t len) {{{ // (0x10) for what ?
	uint8_t p_hash[KAD_HASH_LEN];
	GET_HASH (&buf, &len, p_hash);
	unsigned extend = GET_U1(&buf, &len);
	unsigned min    = GET_U2(&buf, &len);
	unsigned max    = GET_U2(&buf, &len);
	send_Search_result(p_hash, min, max);
	return (extend == 0);
	}}}
bool     cKademlia::recv_Search_result     (unsigned char *buf, size_t len) {{{ // (0x11) Query successful return the stored data
	size_t LEN = len;
	unsigned char *BUF = buf;
	result_countdown--;
	uint8_t	info_hash [KAD_HASH_LEN];
	uint8_t peer_hash [KAD_HASH_LEN];
	GET_HASH (&buf, &len, info_hash);
	if (cSearchManager::isKeywordSearch(info_hash)) {
		size_t len_gui, LEN_gui = len_gui = 6 + LEN;
		unsigned char *buf_gui, *BUF_gui = buf_gui = reinterpret_cast<unsigned char*>(alloca (LEN_gui));
		ADD_U1 (&buf_gui, &len_gui, OP_EDONKEYHEADER);
		ADD_U4 (&buf_gui, &len_gui, (LEN_gui - 5));
		ADD_U1 (&buf_gui, &len_gui, CO_OV_SEARCH_RESULT);
		ASSERT(len_gui == LEN);
		memcpy(buf_gui, BUF, LEN);
		GUI_LIST akt->Write(BUF_gui, LEN_gui);
		}
	if(logSend('<', 0x11, len, true)) printf(" %32s\n", hash_bin2hex(info_hash));
	GET_HASH (&buf, &len, peer_hash);
	class sFile *file = forHash(peer_hash);
	if (file != NULL) {
		if (file->publish_next < currentTime || file->publish_count > 0) {
			if (file->publish_next < currentTime) {
				file->publish_next  = currentTime + 3600;
				file->publish_count = OVERNET_BUCKET; 
				}
			struct in_addr old_ip   = peer_ip;
			uint16_t       old_port = peer_port;
			send_Publish(file->hash, file->size, file->Name());
			peer_ip   = old_ip;
			peer_port = old_port;
			}
		}
	unsigned char *start = buf;
	class sTag *tag = parse_tag (buf, &len);
	if (tag == NULL) return false;
	if (tag->loc  != NULL) {{{
		insertFiles (info_hash, peer_ip, len, start);
		if (0 == strncmp("bcp://", tag->loc, strlen("bcp://"))) {{{
			char *link1 = tag->loc + strlen("bcp://");
//			printf("x bcp:// %s\n", link1);
			int cnt = 0;
			char *token[4];
			link1 = strtok(link1, ":");
			struct in_addr new_ip;
			uint16_t new_port = 0;
			while (cnt < 3 && link1 != NULL) {{{
				token[cnt++] = STRDUP (link1);
				link1 = strtok(NULL, ":");
				if (NULL == link1) break;
				}}}
			switch (cnt) {
				case 2: // printf("2 bcp:// %s - %s\n", token[0], tocken[1]);
					new_ip.s_addr = inet_addr (token[0]);
					new_port      = (atol(token[1]));
					if (new_ip.s_addr != 0 && (new_ip.s_addr & 0xFF000000) != 0) {
						addSource (new_ip, new_port, 0x02000000, true);
						insertFiles(info_hash, new_ip, 0 , NULL);
						}
					break;
				case 3: // printf("3 bcp:// %s - %s - %s\n", token[0], tocken[1], tocken[2]);
					// This is an overnet peer information with UDP port
					new_ip.s_addr = inet_addr (token[1]);
					new_port      = (atol(token[2]));
					if ((new_ip.s_addr & 0xFF000000) != 0)
						if (new_ip.s_addr != 0) overnet.add(hash_hex2bin(token[0]), new_ip.s_addr, new_port, 0);
					break;
				default:printf("n bcp:// %s\n", tag->loc + strlen("bcp://"));
					break;
				}
			for (int i = 0; i < cnt ; i++ ) free (token[i]);
			}}}
		else	printf("LOC : %s\n", tag->loc);
		delete tag;
		return true;
		}}}
	if (tag->name != NULL) {{{
		insertFiles (info_hash, peer_ip, len, start);
		if (tag->size > 0) {
//			printf("ed2k://|file|%32s|%u|%s\n", hash_bin2hex(info_hash), tag->size, tag->name);
			delete tag;
			return true;
			}
//		printf("Name: %s\n", tag->name);
//		printf("Size: %u\n", tag->size);
		delete tag;
		return false;
		}}}
	if (tag->ip.s_addr != 0 && tag->port != 0) {{{ // TODO
		printf("info => IP %15s:%5u\n", inet_ntoa(tag->ip), tag->port);
		delete tag;
		return true;
		}}}
	dump (BUF, LEN);
	delete tag;
	return false;
	}}}
bool     cKademlia::recv_search_end        (unsigned char *buf, size_t len) {{{ // (0x12) end the Query resonse (no more infos follow)
	uint8_t p_hash [HASH_LEN];
	GET_HASH(&buf, &len, p_hash);
	return true;
	}}}
bool     cKademlia::recv_Publish           (unsigned char *buf, size_t len) {{{ // (0x13) Publish an meta description
	uint8_t file_hash  [KAD_HASH_LEN];
	uint8_t p_hash[KAD_HASH_LEN];
	GET_HASH(&buf, &len, file_hash  );
	GET_HASH(&buf, &len, p_hash);
	unsigned char *start = buf;
	class sTag *tag = parse_tag (buf, &len);
	if (tag == NULL) return false;
	delete tag;
	insertFiles (file_hash, peer_ip, len, start);
	return true;
	}}}
bool     cKademlia::recv_Publish_ack       (unsigned char *   , size_t    ) {{{ // (0x14) acknowlegde the send_Publish
	return true;
	}}}
bool     cKademlia::recv_Identify_reply    (unsigned char *buf, size_t len) {{{ // (0x15)
	parsePeer_lst(&buf, &len, 1);
	send_Identify_ack ();
	return true;
	}}}
bool     cKademlia::recv_Identify_ack      (unsigned char *buf, size_t len) {{{ // (0x16) acknowlegde the send_Publish
	unsigned p_port = GET_U2(&buf, &len);
	addSource(peer_ip, p_port, 0x02000000);
	status = 8; 
	return true;
	}}}
bool     cKademlia::recv_FireCon           (unsigned char *   , size_t    ) {{{ // (0x18) i handle the firewalled server
	return true;
	}}}
bool     cKademlia::recv_FireCon_ack       (unsigned char *   , size_t    ) {{{ // (0x19)
	return true;
	}}}
bool     cKademlia::recv_FireCon_nack      (unsigned char *   , size_t    ) {{{ // (0x1A)
	return true;
	}}}
bool     cKademlia::recv_IP_query          (unsigned char *   , size_t    ) {{{ // (0x1B)
	send_IP_answer ();
	send_IP_end    ();
	return true;
	}}}
bool     cKademlia::recv_IP_answer         (unsigned char *buf, size_t len) {{{ // (0x1C) Answer for send_IP_query
//static struct in_addr last_ip;
        struct in_addr tmp_ip; 
        tmp_ip.s_addr = GET_U4( &buf, &len);
	cSocket::setLocalExternalIP(tmp_ip);
	this_ip.s_addr = tmp_ip.s_addr; // believe it for this connection
//	if (my_ip.s_addr != last_ip.s_addr ) {
//		if (protocol_logLevel == 9) printf("MY IP = %15s\n", inet_ntoa(my_ip));
//		last_ip.s_addr = my_ip.s_addr;
//		}
	next_ip_query = currentTime + 3600;
	return true;
	}}}
bool     cKademlia::recv_IP_end            (unsigned char *   , size_t    ) {{{ // (0x1D) returned after send_IP_answer
	next_ip_query = currentTime + 3600;
	return true;
	}}}
bool     cKademlia::recv_Identify          (unsigned char *   , size_t    ) {{{ // 0x1E
	send_Identify_reply ();
	return true;
	}}}
// }}}

bool	 cKademlia::logSend(char dir, unsigned cmd, size_t len,bool extra) {{{
	if (dir == '<' && cmd == 0x12) return false;
	if (dir == '<' && cmd == 0x11) return false;
	if (dir == '<' && cmd == 0x0F) return false;
	switch (protocol_logLevel) {
		default :
		case 8  : return false;
		case 2  : if (cmd == 0x0E || cmd == 0x1E) return false;
		case 1  : if (cmd == 0x12 || cmd == 0x10) return false;
		case 9  : 
		case 0  : break;
		}
	printf("%s %c 0x%04X overnet  [%15s](%9u) %-15s [%3u]", currentTime_str, dir, cmd, inet_ntoa(peer_ip), peer_port, op2str(cmd), len);
	if (!extra) printf("\n");
	return true;
	}}}

int      cKademlia::doRead_high (void) {{{
	if (read_len == 0) return 0;
	IP_is_OK(peer_ip);
	overnet.setAlive(peer_ip.s_addr, peer_port);
	if (next_ip_query < currentTime || this_ip.s_addr == 0) send_IP_query ();
	unsigned char *BUF = read_buf;
	size_t len, LEN = len = read_len;
	unsigned proto = GET_U1(&BUF, &LEN);
	if (proto == OP_EDONKEYHEADER) {
		unsigned cmd = GET_U1(&BUF, &LEN);
		if (cmd!=0x0F && cmd!=0x11) logSend('<', cmd, len);
		switch(cmd) {
		case 0x0A : recv_Connect	 (BUF, LEN); break;
		case 0x0B : recv_Connect_reply	 (BUF, LEN); break;
		case 0x0C : recv_Publicize	 (BUF, LEN); break;
		case 0x0D : recv_Publicize_ack	 (BUF, LEN); break;
		case 0x0E : recv_Search 	 (BUF, LEN); break;
		case 0x0F : recv_Search_next	 (BUF, LEN); break;
		case 0x10 : if (!recv_Search_info(BUF, LEN)) dump(BUF, LEN);
			    break;
		case 0x11 : recv_Search_result   (BUF, LEN); break;
		case 0x12 : recv_search_end	 (BUF, LEN); break;
		case 0x13 : recv_Publish	 (BUF, LEN); break;
		case 0x14 : recv_Publish_ack	 (BUF, LEN); break;
		case 0x15 : recv_Identify_reply	 (BUF, LEN); break;
		case 0x16 : recv_Identify_ack	 (BUF, LEN); break;
		case 0x18 : // firewall
			    break;
		case 0x1B : recv_IP_query	 (BUF, LEN); break;
		case 0x1C : recv_IP_answer	 (BUF, LEN); break;
		case 0x1D : break; // IP_end
		case 0x1E : recv_Identify	 (BUF, LEN); break;
		case 0x21 : break; // TODO WARNING unclear if this opcode is real or an bug
		default   : printf("Protocol %3u (0x%0X) (overnet) (%15s:%5u)\n", proto  , proto, inet_ntoa(peer_ip), peer_port);
			    printf("Command  %3u (0x%0X) (overnet)\n", cmd, cmd);
			    printf("len:     %u\n", LEN);
			    dump (BUF, LEN);
			    break;
			}
		}
	else	printf("Protocol %3u (0x%0X)\n", proto, proto);
	Read (read_buf, read_len);
	Work();
	return 0;
	}}}
void     cKademlia::Work        (void) {{{
	cSearchManager::jumpStart();
	overnet.onSmallTimer();
	load_Boot("kademlia.lst");
	if (last_query > currentTime) return;
	last_query = currentTime + 2;
	switch(status) {
	case  0 : memcpy (seek_hash, this_hash, KAD_HASH_LEN);
		  Init_Search();
		  status = 1;
	case  1 : {
		  cPeerList peers;
		  overnet.getBootstrapContacts (&peers, OVERNET_BUCKET);
		  cPeerList::const_iterator it;
		  for (it = peers.begin(); it != peers.end(); it++) {
			cPeer *c = *it;
			peer_port = c->udp;
			peer_ip.s_addr = c->ip;
			if(peer_port != 0 && IP_is_OK(peer_ip)) send_Connect ();
		  	}
		  }
		  status = 8;
	case  8 : break;
	default : printf("status %i\n", status);
		  return;
		  break;
		}
	}}}

