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

/*
Spezial Hashes:
fffffffffffffffffffffff00000001 -> Information how many dl File Transfers requested
fffffffffffffffffffffff00000002 -> Socket  / Sources
fffffffffffffffffffffff00000003 -> Traffic / Downlaod

Information how many sSource are Slot waiting (max 257 Parts 0 => Have Slot 1-256 => dl_slot+1
*/
static size_t sourceCount4Part (tHash &hash, int block, unsigned limit) {{{
	class sFile *file = forHash(hash);
	if (file == NULL) return 0;
	size_t ret = file->part_sources[block];
	if (ret > limit) return limit;
	return ret;
	}}}
static size_t sourceCount4Part (tHash &hash, uint32_t von, uint32_t bis, unsigned limit) {{{
	int block_0 = von / PARTSIZE;
	int block_n = bis / PARTSIZE;
	class sFile *file = forHash(hash);
	if (file == NULL) return 0;
	size_t check = 0;
	for (int j = block_0 ; j <= block_n; j++) check+=file->part_sources[j];
	if (check > limit) check = limit;
	return check;
	}}}

unsigned 	 sGui::count = 0;
sGui *sGui::first_gui = NULL;

sGui     :: sGui      (class cSocket *Fd, struct in_addr ) : sSocket(Fd) {{{
	prio = 0;
	next_gui = first_gui;
	first_gui = this;
	Type = tGui;
	status 	    = CONNECTING;
	name 	    = NULL;
	result_tree = NULL;
	result_len  = 0;
	search_tree = NULL;
	search_len  = 0;
	last_code   = 0;
	auth 	    = false;
	dl_status   = false;
	ul_status   = false;
	announced_server_connect = true ; // true to send initial disconncet()
	dl_timeout  = currentTime;
	ul_timeout  = currentTime;
	count++;
	}}}
sGui     ::~sGui      (void) {{{
	if (result_tree != NULL) Free2 (result_tree);
	if (search_tree != NULL) Free2 (search_tree);
	if (name        != NULL) Free2 (name);
	count--;
	// remove me from the file_list
	if (first_gui == this) {
		first_gui = next_gui;
		return;
		}
	sGui *prev_file = first_gui;
	while (prev_file->next_gui != this && prev_file->next_gui != NULL)
	prev_file = prev_file->next_gui;
	if (prev_file == NULL) return;
	ASSERT (prev_file->next_gui == this);
	prev_file->next_gui = prev_file->next_gui->next_gui;
	}}}
void sGui::Name       (const char *NAME) {{{
	Free2 (name);
	name = STRDUP (NAME);
	}}}
void sGui::resultTree	(char *tree,uint32_t len) {{{
	if (result_tree != NULL) Free2 (result_tree);
	result_tree = tree;
	result_len = len;
	}}}
void sGui::searchTree	(char *tree,uint32_t len) {{{
	if (result_tree != NULL) Free2 (search_tree);
	search_tree = tree;
	search_len = len;
	}}}

int   sGui::GUI_txt_g ( void ) {{{
	if (!auth) return 0;
	char logbuf[256];
	GUI_statusMessage("(open)");
	if (use_kademlia) {
		snprintf(logbuf, 255, "ID: %32s", hash_bin2hex(client_hash));
		GUI_statusMessage(logbuf);
		}
	snprintf(logbuf, 255, "%s:%u  (0.0.0.0:0)\n", inet_ntoa(cSocket::getLocalExternalIP()), pref.ports.client);
	GUI_statusMessage(logbuf);
	snprintf(logbuf, 255, "Users: 172032   Files: 62914");
	GUI_statusMessage(logbuf);
	if (use_kademlia) 
		GUI_statusMessage("Shibboleth: This is cdonkey-0.8.5-cvs hybrid");
	return 1;
	}}}


