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

const int dlTimeout_hello	= 60;
const int dlTimeout_data	= 60;
const int dlTimeout_fileinfo	= 60;
int ulRang = - 5;

#define PACKET_LEN_SUB	5

unsigned int	 sSource::count0 = 0;
unsigned int	 sSource::count1 = 0;
unsigned int	 sSource::count = 0;
class sSource *sSource::first_Source = NULL;

class c32Key {
	public:
	char 		 key[32];
			 c32Key	() { bzero(key, 32); }
			 c32Key	(const uint8_t *a, const uint8_t *b) { 
				memcpy(key +  0, a, 16); 
				memcpy(key + 16, b, 16); 
			 	}
			 c32Key	(const class c32Key &b) { memcpy(key, b.key, 32); }
	class c32Key*   operator  =	(const class c32Key &b) { memcpy(key, b.key, 32); return this; }
	bool            operator <  (const class c32Key &B) const { return 0> memcmp(key, B.key, 32); }
	bool            operator >  (const class c32Key &B) const { return 0< memcmp(key, B.key, 32); }
	bool            operator <= (const class c32Key &B) const { return 0>=memcmp(key, B.key, 32); }
	bool            operator >= (const class c32Key &B) const { return 0<=memcmp(key, B.key, 32); }
	bool            operator == (const class c32Key &B) const { return 0==memcmp(key, B.key, 32); }
	bool            operator != (const class c32Key &B) const { return 0!=memcmp(key, B.key, 32); }
	};

class cFileInfo { 
public:
        size_t   file_size;
        uint8_t  file_info[256];
	size_t	 len;
	cFileInfo() { 
		file_size = 0;
		len = 0;
		}
	cFileInfo(size_t s, uint8_t *i) { 
		file_size = s;
		len = (s + PARTSIZE - 1) / PARTSIZE;
		memcpy(file_info, i, len);
		}
	cFileInfo (const class cFileInfo &b) {{{
		file_size = b.file_size;
		len = (file_size + PARTSIZE - 1) / PARTSIZE;
		if (len == 0) return;
		memcpy(file_info, b.file_info, len);
		}}}
	};

typedef std::map<class c32Key, class cFileInfo*> tInfo_File;
tInfo_File	info_File;


void File_info_set   (uint8_t *peer_hash, uint8_t *file_hash, size_t file_size, uint8_t *file_info) {{{
	return;
	if (file_info == NULL) return;
	class c32Key key(peer_hash, file_hash);
	class cFileInfo *val = new cFileInfo (file_size, file_info);
	info_File[key] = val;
	}}}
void File_info_del   (const uint8_t *peer_hash, const uint8_t *file_hash) {{{
	return;
	class c32Key key(peer_hash, file_hash);
	tInfo_File::iterator it = info_File.find(key);
	if (it == info_File.end()) return;
	delete it->second;
	it->second = NULL;
	info_File.erase(it);
	}}}
bool File_info_get   (const uint8_t *peer_hash, const uint8_t *file_hash, size_t file_size, uint8_t *file_info) {{{
	return false;
	if (file_info == NULL) return false;
	class c32Key key(peer_hash, file_hash);
	tInfo_File::const_iterator it = info_File.find(key);
	if (it == info_File.end()) return true;
	if (file_size != it->second->file_size) {
		File_info_del(peer_hash, file_hash);
		return false;
		}
	memcpy(file_info, it->second->file_info, it->second->len);
	return true;
	}}}

static int res_sources          (sSource *, unsigned char *buf, unsigned len) {{{
	tHash    hash;
	unsigned count;
	GET_HASH(&buf, &len, hash);
	count = GET_U2(&buf, &len);
	if (len != count * 12) {{{
		printf("%s %i len %i count %i USED %i\n", __FILE__, __LINE__, len, count, count*(4+2+4+2));
		return 0;
		}}}
	for (unsigned i = 0; i < count; i++) {{{
		if (len == 0) return 1;
		in_addr  c_ip;
		uint16_t c_port;
		in_addr  s_ip;
		uint16_t s_port;
		c_ip.s_addr = GET_U4(&buf, &len);
		c_port      = GET_U2(&buf, &len);
		s_ip.s_addr = GET_U4(&buf, &len);
		s_port      = GET_U2(&buf, &len);
		if (htonl(c_ip.s_addr) > LOW_ID) {
			if (0 == (c_ip.s_addr & 0x000000FF)) continue; // Who send that damm IP's
			addSource   (c_ip, c_port, 0x01000000, true);
			insertFiles (hash, c_ip, 0, NULL);
			}
		if (s_port == 4665) s_port = 4661;	// Kome this from UDP ?
		if (htonl (s_ip.s_addr)> LOW_ID) saveServer  (s_ip, s_port, 0, 0, false);
		}}}
	return 1;
	}}}

void sSource::Name (const char *NAME) {{{
	if (name != NULL) Free2 (name);
	name = STRDUP (NAME);
	}}}

void sSource::Init(void){{{
	ftp_aktive	= false;
	next_slot 	= 0;
	ask_info	= false;
	dl_file		= NULL;
	part_len	= 0;
	auto_select 	= false;
	next_Source 	= first_Source;
	first_Source 	= this;
	overnet 	= true;
	Type 		= tSource;
	dl_slot 	= -1;
	version 	= 0;
	dl_select 	= false;
	name 		= NULL;
	last_code 	= 0;
	extra_status 	= 0;
	have_slot 	= false;
	compress	= false;
	eMule 		= false;
	ask_sources 	= true;
	zip 		= NULL;
	recvData 	= false;
	ul_rang 	= 1000;
	dl_rang 	= 0;
	dl_von[0] = dl_von[1] = dl_von[2] = 0;
	dl_bis[0] = dl_bis[1] = dl_bis[2] = 0;
	logged = false ; // one time printf of sSource stats ?
	ratio = getSources_ratio (peer_ip);
	if (ratio < -10240 || ratio > 10240) log (1, "%15s init ratio %li\n", inet_ntoa(peer_ip), ratio);
	count++;
	dl_next_event = currentTime + 0;
	ul_next_event = currentTime + 0;
	bzero (dl_hash, HASH_LEN);
	bzero (ul_hash, HASH_LEN);
	}}}

sSource::sSource (class cSocket *Fd) : sSocket(Fd) {{{
	sort = 0;
	count0++;
	status = WAIT_HELLO;
	Init();
	if (mask != 0) doWork(mask);
	}}}
sSource::sSource (struct in_addr Ip, uint16_t PORT) : sSocket (Ip, PORT) {{{
	addSource   (Ip, PORT, 0x00000000, true);
	sort = 1;
	count1++;
	Init();
	if(!IP_is_OK(Ip)) {
		Close();
		return;
		}
	status = SEND_HELLO;
	setSources_aktive (peer_ip);
	struct eSources *info = Source_ip(peer_ip);
	if (info != NULL) version = info->version & 0xFFFFFF;
	else {
		version = 1044;
		overnet = true;
		}
	if (info != NULL) {
		if (info->version & 0x02000000) overnet = true;
		if ( overnet && version == 0) version =   61;
		if (!overnet && version == 0) version = 1661;
		}
	if (mask != 0) doWork(mask);
	}}}
sSource::~sSource    (void) {{{
	if(sort == 0) count0--;
	else		count1--;
	if (ul_rang <= 0) ulRang--;
	tHash hash;
	bzero (hash,  16);
	dlFile(hash);
	SOURCE_LIST if (akt->ul_rang > 0 && akt->ul_rang < 1000) akt->ul_rang--;
	if (zip != NULL)  zip = ende_Infalte (zip);
	if (connected == 1 && status != 2 && status != 9) {
//		printf("Close Aktive source\n");
		endSources_aktive (peer_ip);
		setSource_timeout (peer_ip, currentTime + 6 * 60);
		long ratio_ende = dlCount - ulCount;
		if (ratio_ende != 0) {
			chgSources_ratio (peer_ip, ratio_ende);
			if (ratio_ende < -10240 || ratio_ende > 10240) log (1, "%15s ende ratio %li\n", inet_ntoa(peer_ip), ratio_ende);
			}
		}
	else	Delete_src(peer_ip);
	if (status > 5) log (4, "del [%15s]\n", inet_ntoa (peer_ip));
	if (name    != NULL) Free2 (name);
	count--;
	// remove me from the file_list
	if (first_Source == this) {
		first_Source = next_Source;
		return;
		}
	class sSource *prev_file = first_Source;
	while (prev_file->next_Source != this && prev_file->next_Source != NULL)
	prev_file = prev_file->next_Source;
	ASSERT (prev_file != NULL);
	ASSERT (prev_file->next_Source == this);
	prev_file->next_Source = prev_file->next_Source->next_Source;
	}}}
void          sSource::setPartOK (const unsigned char *a, size_t LEN) {{{
	ASSERT (LEN == dl_file->part_count);
	if (dl_file == NULL) return;
	part_len = (LEN + 7) / 8;
	memcpy (part_ok, a, part_len);
	for (size_t i = 0; i < LEN; i++) if (partStatus(i)) dl_file->part_sources[i]++;
	}}}
bool          sSource::partStatus(uint32_t i) const {{{
	ASSERT(dl_file != NULL);
	if (part_len <= 0) return false;
	if (i > dl_file->part_count ) return false;
	uint32_t j = i / 8;
	uint8_t msk = 1 << (i&7);
	return (part_ok[j] & msk) == msk;
	}}}
class sFile* 	sSource::ulFile (void) 		  {{{ 
	return forHash(ul_hash); 
	}}}