// Client-Gui    TCP handling (0x64) ... (0xE3)
int   sGui::GUI_serverlist      (void                    ) {{{ // (0xAA) // send server list to gui
	if (!auth) return 0;
	tHash	hash;
	bzero (hash, HASH_LEN);
	const struct eServers *s;
	// If all server are send AN if all server use max name.length and max desc.lenth
	unsigned max_cnt = cntServer() ;
	unsigned limit = max_cnt * (78 + sizeof(s->name) + sizeof(s->desc));
	unsigned char *buffer, *BUFFER = buffer = reinterpret_cast<unsigned char*>(alloca (limit));	
	s = nextServers (true);
	unsigned unused = limit;
	unsigned cnt = 0 ;
	while (s != NULL) {	// we have reserved the maximum possible size
		cnt++;
		ADD_HASH   (&buffer, &unused, hash);
		ADD_U4     (&buffer, &unused, s->ip.s_addr);
		ADD_U2     (&buffer, &unused, s->port     );
		ADD_U4     (&buffer, &unused, 6);
		add_intTag (&buffer, &unused, FT_PRIORITY	, s->prio );
		add_intTag (&buffer, &unused, ST_PING    	, currentTime - s->last_action);
		add_intTag (&buffer, &unused, ST_LONG_USERS	, s->user);
		add_intTag (&buffer, &unused, ST_LONG_FILES	, s->file);
		add_strTag (&buffer, &unused, ST_SERVERNAME	, s->name);
		add_strTag (&buffer, &unused, ST_DESCRIPTION	, s->desc);
		s = nextServers (false);
		}
	log (1, "Server list %i/%i\n", max_cnt, cnt);
    	if (logSend ('>', this, CO_SERVER_LIST, true)) printf("count: %i\n", cnt);

	size_t len, LEN = len = 6 + 4 + limit - unused;
	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, CO_SERVER_LIST);
	ADD_U4 (&buf, &len, cnt);
	if (cnt > 0) memcpy (buf, BUFFER, len);
	Write (BUF, LEN);
	return 1;
	}}}
int   sGui::GUI_shareFiles      (void                    ) {{{ // (0xAD)
    	logSend ('>', this, CO_SHARED_FILES, false);
    	if (!auth) return 0;
    	unsigned limit = 3000; // hardcoded ! BAD
    	unsigned char *buffer, *BUFFER = buffer = reinterpret_cast<unsigned char*>(alloca (limit));
    	unsigned unused = limit;
    	unsigned cnt = 0 ;
    	FILE_LIST {{{
        if ( ! akt->get_Completed() ) continue;
        	cnt++;
        	ADD_HASH   (&buffer, &unused, akt->hash);   			// hash
        	ADD_U4     (&buffer, &unused, 0);       			// unused
        	ADD_U2     (&buffer, &unused, 0     );  			// unused
        	ADD_U4     (&buffer, &unused, 4);       			// cnt of tag elements
        	add_strTag (&buffer, &unused, FT_FILENAME  , akt->Name());	// filename
        	add_strTag (&buffer, &unused, FT_FILEFORMAT, "unknown");	// fileformat
        	add_intTag (&buffer, &unused, FT_FILESIZE  , akt->Size());	// filesize 
        	add_strTag (&buffer, &unused, FT_FILETYPE  , "unknown" );	// filetype
    		}}}
    	if (cnt > 0) {
		size_t len, LEN = len = 6 + 4 + limit - unused;
		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, CO_SHARED_FILES);
        	ADD_U4 (&buf, &len, cnt);
		memcpy (buf, BUFFER, limit - unused);
		Write (BUF, LEN);
    		}
    	Code(0xE0); // ? 
    	return 1;
    	}}}
bool  sGui::GUI_res_part_status (tHash  &hash            ) {{{ // (0xAE) SPLIT
	if (!auth) return 0;
	logSend ('>', this, CO_GAP_DETAILS, false);
	sFile *file = forHash (hash);
	if (file == NULL) {{{
		printf("%s %i Unkown file\n", __FILE__, __LINE__);
		return false;
		}}}
	log (1,"stat for %s\n", file->Name());
	unsigned maxCount = (file->size + PARTSIZE - 1) / PARTSIZE;
	uint32_t realCount = 0;
	size_t len, LEN = len = 24 + 10 * maxCount;
	unsigned char *buf, *BUF = buf = reinterpret_cast<unsigned char*>(alloca (LEN));
	ADD_U1   (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4   (&buf, &len, 0);
	ADD_U1   (&buf, &len, CO_GAP_DETAILS);
	ADD_HASH (&buf, &len, hash);
	ADD_U2   (&buf, &len, 0);
	uint32_t p_old_von  = 0;
	uint16_t p_old_stat = 9997;
	for (unsigned b = 0; b < maxCount; b++) {{{
		uint32_t p_von  = b * PARTSIZE;
		uint32_t p_bis  = p_von  + PARTSIZE - 1; 
		uint16_t p_stat = 2000;
		if (p_bis > file->size) p_bis = file->size - 1;
		if (!file->partStatus(b)) {
			if (file->downloads == 0) p_stat = 0;
			else 			  p_stat = sourceCount4Part (hash, p_von, p_bis, 255);
			}
		if (p_old_stat == p_stat && b != 0) {
			buf -= 10;
			len += 10;
			p_von = p_old_von;
			realCount--;
			}
		ADD_U4   (&buf, &len, p_old_von  = p_von);
		ADD_U4   (&buf, &len, p_bis);
		ADD_U2   (&buf, &len, p_old_stat = p_stat);
		realCount++;
		}}}
	buf = BUF;
	len = LEN;
	ADD_U1   (&buf, &len, OP_EDONKEYHEADER);
	ADD_U4   (&buf, &len, HASH_LEN + 2 + 10 * realCount + 1);
	ADD_U1   (&buf, &len, CO_GAP_DETAILS);
	ADD_HASH (&buf, &len, hash);
	ADD_U2   (&buf, &len, realCount);
	Write (BUF, 24 + realCount * 10);
	return true;
	}}} 
int   sGui::GUI_core_status     (void                    ) {{{ // (0xAF)
	if (!auth) return 0;
	logSend('>', this, CO_CLIENT_STATS, false);
	unsigned long need = 0;
	FILE_LIST need += akt->size;
	size_t len, LEN = len = 6 + 20;
	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, CO_CLIENT_STATS);
	ADD_F4 (&buf, &len, 10000.0);					// avail Space on Temp
	ADD_F4 (&buf, &len, 10000.0); 					// avail Space on Incomming
	ADD_F4 (&buf, &len, ((need / (1024.0*1024.0))));		// Space needed by the Downloads
	ADD_U4 (&buf, &len, cSocket::getLocalExternalIP().s_addr);	// my ID
	ADD_U2 (&buf, &len, sSource::count + sServer::count);
	ADD_U2 (&buf, &len, 10);					// People in the qeue
	ASSERT (len == 0);
	Write (BUF, LEN);
	return 1;
	}}}
int   sGui::GUI_statusMessage   (const char *txt         ) {{{ // (0xB4)
	if (!auth) return 0;
	size_t cnt = strlen(txt);
	if (cnt == 0) return 0;
	// ed2k_gui does not need a trailing LF
	char *temp = STRDUP(txt);
	if ( temp[cnt-1] == '\n' ) {
		temp[cnt-1] = 0;
		--cnt;
		}
	if (logSend('>', this, CO_STATUS_MSG, true)) printf("%s\n", txt);
	cnt = strlen(temp);
	if (cnt == 0 ) {
		free (temp);
		return 1;
		}
	size_t len, LEN = len = 6 + 2 + cnt;
	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, CO_STATUS_MSG);
	ADD_S2 (&buf, &len, temp);
	ASSERT (len == 0);
	Write (BUF, LEN);
	free (temp);
	return 1;
	}}}
int   sGui::GUI_errorMessage    (const char *txt         ) {{{ // (0xB5) Currently not used
	if (!auth) return 0;
	logSend('>', this, CO_ERROR_MSG, false);
	size_t len, LEN = len = 6 + 2 + strlen(txt);
	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, CO_ERROR_MSG);
	ADD_S2 (&buf, &len, txt);
	ASSERT (len == 0);
	Write (BUF, LEN);
	return 1;
	}}}
int   sGui::GUI_connected_to    (const char* to_name     ) {{{ // (0xB6)
	if (!auth) return 0;
	logSend('>', this, CO_CONNECTED_TO, false);
	size_t len, LEN = len = 6 + 2 + strlen(to_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, CO_CONNECTED_TO);
	ADD_S2 (&buf, &len, to_name);
	ASSERT (len == 0);
	Write (BUF, LEN);
	announced_server_connect = true;
	return 1;
	}}}
int   sGui::GUI_disconnected    (void                    ) {{{	// (0xB7)
	if (!auth) return 0;
	if ( announced_server_connect == false ) return 1;	// Dont send disconnect twice
	logSend('>', this, CO_DISCONNECTED, 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, CO_DISCONNECTED);
	ASSERT (len == 0);
	Write (BUF, LEN);
	announced_server_connect = false;
	return 1;
	}}}
int   sGui::GUI_result_tcp      (void                    ) {{{ // (0xBB)
	if (!auth) return 0;
	if (resultLen() <= 0) return 0;
	logSend('>', this, CO_ED_NEW_SEARCH_RESULT, false);
	size_t len, LEN = len = 6 + resultLen();
	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, CO_ED_NEW_SEARCH_RESULT);
	memcpy (buf, resultTree(), resultLen());
	Write (BUF, LEN);
	return 1;
	}}}
int   sGui::GUI_new_download    (const class sFile *file ) {{{ // (0xBC 188)
	if (!auth) return 0;
	logSend('>', this, CO_NEW_DOWNLOAD, false);
	const char *tmp_name = file->Name();
	const char *tmp_hash = reinterpret_cast<const char*>(hash_bin2hex (file->hash));
	size_t cnt = 43 + strlen(tmp_name) + strlen(tmp_hash);
	size_t len, LEN = len = 6 + cnt;
	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, CO_NEW_DOWNLOAD);
	ADD_HASH   (&buf, &len, file->hash);
	ADD_U4     (&buf, &len, 0);			// IP
	ADD_U2     (&buf, &len, 0);			// PORT
	ADD_U4     (&buf, &len, 2);			// number of tags
	add_strTag (&buf, &len, FT_FILENAME, tmp_name);
	add_intTag (&buf, &len, FT_FILESIZE, file->Size());
	ADD_U1     (&buf, &len, 1); 			// Prio
	ADD_S2     (&buf, &len, tmp_hash ); 		// uniqID
	ASSERT (len == 0);
	Write (BUF, LEN);
	return 1;
	}}}