void         	sSource::dlFile (const tHash &f	) {{{
	if (0 == memcmp(dl_hash, f, 16)) return;
	// Here there old dl file become unused
	if (dl_file != NULL) {{{
		dl_file->downloads--;
		if (part_len > 0) 
		  for (size_t i = 0; i < dl_file->part_count; i++)
		    if (partStatus(i)) dl_file->part_sources[i]--;
		bzero(part_ok, part_len);
		}}}
	// After this point the the new dl file is valid
	memcpy (dl_hash, f , HASH_LEN);
	dl_file = forHash(dl_hash);
	if (dl_file == NULL) return;
	dl_file->downloads++;
        char INFO[256];
        uint8_t *info = reinterpret_cast<uint8_t*>(INFO);
	if (File_info_get(peer_hash, dl_hash, dl_file->size, info)) 
		setPartOK(info, ((dl_file->size + PARTSIZE - 1) / PARTSIZE));
	}}}
void         	sSource::ulFile (const tHash &f	) {{{
	memcpy (ul_hash, f, HASH_LEN); 
	}}}
void sSource::moreData_recv       (uint32_t  von, uint32_t  bis) {{{
	log (4, "%s %i [%9u-%9u],[%9u-%9u],[%9u-%9u]\n",__FILE__,__LINE__,
			dl_von[0],dl_bis[0],
			dl_von[1],dl_bis[1],
			dl_von[2],dl_bis[2]);
	{ for (int j=0; j<3; j++) {
		if (dl_von[j] == von) dl_von[j] = bis;
		if (dl_von[j] >= dl_bis[j]) dl_von[j] = dl_bis[j] = 0;
		} }
	log (4, "%s %i [%9u-%9u],[%9u-%9u],[%9u-%9u]\n",__FILE__,__LINE__,
			dl_von[0],dl_bis[0],
			dl_von[1],dl_bis[1],
			dl_von[2],dl_bis[2]);
	int sum = 0;
	{ for (int j=0; j<3; j++) sum += (dl_bis[j]-dl_von[j]); }
	if (sum == 0) {
		ftp_aktive = false;
		log (1, "PART RESQUEST ended !!!\n");
		req_part_data ();
		}
	}}}
bool sSource::calcUpload_block    (size_t &von, size_t &bis) {{{
	class sFile *ul_file = ulFile();
	ASSERT (ul_hash != NULL);
	von = 0;
	bis = 0;
	do {
		if (ul_bis[0] == 0 || ul_von[0] >= ul_bis[0]) {
			ul_bis[0] = ul_bis[1];
			ul_von[0] = ul_von[1];
			ul_bis[1] = ul_bis[2];
			ul_von[1] = ul_von[2];
			ul_von[2] = 0;
			ul_bis[2] = 0;
			}
		if (ul_bis[0] == 0 || ul_von[0] >= ul_bis[0]) {
			ul_bis[0] = ul_bis[1];
			ul_von[0] = ul_von[1];
			ul_bis[1] = 0;
			ul_von[1] = 0;
			}
		if (ul_bis[0] == 0 || ul_von[0] >= ul_bis[0]) return false;
		const dtype *part = ul_file->firstBlock();
		while (part!=NULL && bis== 0) {
			if (part->stat == BLOCK_VERIFY) {
				if(ul_bis[0] > part->von && ul_von[0] < part->bis) {
					von = part->von;
					bis = part->bis;
					if(von < ul_von[0]) von = ul_von[0];
					if(bis > ul_bis[0]) bis = ul_bis[0];
					if(bis+1-von>10240) bis = von + 10240 - 1;
					if(bis >= ulFile()->Size()) bis = ulFile()->Size() - 1;
					}
				}
			part = ul_file->nextBlock();
			}
		if (bis==0 || von>=bis) ul_bis[0] = ul_von[0]=0;
		} while (bis==0);
	if (bis==ul_bis[0]) ul_bis[0]=von-1;
	if (von==ul_von[0]) ul_von[0]=bis+1;
	if (!ul_file->partStatus(von/PARTSIZE)) return false;
	if (!ul_file->partStatus(bis/PARTSIZE)) return false;
	return true;
	}}}

// send Handling {{{
int  sSource::client_info          (uint8_t opcode    ) {{{ /// 0x01/0x4C
	temp_buffer(tcp,56 + strlen(client_name) + ((opcode == OP_HELLO)?1:0));
	logSend ('>', this, opcode, false);
	ADD_U1     (&tcp.buf, &tcp.len, OP_EDONKEYHEADER);
	ADD_U4     (&tcp.buf, &tcp.len, tcp.LEN - PACKET_LEN_SUB);		// need to be set later
	ADD_U1     (&tcp.buf, &tcp.len, opcode);
	if (opcode == OP_HELLO) ADD_U1(&tcp.buf, &tcp.len, HASH_LEN);	// hash-length
	ADD_HASH   (&tcp.buf, &tcp.len, client_hash);			// client_hash
	ADD_U4     (&tcp.buf, &tcp.len, this_ip.s_addr);		// User_ID
	ADD_U2     (&tcp.buf, &tcp.len, pref.ports.client);		// Client_Port
	ADD_U4     (&tcp.buf, &tcp.len, 2);				// TAG count
	add_intTag (&tcp.buf, &tcp.len, CT_VERSION, my_version);
	add_strTag (&tcp.buf, &tcp.len, CT_NAME   , client_name);
	if (run_server) {{{
		ADD_U4 (&tcp.buf, &tcp.len, this_ip.s_addr);
		ADD_U2 (&tcp.buf, &tcp.len, pref.ports.server);
		}}}
	else {{{
		ADD_U4 (&tcp.buf, &tcp.len, 0);
		ADD_U2 (&tcp.buf, &tcp.len, 0);
		}}}
	ADD_U4     (&tcp.buf, &tcp.len, 0);
//	ADD_U2     (&tcp.buf, &tcp.len, 0);
	if(tcp.len != 0) {
		printf("tcp.len = %u\n", tcp.len);
		ASSERT(tcp.len == 0);
		}
	Write(tcp.BUF, tcp.LEN);
	return 1;
	}}}
int  sSource::C5_client_info       (bool start        ) {{{ // (0x01/0x02)
	logSend ('>', this, (start)?0xC501:0xC502, false);
	temp_buffer(tcp, 79);
	ADD_U1     (&tcp.buf, &tcp.len, OP_EMULEPROT);
	ADD_U4     (&tcp.buf, &tcp.len, tcp.LEN - PACKET_LEN_SUB);
	ADD_U1     (&tcp.buf, &tcp.len, (start)?0x01:0x02);
	ADD_U1     (&tcp.buf, &tcp.len, CURRENT_VERSION_SHORT);				//  1 Bytes
	ADD_U1     (&tcp.buf, &tcp.len, EMULE_PROTOCOL);				//  1 Bytes
	ADD_U4     (&tcp.buf, &tcp.len, 8);						//  4 Bytes
	add_intTag (&tcp.buf, &tcp.len, ET_COMPRESSION		, 1);			//  8 Bytes
	add_intTag (&tcp.buf, &tcp.len, ET_UDPPORT		, pref.ports.emule);	//  8 Bytes
	add_intTag (&tcp.buf, &tcp.len, ET_UDPVER		, 1);			//  8 Bytes (udp protocol version)
	add_intTag (&tcp.buf, &tcp.len, ET_SOURCEEXCHANGE	, 1);			//  8 Bytes (source-exchange)
	add_intTag (&tcp.buf, &tcp.len, ET_COMMENTS		, 2);			//  8 Bytes (comment an file)
	add_intTag (&tcp.buf, &tcp.len, ET_EXTENDEDREQUEST	, 1);			//  8 Bytes (extended requests)
	add_intTag (&tcp.buf, &tcp.len, ET_COMPATABLECLIENT	, 1);			//  8 Bytes (Compatibler Client 1=cDonkey)
	add_intTag (&tcp.buf, &tcp.len, "HASH"			, 1);
	assert(tcp.len == 0);
	Write (tcp.BUF, tcp.LEN);
	return 1;
	}}}