int   sGui::GUI_new_upload      (const class sFile *file ) {{{ // (0xBE 190)
	if (!auth) return 0;
	logSend('>', this, CO_NEW_UPLOAD, false);
	const char *tmp_name = "Group";
	size_t cnt = 2 + strlen(file->Name()) + 40 + strlen(tmp_name);
	size_t len, LEN = len = 6 + cnt;
	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, CO_NEW_UPLOAD);
	ADD_S2     (&buf, &len, file->Name());
	ADD_HASH   (&buf, &len, file->hash);
	ADD_U4     (&buf, &len,  0 );			// IP
	ADD_U2     (&buf, &len,  0 );			// Port
	ADD_U4     (&buf, &len,  2 );			// number of tags
	add_strTag (&buf, &len, FT_FILENAME, tmp_name);
	add_intTag (&buf, &len, FT_FILESIZE, file->Size());
	ASSERT (len == 0);
	Write (BUF, LEN);
	return 1;
	}}}
int   sGui::GUI_removeDownload  (tHash &hash             ) {{{ // (0xBD)
	if (!auth) return 0;
	if (!validSocket()) return 0;
	if (logSend('>', this, CO_REMOVE_DOWNLOAD, true)) printf("%32s\n", hash_bin2hex(hash));
	size_t len, LEN = len = 6 + HASH_LEN;
	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, CO_REMOVE_DOWNLOAD);
	ADD_HASH(&buf, &len, hash);
	ASSERT (len == 0);
	Write (BUF, LEN);
	return 1;
	}}}
int   sGui::GUI_dl_status       (void                    ) {{{ // (0xC5 197)
	if (!auth) return 0;
	logSend('>', this, CO_DOWNLOAD_STATUS, false);
	int cnt = sFile::count - sFile::count_complete;
	cnt = 0; 
	{ FILE_LIST if (! akt->get_Completed() ) cnt++; }
	int cnt1 = 2 + 13 * cnt;
	size_t len, LEN = len = 6 + cnt1;
	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, CO_DOWNLOAD_STATUS);
	ADD_U2 (&buf, &len, cnt) ; 			// NUM of DL
	uint16_t dl_id = 0;
	FILE_LIST {{{				// Information about all files
		if ( akt->get_Completed() ) continue; // dont show finished files in main view
		ADD_U2 (&buf, &len, dl_id++); 		// DL_ID
		uint8_t akt_status = STATUS_LOOKING;
		uint8_t akt_sources = 255;
		if (akt->downloads < akt_sources) akt_sources = akt->downloads;
		if (akt->downloads 	> 0   	  ) akt_status = STATUS_DOWNLOADING;
		if (akt->Pause  () 	      	  ) akt_status = STATUS_PAUSED;
		if (akt->status == ALL_DONE	  ) akt_status = STATUS_DONE;
		ADD_U1 (&buf, &len, akt_status);			// DL_STATUS
		ADD_F4 (&buf, &len, akt->dlRate() /1024.0);		// current speed
//		printf("akt->transfered %u\n", akt->transfered);
//		printf("akt->name %s\n", akt->Name());
//		ASSERT(akt->transfered < 1000);
		ADD_U4 (&buf, &len, akt->transfered) ; 			// kb transferred
		unsigned not_full = 0;
		unsigned trying   = 0;
		unsigned maxCount = (akt->size + PARTSIZE - 1) / PARTSIZE;
		tHash hash;
		memcpy (hash, akt->hash , HASH_LEN);
		for (unsigned i = 0; i < maxCount; i++) {
			if (akt->partStatus(i)) continue;
			not_full ++;
			if (sourceCount4Part (hash, i, 1)) trying++;
			}
		uint8_t percent = 100;
		if (not_full > 0) percent = (trying * 100) / not_full;
		ADD_U1 (&buf, &len, percent); 				// % avalable
		ADD_U1 (&buf, &len, akt_sources);		// sources (max 255 shown) count OK but Gui show other value WHY ?
		}}}
	ASSERT (len == 0);
	Write (BUF, LEN);
	dl_timeout = currentTime;
	return 1;
	}}}