int  sSource::res_part_data        (void              ) {{{ // (0x46)
	sFile *file = ulFile();
	if (file == NULL) {
		if (logSend ('>', this, OP_SENDINGPART, true)) printf("invalid file\n");
		printf(">257<(null)\n");
		return 0;
		}
	size_t von = 0;
	size_t bis = 0;
	if (!calcUpload_block(von, bis)) {
		if (logSend ('>', this, OP_SENDINGPART, true)) printf("%32s no_blocks\n", hash_bin2hex(file->hash));
		return 0;
		}
	unsigned int cnt = bis - von + 1;
	if (bis < von) {{{
		printf("ERROR %s %i\n", __FILE__, __LINE__);
		return 0;
		}}}
	if (file->fd_in < 0) {{{
		printf("ERROR %s %i\n", __FILE__, __LINE__);
		return 0;
		}}}
	FD_LSEEK(file->fd_in, von, SEEK_SET);
	if (von + cnt > file->size) {
		printf("Request wrig sieze\n");
		return 0;
		}

	size_t len, LEN = len = 6 +  HASH_LEN + 4 + 4 + cnt;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca(LEN));
	ADD_U1   (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4   (&buf, &len, LEN - PACKET_LEN_SUB);
	ADD_U1   (&buf, &len, OP_HASHSETANSWER);
	ADD_HASH (&buf, &len, file->hash);
	ADD_U4   (&buf, &len, von);
	ADD_U4   (&buf, &len, bis);
	Bytef *file_pos = reinterpret_cast<Bytef*>(buf);
	if (!FD_READ2 (file->fd_in, buf, cnt)) {
		return 0;
		}
	len -= cnt;
	ASSERT(len == 0);
	if (compress && file->compress_failed < 10) {{{
		size_t len1, LEN1 = len1 = 6 +  HASH_LEN + 4 + 4 + cnt + 320;
		unsigned char  *buf1, *BUF1 = buf1 = reinterpret_cast<unsigned char*>(alloca(LEN1));
		ADD_U1   (&buf1, &len1, OP_EMULEPROT);
		ADD_U4   (&buf1, &len1, LEN - PACKET_LEN_SUB);
		ADD_U1   (&buf1, &len1, OP_COMPRESSEDPART);
		ADD_HASH (&buf1, &len1, file->hash);
		ADD_U4   (&buf1, &len1, von);
		ADD_U4   (&buf1, &len1, bis);
		uLong pack_len = len1;
		Bytef *pack = reinterpret_cast<Bytef*>(buf1);
		int err = compress2 (reinterpret_cast<Bytef*>(pack), &pack_len, reinterpret_cast<Bytef*>(file_pos), cnt, 9);
		if (err == Z_OK && pack_len < cnt) {
			if ((pack_len * 100) / cnt < 5) {{{ // safety check
				return 1;
				}}}
			if (logSend ('>', this, 0xC540, true)) 
				printf("%32s (0x40) (%9u-%9lu) %3lu\n", hash_bin2hex(file->hash), von, pack_len, (pack_len*100)/cnt);
			size_t len_real =  6 +  HASH_LEN + 4 + 4 + pack_len;
			ADD_U1   (&buf1, &len1, OP_EMULEPROT);
			ADD_U4   (&buf1, &len1, len_real - PACKET_LEN_SUB);
			ADD_U1   (&buf1, &len1, OP_COMPRESSEDPART);
			ADD_HASH (&buf1, &len1, file->hash);
			ADD_U4   (&buf1, &len1, von);
			ADD_U4   (&buf1, &len1, pack_len);
			file->ulCount (pack_len);
			Write (BUF1, len_real);
			return 1;
			}
		file->compress_failed ++;
		}}}
	// Not Compressed Handling
	if (logSend ('>', this, OP_SENDINGPART, true)) printf("%32s (%9i-%9i)\n", hash_bin2hex(file->hash), von, bis-von);
	file->ulCount (cnt);
	Write (BUF, LEN);
	return 1;
	}}}

int  sSource::req_part_data        (void              ) {{{ // (0x47)
	if (dl_file == NULL) return 0;
	if (dl_file->get_Completed()) return 0;
	if (!PartInfo()) {
		printf("ALERT: req_part_data source->PartInfo() == NULL\n");
		return 0;
		}
	bool proto_log = logSend ('>', this, OP_REQUESTPARTS, true);
	if (proto_log) {{{
		char *info = reinterpret_cast<char*>(alloca (dl_file->partCount() + 1));
		info[dl_file->partCount()]=0;
		char c = '0';
		for(uint32_t i=0; i<dl_file->partCount(); i++) {
			c='0';
			if (c == '0' && dl_file->partStatus (i)) c = '1';
			if (c == '0' && 	 partStatus (i)) c = '+';
			if (c == '1' && 	 partStatus (i)) c = '2';
			info[i] = c;
			}
		printf("%32s -> %s ", hash_bin2hex(dl_file->hash), info);
		}}}
	// 1. Make an Map of all useable blocks of size BLOCKSIZE
	// 2. The key of each block is the (source_count + request_count)
	// 3. Take 3 Blocks with the smallest key

	dl_von[0] = dl_bis[0] = 0;
	dl_von[1] = dl_bis[1] = 0;
	dl_von[2] = dl_bis[2] = 0;
	size_t dl_key[3] = {    0,    0,    0};
	size_t dl_use[3] = {40000,40000,40000};
	size_t dl_idx = 0;
	const dtype *part = NULL;
	size_t max_size = dl_file->Size();
	bool done = false;
	for (part = dl_file->firstBlock(); part != NULL && !done; part = dl_file->nextBlock()) {
//		printf("\nBLOK %9u - %9u - %9u\n", part->von, part->bis, part->stat);
		if (part->stat == BLOCK_RECIVE) continue;
		if (part->stat == BLOCK_VERIFY) continue;
		size_t von = part->von;
		size_t bis = part->bis;
		ASSERT(von <= bis);
		do {
			if (bis - von + 1 > BLOCKSIZE) bis = von + BLOCKSIZE - 1;
			if (bis > max_size) bis = max_size - 1;
			if (partStatus (von/PARTSIZE)) {
				size_t key = part->stat*20 + dl_file->part_sources[von/PARTSIZE];
				if (key < dl_use[dl_idx]) {
					dl_use[dl_idx] = key;
					dl_von[dl_idx] = von;
					dl_bis[dl_idx] = bis;
					dl_key[dl_idx] = part->stat;
					dl_idx++;
					if (dl_idx == 3) dl_idx = 0;
					}
				}
			von = bis + 1;
			bis = part->bis;
			if (dl_use[0] == 0 && dl_use[1] == 0 && dl_use[2] == 0) done = true;
			} while (von < part->bis && !done && von < max_size);
		}
	for (dl_idx = 0; dl_idx < 3; dl_idx++) {
		if (dl_use[dl_idx] == 40000) {
			dl_von[dl_idx] = dl_bis[dl_idx] = 0;
			continue;
			}	
		dtype changed;
		changed.von   = dl_von[dl_idx];
		changed.bis   = dl_bis[dl_idx];
		changed.stat  = dl_key[dl_idx] + 1;
		changed.since = currentTime;
		dl_file->Change_range (&changed);
		dl_bis[dl_idx]++;
		}
	if (dl_bis[0] == 0) {
		File_info_del (reinterpret_cast<const uint8_t*>(peer_hash), reinterpret_cast<const uint8_t*>(dl_hash));
		deleteFiles_ip (peer_ip);
		Close ();
		printf(" no-need\n");
		return 0; // if first is always set
		}
	if (proto_log) 
		printf("[%9i-%9i][%9i-%9i][%9i-%9i]\n", dl_von[0], dl_bis[0], dl_von[1], dl_bis[1], dl_von[2], dl_bis[2]);
	size_t len, LEN = len = 46;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca(LEN));
	ADD_U1  (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4  (&buf, &len, LEN - PACKET_LEN_SUB);
	ADD_U1  (&buf, &len, OP_REQUESTPARTS);
	ADD_HASH(&buf, &len, dl_hash);
	ADD_U4  (&buf, &len, dl_von[0]);
	ADD_U4  (&buf, &len, dl_von[1]);
	ADD_U4  (&buf, &len, dl_von[2]);
	ADD_U4  (&buf, &len, dl_bis[0]);
	ADD_U4  (&buf, &len, dl_bis[1]);
	ADD_U4  (&buf, &len, dl_bis[2]);
	ASSERT(len == 0);
	Write (BUF, LEN);
	dl_next_event = currentTime + dlTimeout_data * 10;
        status = FTP_AKTIVE;
	return 1;
	}}}
int  sSource::no_such_file         (const tHash &hash ) {{{ // (0x48)
	logSend ('>', this, OP_FILEREQANSNOFIL, false);
	unsigned LEN , len = LEN = 22;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca(LEN));
	ADD_U1  (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4  (&buf, &len, LEN - PACKET_LEN_SUB);
	ADD_U1  (&buf, &len, OP_FILEREQANSNOFIL);
	ADD_HASH(&buf, &len, hash);
	ASSERT(len == 0);
	Write(BUF, LEN);
	return 1;
	}}}
int  sSource::end_of_download      (void              ) {{{ // (0x49) o.p.
	logSend ('>', this, OP_END_OF_DOWNLOAD, false);
	unsigned LEN , len = LEN = 22;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca(LEN));
	ADD_U1  (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4  (&buf, &len, LEN - PACKET_LEN_SUB);
	ADD_U1  (&buf, &len, OP_END_OF_DOWNLOAD);
	ADD_HASH(&buf, &len, dl_hash);
	ASSERT(len == 0);
	Write(BUF, LEN);
	tHash hash;
	bzero (hash, HASH_LEN);
	dlFile (hash);
	return 1;
	}}}
int  sSource::CMD_req_shared_list  (void              ) {{{ // (0x4A) Currently unused
	logSend ('>', this, OP_ASKSHAREDFILES, 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 - PACKET_LEN_SUB);
	ADD_U1  (&buf, &len, OP_ASKSHAREDFILES);
	logSend ('>', this, OP_ASKSHAREDFILES, false);
	ASSERT(len == 0);
	Write(BUF, LEN);
	return 1;
	}}}
int  sSource::res_shared_files     (void              ) {{{ // (0x4B)  Currently unused (maybe never again used)
	size_t len, LEN = len = 4092;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca(LEN));
	size_t cnt = 0;
	ADD_U1 (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4 (&buf, &len, LEN - PACKET_LEN_SUB);
	ADD_U1 (&buf, &len, OP_ASKSHAREDFILESANSWER);
	ADD_U4 (&buf, &len, cnt);
	FILE_LIST {
		ADD_HASH   (&buf, &len, akt->hash);
		ADD_U4     (&buf, &len, this_ip.s_addr);	
		ADD_U2     (&buf, &len, pref.ports.client);
		ADD_U4 	   (&buf, &len, 2);
		add_strTag (&buf, &len, FT_FILENAME, akt->Name());
		add_intTag (&buf, &len, FT_FILESIZE, akt->Size());
		cnt++;
		}
	size_t len_real = LEN - len;
	buf = BUF;
	len = LEN;
	ADD_U4 (&buf, &len, len_real - PACKET_LEN_SUB);
	ADD_U1 (&buf, &len, OP_ASKSHAREDFILESANSWER);
	logSend ('>', this, OP_ASKSHAREDFILESANSWER, false);
	ADD_U4 (&buf, &len, cnt);
	Write(BUF, LEN);
	return 1;
	}}}
int  sSource::CMD_client_message   (const char *msg   ) {{{ // (0x4E) Currently unused
	size_t len, LEN = len = 6 + 2 + strlen(msg);
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca(LEN));
	ADD_U1 (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4 (&buf, &len, LEN - PACKET_LEN_SUB);
	ADD_U1 (&buf, &len, OP_MESSAGE);
	logSend ('>', this, OP_MESSAGE, false);
	ADD_S2 (&buf, &len, msg);
	ASSERT(len == 0);
	Write(BUF, LEN);
	return 1;
	}}}
int  sSource::req_file_status      (const tHash &hash ) {{{ // (0x4F) o.p. <HASH>
	if (tcp_server != NULL) {
		if (tcp_server->peer_ip.s_addr == peer_ip.s_addr) assert(0);
		}
	tHash old_dl;
	memcpy(old_dl, hash, 16);
	dlFile(hash);
	if (dl_file == NULL) return 0;
	if (PartInfo()) {
		for(uint32_t i=0; i<dl_file->partCount(); i++) {
			if (!dl_file ->partStatus (i) && partStatus(i)) {
				if (!have_slot) req_slot();
				else		req_part_data();
				return 1;
				}
			}
		}
	dlFile(old_dl);

	if (logSend('>', this, OP_SETREQFILEID, true)) printf("%32s\n", hash_bin2hex(hash));
	unsigned LEN , len = LEN = 22;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca(LEN));
	ADD_U1  (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4  (&buf, &len, LEN - PACKET_LEN_SUB);
	ADD_U1  (&buf, &len, OP_SETREQFILEID);
	ADD_HASH(&buf, &len, hash);
	Write(BUF, LEN);
	return 1;
	}}}
int  sSource::res_part_status      (tHash &hash       ) {{{ // (0x50)
	const class sFile *file = forHash(hash);
	if (NULL == file) return 0;
	if (NULL == File_idx (hash, peer_ip)) {
		insertFiles(hash, peer_ip, 0, NULL);
		}
	if (logSend ('>', this, OP_FILESTATUS, true)) printf("%32s\n", hash_bin2hex(file->hash));
	const uint8_t  *file_stat = file->listPartStatus();
	int 		cnt = (file->partCount()+7)/8;
	size_t len, LEN = len = 6 + HASH_LEN + 2 + cnt;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca(LEN));
	ADD_U1   (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4   (&buf, &len, LEN - PACKET_LEN_SUB);
	ADD_U1   (&buf, &len, OP_FILESTATUS);
	ADD_HASH (&buf, &len, file->hash);
	ADD_U2   (&buf, &len, file->partCount());
	for (int j=0; j < cnt; j++) ADD_U1 (&buf, &len, file_stat[j]);
	ASSERT(len == 0);
	Write (BUF, LEN);
	log (4, "file :  ");
	for(uint32_t i=0; i < file->partCount(); i++) log (4, "%c", file->partStatus(i)?'1':'0');
	log (4, "\n");
	return 1;
	}}}
int  sSource::req_part_hash        (const tHash &hash ) {{{ // (0x51) o.p. <HASH>
	if (logSend('>', this, OP_HASHSETREQUEST, true)) printf("%32s\n", hash_bin2hex(hash));
	unsigned LEN , len = LEN = 22;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca(LEN));
	ADD_U1  (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4  (&buf, &len, LEN - PACKET_LEN_SUB);
	ADD_U1  (&buf, &len, OP_HASHSETREQUEST);
	ADD_HASH(&buf, &len, hash);
	Write(BUF, LEN);
	return 1;
	}}}
int  sSource::res_part_hash        (void              ) {{{ // (0x52)
	if (ulFile() == NULL) return 1;		// Maybe we deleted this file in the meantime
	const class sFile *file = ulFile();
	if (!file->havePartHash ()) return 1;		// No error if we do not have an part hash for that file

	int cnt = file->partCount();
	size_t len, LEN = len = 6 + HASH_LEN + 2 + cnt * HASH_LEN;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca(LEN));
	ADD_U1  (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4  (&buf, &len, LEN - PACKET_LEN_SUB);
	ADD_U1  (&buf, &len, OP_HASHSETANSWER);
	logSend ('>', this,  OP_HASHSETANSWER, false);
	ADD_HASH(&buf, &len, file->hash);
	ADD_U2  (&buf, &len, cnt);
	for (int i=0; i < cnt; i++) ADD_HASH(&buf, &len, *file->getPartHash (i));
	ASSERT (len == 0);
	Write (BUF, LEN);
	return 1;
	}}}
int  sSource::req_slot             (void              ) {{{ // (0x54) o.p. (null)
	if (next_slot >= currentTime) return 1;
	next_slot = currentTime + 300;
	logSend('>', this, OP_STARTUPLOADREQ, false);
	unsigned LEN, len = LEN = 22;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca(LEN));
	ADD_U1  (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4  (&buf, &len, LEN - PACKET_LEN_SUB);
	ADD_U1  (&buf, &len, OP_STARTUPLOADREQ);
	ADD_HASH(&buf, &len, dl_hash);
	ASSERT(len == 0);
	Write(BUF, LEN);
	return 1;
	}}}
int  sSource::CMD_slot_available   (void              ) {{{ // (0x55) o.p. (null)
	logSend('>', this, OP_ACCEPTUPLOADREQ, false);
	unsigned 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 - PACKET_LEN_SUB);
	ADD_U1(&buf, &len, OP_ACCEPTUPLOADREQ);
	Write(BUF, LEN);
	return 1;
	}}}
int  sSource::res_slot_close       (void              ) {{{ // (0x57) o.p. (null)
	logSend('>', this, OP_OUTOFPARTREQS, false);
	unsigned 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 - PACKET_LEN_SUB);
	ADD_U1(&buf, &len, OP_OUTOFPARTREQS);
	Write(BUF, LEN);
	return 1;
	}}}
int  sSource::req_fileName         (const tHash &hash ) {{{ // (0x58) o.p. <HASH>
	tHash old_dl;
	memcpy(old_dl, hash, 16);
	dlFile(hash);
	if (dl_file == NULL) return 0;
	if (PartInfo()) {
		for(uint32_t i=0; i<dl_file->partCount(); i++) {
			if (!dl_file ->partStatus (i) && partStatus(i)) {
				if (!have_slot) req_slot();
				else		req_part_data();
				return 1;
				}
			}
		}
	dlFile(old_dl);
	if (logSend('>', this, OP_FILEREQUEST, true)) printf("%32s\n", hash_bin2hex(hash));
	unsigned LEN , len = LEN = 22;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca(LEN));
	ADD_U1  (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4  (&buf, &len, LEN - PACKET_LEN_SUB);
	ADD_U1  (&buf, &len, OP_FILEREQUEST);
	ADD_HASH(&buf, &len, hash);
	ASSERT(len == 0);
	Write(BUF, LEN);
	return 1;
	}}}
int  sSource::res_offer_file       (const sFile *file ) {{{ // (0x59)
	if (logSend('>', this, OP_FILEREQANSWER, true))
		printf("%32s\n", hash_bin2hex(file->hash));
	const char *f_name = file->Name();
	f_name = "";
	size_t len, LEN = len = 6 + HASH_LEN + 2 + strlen (f_name);
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca(LEN));
	ADD_U1  (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4  (&buf, &len, LEN - PACKET_LEN_SUB);
	ADD_U1  (&buf, &len, OP_FILEREQANSWER);
	ADD_HASH(&buf, &len, file->hash);
	ADD_S2  (&buf, &len, f_name);
	ASSERT(len == 0);
	Write(BUF, LEN);
	return 0;
	}}}
int  sSource::C5_res_rang          (uint8_t rang      ) {{{ // (0x60) o.p. <RANG(2)><padding(10)>
	if (logSend ('>', this,0xC560, true)) printf("%hi\n", rang);
	unsigned LEN , len = LEN = 18;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca(LEN));
	ADD_U1 (&buf, &len, OP_EMULEPROT);
	ADD_U4 (&buf, &len, LEN - PACKET_LEN_SUB);
	ADD_U1 (&buf, &len, OP_QUEUERANKING);
	ADD_U2 (&buf, &len, rang);
	ADD_U4 (&buf, &len, 0);
	ADD_U4 (&buf, &len, 0);
	ADD_U2 (&buf, &len, 0);
	ASSERT(len == 0);
	Write(BUF, LEN);
	return 1;
	}}}
int  sSource::C5_req_sources       (const tHash &hash ) {{{ // (0x81) o.p. <HASH>
	tHash Hash;
	memcpy(Hash,hash,HASH_LEN);
	nextFile (hash, true);	// <- set cursor_count
	if (cursor_count > 512) return 1;
	if(logSend ('>', this , 0xC581, true)) printf("%32s\n", hash_bin2hex(hash));
	unsigned LEN , len = LEN = 22;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca(LEN));
	ADD_U1  (&buf, &len, OP_EMULEPROT);
	ADD_U4  (&buf, &len, LEN - PACKET_LEN_SUB);
	ADD_U1  (&buf, &len, OP_REQUESTSOURCES);
	ADD_HASH(&buf, &len, hash);
	ASSERT(len == 0);
	Write(BUF, LEN);
	return 1;
	}}}