int   sGui::GUI_ul_status       (void                    ) {{{ // (0xC6 218) Mehr Infos would be nice
	if (!auth) return 0;
	logSend('>', this, CO_UPLOAD_STATUS, false);
	int cnt = sFile::count;
	if (cnt > 65535) cnt = 65535;
	int cnt1 = 2 + 6 * cnt;
	size_t len, LEN = len = 6 + cnt1;
	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, CO_UPLOAD_STATUS);
	ADD_U2 (&buf, &len, (cnt)); 				// NUM of DL
	uint16_t ul_id = 0;
	FILE_LIST {						// LOOP through all files
		ADD_U2 (&buf, &len, ul_id++); 			// UL_ID-1 since here is no extra info
		ADD_F4 (&buf, &len, akt->ulRate() / 1024.0);	// current speed
		}
	ASSERT (len == 0);
	Write (BUF, LEN);
	ul_timeout = currentTime;
	return 1;
	}}}
int   sGui::GUI_send_option     (void                    ) {{{ // (0xC7)
	if (!auth) return 0;
	logSend ('>', this, CO_OPTIONS, false);
	size_t len, LEN = len = 6 + 39 + strlen(client_name) + strlen(incoming) + strlen(completed);
	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, CO_OPTIONS);
	ADD_U2 (&buf, &len, (my_version)); 			// client version
	ADD_F4 (&buf, &len, dlSecond / 1024.0);			// max down
	ADD_F4 (&buf, &len, ulSecond / 1024.0);			// max up
	ADD_U2 (&buf, &len, pref.ports.client);			// doorport
	ADD_U2 (&buf, &len, 0);					// CRAP (deprecated)
	ADD_S2 (&buf, &len, client_name);			// client name
	ADD_S2 (&buf, &len, incoming);				// temp dir
	ADD_S2 (&buf, &len, completed);				// incoming dir (completed dir)
	ADD_U1 (&buf, &len, 0);					// DUMMY if overnet
	ADD_U1 (&buf, &len, 0);					// DUMMY if overnet
	ADD_U1 (&buf, &len, 1);					// allow private message
	ADD_U1 (&buf, &len, 1);					// save corrupt
	ADD_U1 (&buf, &len, 1);					// DUMMY 
	ADD_U2 (&buf, &len, pref.ports.gui);			// admin port
	ADD_U4 (&buf, &len, TCP_CLIENT_COUNT);			// max connections
	ADD_U4 (&buf, &len, 0x020c0800);			// core build date
	ADD_F4 (&buf, &len,  (dlSecond + ulSecond) / 1024.0);	// line download speed
	if (len != 0) printf("len = %i\n", len);
	ASSERT (len == 0);
	Write (BUF, LEN);
	Code(0x01);
	return 1;
	}}}
int   sGui::GUI_share_dirs      (void                    ) {{{ // (0xD7) // sende meine shared dirs an die GUI
	logSend('>', this, CO_SHARED_DIRS, false);
	if (!auth) return 0;
	if (completed == NULL ) return 0;
	size_t cnt = 2 + 2 + strlen(completed);
	size_t len, LEN = len = 6 + cnt;
	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, CO_SHARED_DIRS);
	ADD_U2 (&buf, &len, 1);
	ADD_S2 (&buf, &len, completed);
	ASSERT (len == 0);
	Write (BUF, LEN);
	return 1;
	}}}

int  sGui::GUI_login            (sPacket *packet) {{{ // (0x64)
	logSend ('<', this, CO_LOGIN, false);
	if (packet == NULL) return 0;
	if (ctrl_pass == NULL || ctrl_user == NULL) return 0;
	char *log_name = packet->GET_S2 ();
	char *pass = packet->GET_S2 ();
	if ( 0 != strcmp (log_name, ctrl_user) || 0 != strcmp (pass, ctrl_pass)) {{{
		printf("Gui DENY:%s pass:%s\n", log_name, pass );
		Free2 (log_name);
		Free2 (pass);
		Close();
		return 0;
		}}}
	auth = true;
	Code(0x64);
	Free2 (log_name);
	Free2 (pass);
	FILE_LIST {
		if ( ! akt->get_Completed() ) GUI_new_download (akt);
		GUI_new_upload(akt);
		}
	if ( tcp_server != NULL ) {
		// Stati which will be ignored for successful connection
		if ( tcp_server->Status() >= 4 && tcp_server->Status() <= 15 
			&& tcp_server->Status() != SERVER_CLOSED 
			&& tcp_server->Status() != WAIT_SERVER_LIST ) {
			struct eServers *s = Server_ip (tcp_server->peer_ip);
			if ( s != NULL ) {
				char tmpname[33];
				printf("XXX connected to %15s,%s Status: %i \n", inet_ntoa( s->ip ), s->name, tcp_server->Status() );
			  	if ( strlen( s->name ) == 0 ) {
					// s->name ist leider nicht immer gesetzt (BUG?). Wir verwenden dann die IP
					// This is legal if the server have not yet respond with its name
					snprintf(tmpname,33, "%15s", inet_ntoa( s->ip ) );	
					GUI_connected_to(tmpname);
					}
			  	else GUI_connected_to(s->name);
				}
			}
		}
	return 1;
	}}}