// }}}
// recv Handling {{{
int  sSource::REQ_client_info      (sPacket *packet) {{{ // (0x01)
	bool log = logSend ('<', this, OP_HELLO, true);
	unsigned char hash_len = packet->GET_U1 ();			// hash länge
	if (hash_len != HASH_LEN) {
		if (log)
		printf("Wrong hash len\n");
		return -1;
		}
	Code(OP_HELLO);
	packet->GET_HASH(peer_hash);
	setSources_hash(peer_ip, peer_hash);
	packet->GET_U4 ();	// user->idx_old
	uint16_t client_port = packet->GET_U2 ();	// user->port
	addSource   (peer_ip, client_port, 0x00000000, true);
	class sTag *tag = parseTag (packet);
	bool added = false;
	if (tag != NULL) {
		if (tag->port > 0) {
			added = true;
			addSource   (peer_ip, tag->port, 0x00000000, true);
			}
		if (tag->version != 0) version = tag->version;
		version = tag->version;
		overnet = false;
		if (tag->version ==   43) overnet = true;
		if (tag->version == 1044) overnet = true;
		if (tag->name != NULL) 	Name (tag->name);
		delete tag;
		}
	
	if (name == NULL) Name ("(null)");
	if (log) printf("v.%4u NAME: %s e:%i", version, name, eMule);
	if (log) printf(" LEN: %i\n", packet->Unused());

	if (status == WAIT_HELLO) {
		client_info(0x4C);
		status = WAIT_INFO;
		Code(0x4C);
		if (peer_hash[5]==14 && peer_hash[14]==111) C5_client_info (true);
		}
	return 0;
	}}}
int  sSource::RES_part_data        (sPacket *packet) {{{ // (0x46)
	unsigned int off_start,off_ende;
	// Maybe changed download file (late response)
	tHash hash;
	packet->GET_HASH (hash);
	sFile *file = forHash(hash);
	if (file == NULL) return 0;
	if (file != dl_file) {
		printf("WARNING 584 (%s !=", hash_bin2hex(hash));
		printf("%s)\n", hash_bin2hex(dl_hash));
		}
	if (file == NULL) return 0;
	off_start = packet->GET_U4 ();
	off_ende  = packet->GET_U4 ();
	if(logSend ('<', this, OP_SENDINGPART, true))
		printf("%32s (%9i-%9i)\n", hash_bin2hex (hash), off_start, off_ende - off_start);
	if ( !logged ) {{{	// debug log
		char logbuf[256];
		snprintf(logbuf, 255, "New Download from %15s (E3:edonkey) Ratio: %li Emule: %d Overnet: %d\n",
			inet_ntoa(peer_ip), ratio, is_eMule(), is_Overnet() );
		log(1, logbuf);
		GUI_LIST akt->GUI_statusMessage(logbuf);
		logged = true;
		}}}
	recvData = true;
// printf("RECV %15s - %32s - %9u - %9u\n", inet_ntoa(peer_ip), hash_bin2hex(dl_hash), off_start, off_ende);
	int ret = file->recvPart (off_start,off_ende, packet->DATA ());
	this->dl_count += (off_ende - off_start) ;	// update dl_count
	if (ret == -9) {
		// block already exists
		// cancel request other block
		req_part_data ();
		Code(OP_SENDINGPART);
		return 1;
		}
	moreData_recv (off_start, off_ende);
	Code(OP_SENDINGPART);
	return 1;
	}}}
int  sSource::REQ_part_data        (sPacket *packet) {{{ // (0x47)
	tHash hash;
	packet->GET_HASH (hash);
	ulFile (hash);
	if (ulFile() == NULL) return 0;
	ul_von[0] = packet->GET_U4 ();
	ul_von[1] = packet->GET_U4 ();
	ul_von[2] = packet->GET_U4 ();
	ul_bis[0] = packet->GET_U4 ();
	ul_bis[1] = packet->GET_U4 ();
	ul_bis[2] = packet->GET_U4 ();
	if(logSend ('<', this, OP_REQUESTPARTS, true))
	 printf("%32s [%9u-%9u][%9u-%9u][%9u-%9u]\n", hash_bin2hex(ul_hash),ul_von[0],ul_bis[0],ul_von[1],ul_bis[1],ul_von[2],ul_bis[2]);
	extraSend (OP_SENDINGPART);
	ul_next_event = currentTime + 0;
	return 1;
	}}}
int  sSource::RES_no_such_file     (sPacket *packet) {{{ // (0x48)
	tHash hash;
	packet->GET_HASH(hash);
	if (logSend ('<', this, OP_FILEREQANSNOFIL, true)) printf("%32s\n", hash_bin2hex(hash));
	deleteFiles_ip(peer_ip);
	last_send = last_read = 0;
	return 1;
	}}}
int  sSource::RES_dl_ende          (sPacket *packet) {{{ // (0x49)
	if (packet == NULL) return 0;
	tHash hash;
	packet->GET_HASH (hash);
	extraSend (0);
	return 1;
	}}}
int  sSource::REQ_shared_files     (sPacket *      ) {{{ // (0x4A)
	extraSend (OP_ASKSHAREDFILESANSWER);
	return 1;
	}}}
int  sSource::RES_shared_files     (sPacket *packet) {{{ // (0x4B)
	int cnt = packet->GET_U4();
	Code(-1);		// maybe we got an defekt answer
	while(--cnt>=0) {
		tHash hash;
		packet->GET_HASH(hash);			// HASH
		packet->GET_U4 ();			// IP
		packet->GET_U2 ();			// Port
		class sTag *tag = parseTag (packet);
		if (tag == NULL) return 0;
		if (tag->name != NULL) {
			char buffer[512];
			int l = snprintf(buffer,511, "%16s %s %u\n", hash_bin2hex (hash), inet_ntoa(peer_ip),peer_port);
			buffer[l] = 0;
			printf("%s", buffer);
			}
		delete tag;
		}
	Code(OP_ASKSHAREDFILESANSWER);
	return 1;
	}}}
int  sSource::RES_client_login     (sPacket *packet) {{{ // (0x4C)
	bool log = logSend ('<', this, OP_HELLOANSWER, true);
	if (packet->Unused() < 16 + 4 + 2) {
		if(log) printf("DEFEKT\n");
		Close();
		return 0;
		}
	tHash hash;
	struct in_addr new_ip;
	packet->GET_HASH(hash);
	setSources_hash(peer_ip, peer_hash);
	packet->GET_U4  ();
	uint16_t new_port = packet->GET_U2 ();
	class sTag *tag = parseTag (packet);
	if (tag != NULL) {
		if (tag->port > 0) addSource   (peer_ip, tag->port, 0x00000000, true);
		if (new_port == 0) new_port = tag->port;
		version = tag->version;
		if (tag->version ==   43) overnet = true;
		if (tag->version == 1044) overnet = true;
		if (tag->name != NULL) 	Name (tag->name);
		if (log) printf("v.%4u NAME %s LEN: %i\n", tag->version, tag->name, packet->Unused());
		delete tag;
		}
	else	printf("(noTag)\n");

	new_ip.s_addr = packet->GET_U4 ();
	new_port      = packet->GET_U2 ();
	if (new_port == 4665) new_port = 4661;	// Client got me via UDP i think
	if(new_ip.s_addr != 0) {
		if (htonl(new_ip.s_addr) > LOW_ID) {
			if ((new_ip.s_addr & 0xFF000000) != 0 && (new_ip.s_addr & 0x000000FF) != 0) 
				saveServer (new_ip, new_port, 0, 0, false);
			}
		}
	Code(OP_HELLOANSWER);
	return 0;
	}}}
int  sSource::RES_changed_server   (sPacket *      ) {{{ // (0x4D)
	return 1;	// Heist vermutlich Changed IDX (von low auf heigh);
/*
	struct in_addr new_ip;
	uint32_t new_idx = packet->GET_U4 ();
	new_ip.s_addr 	 = packet->GET_U4 ();
	return 0;
*/
	}}}
int  sSource::RES_source_message   (sPacket *packet) {{{ // (0x4E) (log.info)
	if (packet == NULL) return 0;
	char *msg = packet->GET_S2 ();
	printf("SOURCE %15s <- %s\n", inet_ntoa (peer_ip), msg);
	Free2 (msg);
	return 1;
	}}}
int  sSource::REQ_status           (sPacket *packet) {{{  // (0x4F)
	tHash hash;
	packet->GET_HASH (hash);
	class sFile *file = forHash(hash);
	if (file == NULL) {
//		if(logSend ('<', this, OP_SETREQFILEID, true)) printf("%32s BAD len:%i\n", hash_bin2hex(hash), packet->Unused());
		no_such_file (hash);
		return 1;
		}
	if(logSend ('<', this, OP_SETREQFILEID, true)) printf("%32s OK len:%i\n", hash_bin2hex(hash),packet->Unused());
	res_part_status (hash);
	return 1;
	}}}