int  sGui::GUI_shutdown         (sPacket *      ) {{{ // (0x65)
	logSend ('<', this, CO_SHUTDOWN, false);
	if (!auth) return 0;
	running = false;
	return 1;
	}}}
int  sGui::GUI_txt_command      (sPacket *packet) {{{ // (0x66)
	logSend ('<', this, CO_TXTCOMMAND, false);
	if (packet == NULL) return 0;
	if (!auth) return 0;
	char *text = packet->GET_S2 ();
	const char *msg = sCommand::Execute(text);
	GUI_statusMessage (msg);
	Free2 (text);
	return 1;
	}}}
int  sGui::GUI_connect          (sPacket *packet) {{{ // (0xC8)
	if (packet == NULL) return 0;
	struct in_addr  ip;
	uint16_t	new_port;
	char logbuf[256];
	ip.s_addr = packet->GET_U4();
	new_port	  = packet->GET_U2();
	if (ip.s_addr == 0) {
		if (logSend('<', this, CO_CONNECT, true)) printf("RANDOM\n");
		struct in_addr  new_ip;
		if (tcp_server != NULL) delete tcp_server;
		GUI_disconnected();
		if (nextServer (&new_ip, &new_port)) {
			tcp_server = new sServer (new_ip, new_port);
			snprintf(logbuf,255, "Trying Edonkey Server: %15s\n", inet_ntoa(new_ip) );
            		log (1,logbuf);
            		GUI_LIST akt->GUI_statusMessage(logbuf);
			}
		return 1;
		}
	if (logSend('<', this, CO_CONNECT, true)) printf("SELECTED %15s:%5u\n", inet_ntoa(ip), new_port);
	if (tcp_server != NULL) delete tcp_server;
	GUI_disconnected();
	tcp_server = new sServer (ip, new_port);
	snprintf(logbuf,255, "Trying Edonkey Server: %15s\n", inet_ntoa(ip) );
	log (1,logbuf);
	GUI_LIST akt->GUI_statusMessage(logbuf); 
	return 1;
	}}}
int  sGui::GUI_disconnect       (sPacket *      ) {{{ // (0xC9)
	logSend ('<', this, CO_DISCONNECT, false);
	if (tcp_server != NULL) delete tcp_server ; //tcp_server->Close();
	tcp_server = NULL ;
	GUI_disconnected();
	return 1;
	}}}
int  sGui::GUI_search           (sPacket *packet) {{{ // (0xCA)
	if (packet == NULL) return 0;
	logSend ('<', this, CO_SEARCH, false);
	uint32_t len = packet->Length() - 1;
	char *tree = reinterpret_cast<char*>(MALLOC (len));
	memcpy (tree, packet->DATA(), len);
	packet->GET_U1 ();
	char *searchstr = packet->GET_S2 ();
	log (1,"Searching: %s\n", searchstr) ;
	free(searchstr);
	searchTree (tree, len);
	tcp_search = true;
	return 1;
	}}}
int  sGui::GUI_search_ext       (sPacket *      ) {{{ // (0xCB)
	logSend ('<', this, CO_EXSEARCH, false);
	userver_listen->UDP_search4gui(this);
	return 1;
	}}}
int  sGui::req_download         (sPacket *packet) {{{ // (0xCF)
	logSend ('<', this, CO_DOWNLOAD, false);
	tHash hash;
	packet->GET_HASH(hash);
	if (NULL != forHash(hash)) return 1;	//dl already exists
	printf("HASH %32s\n", hash_bin2hex(hash));
	const struct eFiles *f = nextFile(hash, true);
	printf("Found %i\n", cursor_count);
	while (f != NULL) {
		if (f->info_len != 0) {
			size_t len =  f->info_len;
			unsigned char *dat = reinterpret_cast<unsigned char*>(alloca(len));
			memcpy(dat, f->info, len);
			class sTag *tag = parse_tag (dat, &len);
			if (tag == NULL) {
				Free2 (dat);
				printf("bad\n");
				break;
				}
			if (tag->size > 0 && tag->name != NULL) {
				sFile *file = new sFile (hash, tag->name, tag->size, incoming);
	        		GUI_new_download (file);
					GUI_new_upload (file);
				delete tag;
				return 1;
				}
			delete tag;
			}
		f = nextFile(hash, false);
		}
	return 1;
	}}}
int  sGui::GUI_dlPause          (sPacket *packet) {{{ // (0xD0)
	if (packet == NULL) return 0;
	logSend ('<', this, CO_PAUSE_DOWNLOAD, false);
	tHash hash;
	packet->GET_HASH (hash);
	sFile *file = forHash (hash);
	if (file == NULL) return 1;
	file->Pause (1);
	return 1;
	}}}