int  sSource::RES_status           (sPacket *packet) {{{ // (0x50) Return file Status
	if (ftp_aktive) return 1;
	if (packet->Unused() <= 18) {{{
		printf("MALFORMED PACKET 0x50 from %s LEN %u\n", inet_ntoa(peer_ip), packet->Unused());
		return 0;
		}}}
	tHash hash;
	if (packet->Unused() < 18) {{{
		printf("MALFORMED PACKET 0x50 from %15s LEN %5u VERSION %4u NAME %s\n", inet_ntoa(peer_ip), packet->Unused(), version, name);
		IP_block (peer_ip);
		Close();
		return 0;
		}}}
		       packet->GET_HASH(hash);
	uint16_t cnt = packet->GET_U2();
	class sFile *file = forHash(hash);
	if(file == NULL) {{{
		if (logSend ('<', this, OP_FILESTATUS, true)) printf("%32s -> BAD(1)\n", hash_bin2hex(hash));
		return 0;
		}}}
	size_t file_cnt = (file->Size()+PARTSIZE-1)/PARTSIZE;
	if (cnt != file_cnt && cnt != 0) {{{
		if (logSend ('<', this, OP_FILESTATUS, true)) printf("%32s -> BAD(2)\n", hash_bin2hex(hash));
		IP_block (peer_ip);
		return 0;
		}}}
	unsigned bytes = (file->partCount() + 7) / 8;
	unsigned char *temp_info = reinterpret_cast<unsigned char *>(alloca (bytes));
	char          *info      = reinterpret_cast<char          *>(alloca (file->partCount() + 1));
	if (cnt == 0) memset (temp_info, 0xff             , bytes);
	else	      memcpy (temp_info, packet->DATA()   , bytes);
	bool use = false;
	info[file->partCount()]=0;
	char c;
	for(uint32_t i=0; i<file->partCount(); i++) {
		c = (file->partStatus(i))?'1':'0';
		bool source_have = (0 != ((temp_info[i / 8]) & (1 << (i & 7))));
		if (c == '0' && source_have) { c = '+';  use = true; }
		if (c == '1' && source_have)   c = '2';
		info[i] = c;
		}
	if (!use) {{{
		if (logSend ('<', this, OP_FILESTATUS, true)) printf("%32s -> (%2u)%s NO-USE\n", hash_bin2hex(hash), cnt, info);
		setSource_timeout(peer_ip, currentTime + 3600);
		// XXX if happens -> we already have all the parts(?)
		/* ASSERT(cnt != 0); */
		return 1;
		}}}

	dlFile(hash);
	setPartOK(temp_info, file->partCount());
	File_info_set(peer_hash, hash, file->size, reinterpret_cast<uint8_t*>(part_ok));
	if (logSend ('<', this, OP_FILESTATUS, true)) printf("%32s -> (%2u)%s USE\n", hash_bin2hex(hash), cnt, info);
	insertFiles (hash, peer_ip, 0, NULL);
	if (!have_slot) {
		req_slot();
		return 1;
		}
	if (!file->havePartHash()) req_part_hash(hash);
	req_part_data();
	return 1;
	}}}
int  sSource::REQ_part_hash        (sPacket *packet) {{{ // (0x51)
	tHash hash;
	packet->GET_HASH(hash);
	ulFile (hash);
	if (ulFile() != NULL) extraSend (OP_HASHSETREQUEST);
	return 1;
	}}}
int  sSource::RES_part_hash        (sPacket *packet) {{{ // (0x52) Return partial hashes
	unsigned short cnt;
	tHash hash_file;
	tHash hash_test;
	MD4_CTX ctx;
	MD4Init (&ctx);
	packet->GET_HASH(hash_file);
	cnt = packet->GET_U2();
	bool log = logSend ('<', this, OP_HASHSETANSWER, true);
	if (log) printf("%32s\n", hash_bin2hex(hash_file));
	if (version == 666) return 0;
	printf("ORIG_HASH  = %s\n", hash_bin2hex (hash_file));
	tHash *hash_part = reinterpret_cast<tHash*>(alloca (cnt * sizeof(tHash)));
	sFile *file = forHash(hash_file);
	for(unsigned i = 0; i < cnt; i++) {
		packet->GET_HASH(hash_part[i]);
		if (version == 666) {
			printf("%2i - %32s", i, hash_bin2hex(hash_part[i]));
			if (file != NULL)
				if (file->havePartHash () && file->partCount() > i)
					printf(" + %32s => %i\n", hash_bin2hex(*file->getPartHash(i)),
						memcmp(hash_part[i], *file->getPartHash(i), HASH_LEN));
			if (log) printf("\n");
			}
		MD4Update (&ctx, hash_part[i], sizeof(tHash));
		}
	MD4Final  (hash_test, &ctx);
	if (version == 666) printf("++  - %32s\n", hash_bin2hex(hash_test));
	if (file == NULL) 		{
		return 0;	// We do not need this part hash
		}
	if (file->havePartHash ()) 	{
		return 1;	// We have yet now an valid set
		}
	if (file->partCount() != cnt)   {
		version = 666;
		return 1;
		}
	if (0 != memcmp (hash_file, hash_test, HASH_LEN)) {
		version = 666;
		Code(-1);
		return 0;
		}
	file->savePartHash (hash_part);
	return 1;
	}}}
int  sSource::REQ_slot             (sPacket *packet) {{{ // (0x54)
	if (logSend('<', this, OP_STARTUPLOADREQ, true)) printf("len %i\n", packet->Unused());
	extraSend (OP_ACCEPTUPLOADREQ);
	return 1;
	}}}
int  sSource::RES_slot_avail       (sPacket *      ) {{{ // (0x55)
	logSend('<', this, OP_ACCEPTUPLOADREQ, false);
	Code(OP_ACCEPTUPLOADREQ);
	have_slot = true;	// If more than 2 packet's come
	if (NULL != dl_file && part_len > 0) req_part_data  ();
	// This is safer that it is not missed
	return 1;
	}}}
int  sSource::RES_slot_release     (sPacket *      ) {{{ // (0x56)
	Code(OP_CANCELTRANSFER);
	return 1;
	}}}
int  sSource::RES_slot_close       (sPacket *      ) {{{ // (0x57)
	Code(OP_OUTOFPARTREQS);
	return 1;
	}}}
int  sSource::REQ_fileName         (sPacket *packet) {{{ // (0x58)
	tHash hash;
	packet->GET_HASH(hash);
	const sFile *file = forHash(hash);
	if (file == NULL) {
//		if(logSend ('<', this, OP_FILEREQUEST, true)) printf("%32s BAD\n", hash_bin2hex(hash));
		return 0;
		}
	if(logSend ('<', this, OP_FILEREQUEST, true)) printf("%32s OK\n", hash_bin2hex(hash));
	if (eMule) {
		res_offer_file (file);	// <- Safe UL traffic is only information, but brake eMule
		res_part_status(hash);
		}
	return 1;
	}}}
int  sSource::RES_fileName         (sPacket *packet) {{{ // (0x59)
	tHash hash;
	packet->GET_HASH(hash);
	const sFile *file = forHash(hash);
	if (file == NULL) return 1;
	char *filename = packet->GET_S2 ();
//	printf("%32s => %s\n", hash_bin2hex(hash), file->Name());
	Free2 (filename);
	if(logSend ('<', this, OP_FILEREQANSWER, true)) printf("%32s OK\n", hash_bin2hex(hash));
	file = nextFile_search ();
	if (file != NULL && eMule) C5_req_sources (file->hash);
	return 1;
	}}}
int  sSource::RES_5b_unknown       (sPacket *packet) {{{ // (0x5B)
	tHash hash;
	packet->GET_HASH(hash);
	if (logSend ('<', this, OP_CHANGE_SLOT, true)) printf("%32s\n", hash_bin2hex(hash));
	return 1;
	}}}
int  sSource::RES_5c_unknown       (sPacket *packet) {{{ // (0x5C)
	uint32_t idx = packet->GET_U4 ();
	if(logSend ('<', this, OP_QUEUERANK, true)) printf("%4u\n", idx);
	return 1;
	}}}
int  sSource::C5_client_info       (sPacket *packet) {{{ // (0x01/0x02)
	unsigned short_ver = packet->GET_U1 ();
	unsigned emule_ver = packet->GET_U1 ();
	if (logSend ('<', this, 0xC501, true)) printf("%u - %u\n", short_ver, emule_ver);
	setSources_version(peer_ip, 0x01000000 | short_ver);
	eMule = true;
	if (packet == NULL) {{{
		printf("ERROR %s %i\n", __FILE__, __LINE__);
		return 0;
		}}}
	class sTag *tag = parseTag (packet);
	if (tag != NULL) {
		Compress (tag->compress==1);
		setSources_udp(peer_ip, tag->client_udp);
		delete tag;
		}
	if (eMule && packet->Command() == OP_EMULEINFO) C5_client_info  (false);
	return 1;
	}}}
int  sSource::C5_compressed_data   (sPacket *packet) {{{ // (0x40)
#define OUT_CNT	10240
	bool proto_log = logSend ('<', this, 0xC540, true);
	if (NULL == zip) zip = init_Infalte ();
	if (zip == NULL) {{{
		printf("ERROR %s %i\n", __FILE__, __LINE__);
		return 0;
		}}}
	tHash hash;
	packet->GET_HASH (hash);
	if (proto_log) printf ("%32s ", hash_bin2hex (hash));
	sFile *file = forHash(hash);
	if (file == NULL) return 1;
	ASSERT(dl_file == forHash(dl_hash));
	if (file != dl_file) {
		ende_Infalte(zip);
		zip = NULL;
		printf("WARNING: RECV file.hash %32s WAIT file.hash %32s\n", hash_bin2hex(hash), hash_bin2hex(dl_hash));
		end_of_download ();
		return 0;
		}
	uint32_t off_start   = packet->GET_U4 ();
	uint32_t PACKED_SIZE = packet->GET_U4 ();
	int   in_cnt  = packet->Unused ();
	char *in      = reinterpret_cast<char*>(packet->DATA ());
	while (in_cnt > 0) {
		int   out_cnt = OUT_CNT;
		unsigned char  out [OUT_CNT];
		int   worked = Infalte (zip, in, in_cnt, reinterpret_cast<char*>(out), &out_cnt);
		if (worked <= 0) {
			if (proto_log) printf("FAILED Infalte\n");
			ende_Infalte(zip);
			zip = NULL;
			Close();
			return 0;
			}
		if (out_cnt > 0) {
			uint32_t start = off_start + zip->total_out - out_cnt;
			uint32_t ende  = start + out_cnt;
			if (proto_log) {
				unsigned long S = start;
				unsigned long E = ende;
				printf("[%9lu-%9lu] \n", S, E - S);
				}
			if ( !logged ) {
				char logbuf[256];
			    	snprintf(logbuf, 255, "New Download from %15s (C5:emule) Ratio: %li\n", inet_ntoa(peer_ip), ratio);
				log(1, logbuf);
				GUI_LIST akt->GUI_statusMessage(logbuf);
				logged = true;
				}
			moreData_recv (start, ende);
			int ret = file->recvPart (start, ende, out);
			this->dl_count += (ende - start);	// update dl_count
			if (ret == -9) {
				// block already exists
				// cancel request other block
				ende_Infalte(zip);
				zip = NULL;
				req_part_data ();
				Code(OP_SENDINGPART);
				return 1;
				}
			}
		in     += worked;
		in_cnt -= worked;
		}
	Code(OP_SENDINGPART);
	if (zip->total_in == PACKED_SIZE)
		zip = ende_Infalte (zip);
	if(proto_log) printf("\n");
	return 1;
	}}}
int  sSource::C5_res_rang          (sPacket *packet) {{{ // (0x60)
	unsigned rang = packet->GET_U2 ();
	dl_slot = rang;
	setSource_timeout (peer_ip, currentTime + 10 * 30);
	if (logSend ('<', this,0xC560, true)) printf("%4u\n", rang);
	setSources_hash_dl(peer_ip, dl_hash);
	Code(OP_QUEUERANK);
	return 1;
	}}}
int  sSource::C5_rating            (sPacket *packet) {{{ // (0x61)
	uint8_t	 rate = packet->GET_U1 ();
	uint32_t size = packet->GET_U4 ();
	if(size != packet->Unused()) {
		printf("%s %i WARNING size mismatch\n", __FILE__, __LINE__);
		if (size > packet->Unused()) size = packet->Unused();
		}
	char *data = reinterpret_cast<char*>(packet->DATA());
	if (logSend ('<', this,0xC561, true)) printf("%u -> %.*s\n", rate, size, data);
	return 1;
	}}}
int  sSource::C5_req_sources       (sPacket *packet) {{{ // (0x81)
	tHash hash;
	packet->GET_HASH (hash);
	if (logSend ('>', this,0xC581, true)) printf("%32s\n", hash_bin2hex(hash));
	const struct eFiles *files = nextFile(hash, true);	// cursor_count is now set
	if (cursor_count == 0) return 1;
	uint16_t cnt = cursor_count;
	if (cnt > 100) cnt = 100;
	if (logSend ('>', this,0xC581, true)) printf("%32s\n", hash_bin2hex(hash));
	size_t len, LEN = len = 6 + 18 + cnt * 12;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca (LEN));
	ADD_U1  (&buf, &len, OP_EMULEPROT);
	ADD_U4  (&buf, &len, LEN - 5);
	ADD_U1  (&buf, &len, OP_ANSWERSOURCES);
	ADD_HASH(&buf, &len, hash);
	ADD_U2  (&buf, &len, cnt);
	do {
		const struct eSources *s = Source_ip (files->ip);
		uint16_t new_port = 4662;
		if (s != NULL) new_port = s->port;
		ADD_U4(&buf, &len, files->ip.s_addr);
		ADD_U2(&buf, &len, new_port     );
		ADD_U4(&buf, &len, this_ip.s_addr);
		ADD_U2(&buf, &len, pref.ports.server);
		files = nextFile(hash, false);
		cnt--;
		if (files == NULL && cnt != 0) {
			printf("rest count %i\n", cnt);
			ASSERT(files != NULL || cnt == 0);
			}
		} while (cnt > 0);
	Write (BUF, LEN);
	if(logSend ('>', this,0xC582, true)) printf("%32s\n", hash_bin2hex(hash));
	return 1;
	}}}
int  sSource::D4_res_sources       (sPacket *packet) {{{ // (0x82)
	unsigned long cnt = packet->Unused();
	unsigned long size  = cnt * 10 + 300;
	unsigned char *buf = reinterpret_cast<unsigned char*>(alloca (size));
	Bytef* in = reinterpret_cast<Bytef*>(packet->DATA());
	int ret = uncompress (reinterpret_cast<Bytef*>(buf), &size, in, cnt);
	ASSERT (size <= cnt * 10 + 300);
	if (cnt != size) {
		printf("WARNING 1041 (%lu , %lu)\n", size, cnt);
		return 0;
		}
	if (ret != Z_OK) {{{
		switch(ret) {
		case Z_MEM_ERROR:
			printf("%s %i Z_MEM_ERROR\n", __FILE__, __LINE__);
			ASSERT(0);
			return 0;
		case Z_BUF_ERROR:	// Zu wenig buffer
			printf("%s %i Z_BUF_ERROR\n", __FILE__, __LINE__);
			return 0;
		case Z_DATA_ERROR:	// Falsche daten
			printf("%s %i Z_DATA_ERROR\n", __FILE__, __LINE__);
			return 0;
		case Z_NEED_DICT:
			printf("%s %i Z_NEED_DICT\n", __FILE__, __LINE__);
			return 0;
		default :
			printf("%s %i %i\n", __FILE__, __LINE__, ret);
			ASSERT(0);
			return 0;
			}
		}}}
	logSend ('<', this,0xD482, false);
	res_sources (this, buf, size);
	return 1;
	}}}
int  sSource::C5_res_sources       (sPacket *packet) {{{ // (0x82)
	int cnt = packet->Unused();
	unsigned char *buf = reinterpret_cast<unsigned char*>(alloca (cnt));
	memcpy(buf, packet->DATA(), cnt);
	res_sources (this, buf, cnt);
	return 1;
	}}}

int  sSource::Donkey_emule_packed  (sPacket *packet) {{{ // 0xD4
	if (packet == NULL) {{{
		printf("ERROR %s %i\n", __FILE__, __LINE__);
		return 0;
		}}}
	if (packet->Proto() != 0xD4) {{{
		printf("ERROR %s %i\n", __FILE__, __LINE__);
		return 0;
		}}}
	switch (packet->Command ()) {
	case 0x82 : return D4_res_sources	(packet);
	default   : break;
		}
	packet->Dump ();
	return 0;
	}}}
int  sSource::Donkey_emule         (sPacket *packet) {{{ // 0xC5
	if (packet == NULL) {{{
		printf("ERROR %s %i\n", __FILE__, __LINE__);
		return 0;
		}}}
	if (packet->Proto() != OP_EMULEPROT) {{{
		printf("ERROR %s %i\n", __FILE__, __LINE__);
		return 0;
		}}}
	switch (packet->Command ()) {
	case 0x01 : return C5_client_info   	(packet);
	case 0x02 : return C5_client_info   	(packet);
	case 0x40 : return C5_compressed_data	(packet);
	case 0x60 : return C5_res_rang		(packet);
	case 0x61 : return C5_rating		(packet);
	case 0x81 : return C5_req_sources	(packet);
	case 0x82 : return C5_res_sources	(packet);
	default   : break;
		}
	packet->Dump ();
	return 0;
	}}}
int  sSource::Donkey_client        (sPacket *packet) {{{ // OP_EDONKEYHEADER
	if (packet == NULL) {{{
		printf("ERROR %s %i\n", __FILE__, __LINE__);
		return 0;
		}}}
	if (packet->Proto() != OP_EDONKEYHEADER) {{{
		printf("ERROR %s %i\n", __FILE__, __LINE__);
		return 0;
		}}}
	switch (packet->Command ()) {
	case 0x01 : return REQ_client_info   (packet);
	case 0x1D : return 1; // Chat it deprecated
	case 0x46 : return RES_part_data     (packet);
	case 0x47 : return REQ_part_data     (packet);
	case 0x48 : return RES_no_such_file  (packet);
	case 0x49 : return RES_dl_ende       (packet);
	case 0x4A : return REQ_shared_files  (packet);
	case 0x4B : return RES_shared_files  (packet);
	case 0x4C : return RES_client_login  (packet);
	case 0x4D : return RES_changed_server(packet);
	case 0x4E : return RES_source_message(packet);
	case 0x4F : return REQ_status  	     (packet);
	case 0x50 : return RES_status	     (packet);
	case 0x51 : return REQ_part_hash     (packet);
	case 0x52 : return RES_part_hash     (packet);
	case 0x54 : return REQ_slot	     (packet);
	case 0x55 : return RES_slot_avail    (packet);
	case 0x56 : return RES_slot_release  (packet);
	case 0x57 : return RES_slot_close    (packet);
	case 0x58 : return REQ_fileName      (packet);
	case 0x59 : return RES_fileName      (packet);
	case 0x5B : return RES_5b_unknown    (packet);
	case 0x5C : return RES_5c_unknown    (packet);
	default   : break;
		}
	packet->Dump ();
	return 0;
	}}}