int  sGui::GUI_dlResume         (sPacket *packet) {{{ // (0xD1)
	if (packet == NULL) return 0;
	logSend ('<', this, CO_RESUME_DOWNLOAD, false);
	tHash hash;
	packet->GET_HASH (hash);
	sFile *file = forHash (hash);
	if (file == NULL) return 1;
	file->Pause (0);
	return 1;
	}}}
int  sGui::GUI_cancelDownload   (sPacket *packet) {{{ // (0xD2)
	if (packet == NULL) return 0;
	logSend ('<', this, CO_CANCEL_DOWNLOAD, false);
	tHash hash;
	packet->GET_HASH (hash);
	sFile *file = forHash (hash);
	delete file;
	char *alt = reinterpret_cast<char*>(alloca (strlen(control) + 43));
	char *neu = reinterpret_cast<char*>(alloca (strlen(control) + 43));
	sprintf (alt, "%s%c%s.part.met" , control, dir_delim, hash_bin2hex (hash));
	sprintf (neu, "%s%c%s.part.old" , control, dir_delim, hash_bin2hex (hash));
	rename(alt, neu);
	return 1;
	}}}
int  sGui::GUI_download_prio    (sPacket *packet) {{{ // (0xD3)
	if (packet == NULL) return 0;
	logSend ('<', this, CO_SETPRI_DOWNLOAD, false);
	tHash hash;
	packet->GET_HASH (hash);
	uint8_t sprio = packet->GET_U1 ();
	printf("Prio %u\n",sprio);
	if (sprio>2) return 1;
	sFile *file = forHash (hash);
	if (file == NULL) return 1;
	file->dlPrio (sprio);
	return 1;
	}}}
int  sGui::GUI_get_serverlist   (sPacket *      ) {{{ // (0xD5) // Gui request for server List
	if (!auth) return 0;
	logSend ('<', this, CO_GET_SERVERLIST, false);
	GUI_serverlist ();
	return 1;
	}}}
int  sGui::GUI_req_shared_dir   (sPacket *      ) {{{ // (0xD7)
	if(!auth) return 0;
	logSend ('<', this, CO_GET_SHARE_DIRS, false);
	GUI_share_dirs ();
	return 1;
	}}}
int  sGui::GUI_dlStatus_start   (sPacket *      ) {{{ // (0xD9)
	logSend ('<', this, CO_START_DL_STATUS, false);
	dl_status  = true;
	if(!auth) return 0;
	GUI_dl_status();
	return 1;
	}}}
int  sGui::GUI_dlStatus_stop    (sPacket *      ) {{{ // (0xDA)
	logSend ('<', this, CO_STOP_DL_STATUS, false);
	if(!auth) return 0;
	dl_status  = false;
	return 1;
	}}}
int  sGui::GUI_ulStatus_start   (sPacket *      ) {{{ // (0xDB)
	if(!auth) return 0;
	logSend ('<', this, CO_START_UL_STATUS, false);
	ul_status  = true;
	GUI_ul_status();
	return 1;
	}}}
int  sGui::GUI_ulStatus_stop    (sPacket *      ) {{{ // (0xDC)
	if(!auth) return 0;
	logSend ('<', this, CO_STOP_UL_STATUS, false);
	ul_status  = false;
	return 1;
	}}}
int  sGui::GUI_serverDelete     (sPacket *packet) {{{ // (0xDD)
	if (packet == NULL || !auth) return 0;
	logSend ('<', this, CO_DELETE_SERVER, false);
	struct in_addr  ip;
	uint16_t	new_port;
	ip.s_addr = packet->GET_U4 ();
	new_port  = packet->GET_U2 ();
	delServer (ip, new_port, -7);
	return 1;
	}}}
int  sGui::GUI_serverAdd        (sPacket *packet) {{{ // (0xDE)
	if (packet == NULL || !auth) return 0;
	logSend ('<', this, CO_ADD_SERVER, false);
	struct in_addr  ip;
	uint16_t	new_port;
	ip.s_addr = packet->GET_U4 ();
	new_port  = packet->GET_U2 ();
	saveServer (ip, new_port, 0, 0, true);
	return 1;
	}}}
int  sGui::GUI_serverPrio       (sPacket *packet) {{{ // (0xDF)
	if (packet == NULL) return 0;
	logSend ('<', this, CO_SETPRI_SERVER, false);
	if(!auth) return 0;
	struct in_addr  ip;
	ip.s_addr = packet->GET_U4 ();
	uint16_t new_port = packet->GET_U2 ();
	uint8_t  new_prio = packet->GET_U1 ();
	setServer_prio (ip, new_port, new_prio);
	return 1;
	}}}