// }}}

void sSource::readWork		(void) {{{
	switch (READ->Proto()) {
	case 0xE3: Donkey_client 	(READ); break;	// OP_EDONKEYHEADER
	case 0xC5: Donkey_emule	 	(READ); break;
	case 0xD4: Donkey_emule_packed	(READ); break;
	default  : printf("Client %15s:%5u\n", inet_ntoa(peer_ip), peer_port);
		   READ->Dump   ();
		   break;
		}
	READ->readReset();
	if (last_code != 0) Work();
	}}}
void sSource::Work      (void) {{{	// Only Called if fd!=INVALID_SOCKET
	if (16 == (16 & mask)) return;
	if (connected == 0) {
		if (currentTime - since > 60) Close();
		return;
		}
	if (dl_next_event < currentTime && last_code == 0) {{{
		if (last_send + 60 >  currentTime)  goto no_timeout;
		if (last_read + 60 >  currentTime)  goto no_timeout;
		if (status == WAIT_HELLO && currentTime - last_conn < 20 ) goto no_timeout;
		if (status == WAIT_HELLO && currentTime - last_read < 20 ) goto no_timeout;
		if (status == WAIT_INFO 	) { Close(); return; }
		if (status == WAIT_HELLO) 	  { Close(); return; }
		if (status == REQ_SLOT		) { Close(); return; }
		if (status == WAIT_OFFER_FILE	) { Close(); return; }
//		if (status == WAIT_HELLO) 	  { status = UNUSED; printf("Hello %15s\n", inet_ntoa(peer_ip)); return; }
//		if (status == WAIT_INFO 	) { status = UNUSED; printf("info  %15s\n", inet_ntoa(peer_ip)); return; }
//		if (status != 12)
//		if (status != 8)
		if (status == FTP_AKTIVE	) { dl_next_event = currentTime + 10; goto no_timeout; }
		if (status == WAIT_SLOT 	) { Close(); return; }
		if (status == 8) { Close(); return; }
		printf("TIMEOUT %15s source timeout=> status %3i since connect %li\n", inet_ntoa(peer_ip), status, currentTime - last_conn);
//		dl_next_event = currentTime + 10;
		}}}
no_timeout:
	if (status == 2 && last_code == 0) return; // Wait for info but got nothing
	bool use_all = true;
	int next_extra = 0;
	if (status == UNUSED) {
		printf("Close source %15s because of unused\n", inet_ntoa(peer_ip));
		Close();
		return;
		}
	if (0    >  last_code) {{{
		printf("UNUSE 1 sSource %15s %s %i\n",inet_ntoa(peer_ip),__FILE__,__LINE__);
		status = UNUSED;
		return;
		}}}
	// DL handling {{{
	if (OP_ACCEPTUPLOADREQ == last_code) { last_code = 0;  have_slot = true; }
	if (OP_CANCELTRANSFER  == last_code) { last_code = 0;  extra_status = 0; }
	if (OP_OUTOFPARTREQS   == last_code) { last_code = 0;  req_slot ();  }
	if (status == P_C5060 && have_slot) status = WAIT_SLOT;
//	if (have_slot && status != 13 && status != 7 && status!=6) printf("%15s STATUS with slot %i\n", inet_ntoa(peer_ip), status);
//	if (last_code==1) printf("%15s status on code 1 => %i\n", inet_ntoa(peer_ip), status);
	switch (status) {
	case P_C5060	 :	if (last_code == OP_QUEUERANK) last_code = 0;
				status = WAIT_SLOT;
				break; // Wait for 0x55
	case SEND_HELLO  :	client_info(0x01);
				status = WAIT_INFO;
				dl_next_event = currentTime + dlTimeout_hello;
				break;
	case WAIT_HELLO  :	if (0x01 != last_code) break;
				last_code=0;
				status = WAIT_INFO;
				client_info(0x4C);
				goto get_info;
	case FTP_AKTIVE	 :   	// wait for file blocks
				if (OP_SENDINGPART == last_code) {{{
					dl_next_event = currentTime + dlTimeout_data;
					last_code = 0;
					}}}
				if (OP_QUEUERANK == last_code) {{{
					last_code = 0;
					status = P_C5060;
					break;
					}}}
				break;
	case WAIT_OFFER_FILE:	if (OP_QUEUERANK == last_code) {{{
					last_code = 0;
					status = P_C5060;
					break;
					}}}
				if (dl_file == NULL) break;
			   	last_code = 0;
request_file:			ASSERT(dl_file != NULL);
				if (!PartInfo()) {
					if (ask_info) break;
					{ FILE_LIST {
						if (akt->status == ALL_DONE || akt->Pause() ) continue;
						use_all = false;
						if (eMule) req_fileName    (akt->hash);
						req_file_status (akt->hash);
						} }
					ask_info = true;
					break;
					}
				recvData = true;
			   	if (!dl_file->havePartHash()) req_part_hash(dl_hash);
			   	if (!req_part_data()) {{{ // no more blocks avail
					tHash new_hash;
					bzero (new_hash, HASH_LEN);
					dlFile(new_hash);	// We got maybe in the meantime the part
								// And he maybe have another block we can use
					goto get_info;
					}}}
				dl_next_event = currentTime + dlTimeout_data;
			   	break;
	case WAIT_INFO	:	if (0x4C   != last_code) break;
				last_code = 0;
get_info:			use_all = true;
				if (tcp_server != NULL) 
					if (tcp_server->peer_ip.s_addr == peer_ip.s_addr) break;
				if (!auto_select) {{{
					{ FILE_LIST {
						if (akt->status == ALL_DONE || akt->Pause() ) continue;
						if (NULL == File_idx (akt->hash, peer_ip)) continue;
						use_all = false;
						if (eMule) req_fileName    (akt->hash);
						req_file_status (akt->hash);
						} }
					if (use_all) 
					{ FILE_LIST {
						if (akt->status == ALL_DONE || akt->Pause() ) continue;
						use_all = false;
						if (eMule) req_fileName    (akt->hash);
						req_file_status (akt->hash);
						} }
					ask_info = true;
					auto_select = use_all;
					}}}
				dl_next_event = currentTime + dlTimeout_fileinfo;
			   	status = REQ_SLOT;
	case REQ_SLOT    : 	if (OP_QUEUERANK == last_code && !have_slot) {{{
					status = P_C5060;
					last_code = 0;
					dl_next_event = currentTime + 60;
					break;
					}}}
				dl_next_event = currentTime + 60;
				if (dl_file == NULL) {
					if (have_slot) Close();
					break;
					}
				if (!have_slot) {{{
					req_slot ();
					dl_next_event = currentTime + 60;
					status = WAIT_SLOT;
					break;
					}}}
	case WAIT_SLOT	 : 	dl_next_event = currentTime + 180;
				status = WAIT_SLOT;
				if (0XC540 == last_code) {{{ // resPartData (etwas komisch)
					last_code = 0;
					break;
					}}}
				if (OP_QUEUERANK == last_code) {{{ // infRank
					last_code = 0;
					dl_next_event = currentTime + 60;
					status = P_C5060;
					break;
					}}}
				
				if (!have_slot) break;
				if (dl_file == NULL) goto get_info;
				dl_next_event = currentTime + dlTimeout_fileinfo;
			   	status = WAIT_OFFER_FILE;
				goto request_file;
	default    	 : 	break;
		}
	// }}}
	// ul Status handling {{{
	if (0xC550 == last_code && (status == FTP_AKTIVE || status == REQ_SLOT || status == WAIT_HELLO)) last_code = 0;
	if (last_code != 0) {
		printf("Last STATUS %i %#X <---------------------------------\n", status, last_code);
		last_code = 0;
		}
	if (extra_status <= 0) return;
	if (ul_next_event > currentTime) return;
	switch (extra_status) {
	case 0x46 : 	if (ul_rang > 0) return;
			 ul_next_event = currentTime + 10;
			if (ul_left < 2 * 1024) {
				next_extra = OP_SENDINGPART;
				break;
				}
			if (dlCount + ratio + 128 * 1024 < ulCount && ul_left < 8 * 1024 && global_ul_count * 10 < global_dl_count) {
				next_extra = OP_SENDINGPART;
				break;
				}
		    	if (1 == res_part_data ())	next_extra = OP_SENDINGPART;
		    	else 		   		next_extra = OP_OUTOFPARTREQS;
		    	break;
	case 0x4B : 	break; // Shared files not longer supported
	case 0x52 : 	if (ulFile() == NULL) break;
		    	if (!ulFile()->havePartHash ()) break;
		    	res_part_hash(); 
			break;
	case 0x55 : 	if (ul_rang == 1000) {
				ul_rang = ulRang;
				if (ulRang<990) ulRang++;
				}
			if (ul_rang <= 0) {
				ul_rang = 0;
				ul_next_event = currentTime + 10;
				CMD_slot_available  ();
				return;
				}
			ul_next_event = currentTime + 60;
			next_extra = OP_ACCEPTUPLOADREQ;
			C5_res_rang (ul_rang);
		    	break;
	case 0x57 : 	res_slot_close	(); break;
	default   : 	break;
		}
	extra_status = next_extra;
	// }}}
	}}}

/*
 * vim600: fdm=marker
 */