int  sGui::GUI_get_shareFiles   (sPacket *      ) {{{ // (0xE0)
	if (!auth) return 0;
	logSend ('<', this, CO_GET_SHARE_FILES, false);
	GUI_shareFiles();
	Code(0xE0); // ? 
	return 1;
	}}}
int  sGui::GUI_get_option       (sPacket *      ) {{{ // (0xE1)
	if (!auth) return 0;
	logSend ('<', this, CO_GET_OPTIONS, false);
	GUI_send_option ();
	return 1;
	}}}
int  sGui::GUI_add_download     (sPacket *packet) {{{ // (0xE2)
	if (packet == NULL) return 0;
	logSend ('<', this, CO_REQ_NEW_DOWNLOAD, false);
	tHash hash;
	packet->GET_HASH(hash);
	if (NULL != forHash(hash)) return 1; // dl already exists
	packet->GET_U4  (    );
	packet->GET_U2  (    );
	class sTag *tag = parseTag (packet);
	if (tag == NULL) return 0;
	if (tag->name != NULL && tag->size > 0)
		new sFile (hash, tag->name, tag->size, incoming);
	delete tag;
	return 1;
	}}}
int  sGui::GUI_req_part_status  (sPacket *packet) {{{ // (0xE3)
	if (packet == NULL) return 0;
	logSend ('<', this, CO_GET_GAP_DETAILS, false);
	tHash hash;
	packet->GET_HASH(hash);
	return GUI_res_part_status (hash);
	}}}
int  sGui::GUI_core_status_get	 (sPacket *      ) {{{ // (0xe4)
	if (!auth) return 0;
	return GUI_core_status ();
	}}}

int sGui::Donkey_gui (class sPacket *packet) {{{
	if (packet == NULL) return 0;
	if (packet->Proto() != OP_EDONKEYHEADER) return 0;
	switch (packet->Command ()) {
//	case 0xCC : More Search
//	case 0xCD : Search User
//	case 0xD8 : Set Share Dirs
//	case 0xCE : ext Search user
	case 0xD6 : return 1; // Get Friend List (if anyone wan't it)
//	case 0xD4 : View Friend Files
//	case 0xE4 : return 1; // Request Client Stats
	case 0x64 : return GUI_login		(packet);
	case 0x65 : return GUI_shutdown		(packet);
	case 0x66 : return GUI_txt_command	(packet);
	case 0xC8 : return GUI_connect   	(packet);
	case 0xC9 : return GUI_disconnect	(packet);
	case 0xCA : return GUI_search		(packet);
	case 0xCB : return GUI_search_ext	(packet);
	case 0xCF : return req_download 	(packet);
	case 0xD0 : return GUI_dlPause		(packet); // Pause  Download <HASH>
	case 0xD1 : return GUI_dlResume		(packet); // Resume Download <HASH>
	case 0xD2 : return GUI_cancelDownload	(packet);
	case 0xD3 : return GUI_download_prio	(packet); // Prio   Download <HASH><8 bit>
	case 0xD5 : return GUI_get_serverlist	(packet);
	case 0xD7 : return GUI_req_shared_dir	(packet);
	case 0xD9 : return GUI_dlStatus_start	(packet);
	case 0xDA : return GUI_dlStatus_stop	(packet);
	case 0xDB : return GUI_ulStatus_start	(packet);
	case 0xDC : return GUI_ulStatus_stop 	(packet);
	case 0xDD : return GUI_serverDelete	(packet);
	case 0xDE : return GUI_serverAdd	(packet);
	case 0xDF : return GUI_serverPrio	(packet);
	case 0xE0 : return GUI_get_shareFiles	(packet);
	case 0xE1 : return GUI_get_option	(packet);
	case 0xE2 : return GUI_add_download	(packet);
	case 0xE3 : return GUI_req_part_status	(packet);
	case 0xE4 : return GUI_core_status_get	(packet);
	default   : logSend ('<', this, packet->Command (), false);
		    break;
		}
	packet->Dump ();
	return 0;
	}}}

void sGui::readWork(void) {{{
	if (READ->Proto() == OP_EDONKEYHEADER) Donkey_gui (READ);
	else 	READ->Dump();
	READ->readReset();
	}}}

void    sGui::Work (void) {{{
	if (!validSocket()) return;
	if(!auth) return;
	if (dl_timeout + 3 < currentTime || dl_timeout > currentTime) dl_timeout = currentTime;
	if (ul_timeout + 3 < currentTime || ul_timeout > currentTime)  ul_timeout = currentTime;
	if(dl_timeout + 1 < currentTime && dl_status) GUI_dl_status();
	if(ul_timeout + 1 < currentTime && ul_status) GUI_ul_status();
	return;
	}}}
/*
 * vim600: fdm=marker
 */


