#include "cygwin.h"
#include "donkey.h"
#include "cHTTP.h"
#include "sSocket.h"
#include "sGui.h"
#include "misc.h"
#include "protocol.h"
#include "sFile.h"
#include "sPacket.h"
#include "kademlia.h"
#include "sSource.h"
#include "db_macro.h"

char head200[] ="HTTP/1.1 200 OK\r\n"
		"Status: 200\r\n"
		"Pragma: no-cache,no-cache\r\n";
char head401[]="HTTP/1.1 401 Aut horization Required\r\n"
		"Date: Mon, 17 Feb 2003 21:53:45 GMT\r\n"
		"Server: Apache/1.3.22 (Unix) PHP/4.1.0\r\n"
		"WWW-Authenticate: Basic realm=\"cDonkey\"\r\n"
		"Content-Type: text/html; charset=iso-8859-1\r\n\r\n";
char CON_CLOSE[]="Connection: close\r\n";
	

const char url2txt( const char* what ) {{{
	register char digit;
	digit = (what[0] >= 'A' ? ((what[0] & 0x4F) - 'A')+10 : (what[0] - '0'));
	digit *= 16;
	digit += (what[1] >='A' ? ((what[1] & 0x4F) - 'A')+10 : (what[1] - '0'));
	return digit;
	}}}
const char*url_decode(char *hexstr) {{{
	if ( hexstr == NULL || strlen( hexstr ) == 0 ) return "";
	unsigned int x,y;
	for ( x = 0, y = 0; hexstr[y]; x++, y++ ) {
		if( (hexstr[x] = hexstr[y]) == '+' ) {
			hexstr[x] = ' ';
			continue;
			}
		if ( hexstr[x] == '%' ) {
			hexstr[x] = url2txt(&hexstr[y+1]);
			y+=2;
			}
		}
	hexstr[x] = '\0';
	return hexstr;
	}}}
void split_url(char *url) {{{
	char *query = strchr(url, '?');
	if (query == NULL) return;
	query++;
	while(0 != *query) {
		char *next = strchr(query, '&');
		if (next != NULL) {
			*next = 0;
			next++;
			}
		char *val = strchr(query, '=');
		if (val != NULL) {
			*val = 0;
			val++;
			printf("%s => %s\n", url_decode(query), url_decode(val));
			}
		if (next == NULL) break;
		query = next;
		}
	}}}
cHTTP::cHTTP        (class cSocket *FD_new, struct in_addr, uint16_t) : sSocket(FD_new) {{{
	url  = NULL;
	state = 0;
	Type = tCommand;
	auth = false;
	if (mask != 0) doWork(mask);
	}}}
cHTTP::~cHTTP        (void) {{{
	if (url  != NULL) Free2 (url );
	}}}

int  cHTTP::Parse(unsigned char *input) {{{
	char *pos  = reinterpret_cast<char*>(input);
	char *mark = NULL;
	unsigned char  buffer[1024];
	KeyMap::iterator i = param.find(std::string("fkt"));
	bool FILTER = false;
	if (i != param.end()) {
		FILTER = (i->second == std::string("filter"));
		}
	while (NULL != (mark = strchr(pos, '~'))) {
		mark[0] = 0;
		Write(reinterpret_cast<unsigned char*>(pos), strlen(pos));
		pos = mark + 1;
		if (NULL == (mark = strchr(pos, '~'))) break;
		mark[0] = 0;
		if (0 == strcmp(pos, "PORT_CLIENT"     )) {{{
			Write(buffer, snprintf(reinterpret_cast<char*>(buffer), 1024, "%u", pref.ports.client));
			}}} else
		if (0 == strcmp(pos, "PORT_GUI"        )) {{{
			Write(buffer, snprintf(reinterpret_cast<char*>(buffer), 1024, "%u", pref.ports.gui     ));
			}}} else
		if (0 == strcmp(pos, "PORT_SERVER"     )) {{{
			Write(buffer, snprintf(reinterpret_cast<char*>(buffer), 1024, "%u", pref.ports.server  ));
			}}} else
		if (0 == strcmp(pos, "PORT_TELNET"     )) {{{
			Write(buffer, snprintf(reinterpret_cast<char*>(buffer), 1024, "%u", pref.ports.clc     ));
			}}} else
		if (0 == strcmp(pos, "PORT_WEB"        )) {{{
			Write(buffer, snprintf(reinterpret_cast<char*>(buffer), 1024, "%u", pref.ports.web     ));
			}}} else
		if (0 == strcmp(pos, "PORT_UDP_EMULE"  )) {{{
			Write(buffer, snprintf(reinterpret_cast<char*>(buffer), 1024, "%u", pref.ports.emule   ));
			}}} else
		if (0 == strcmp(pos, "PORT_UDP_OVERNET")) {{{
			Write(buffer, snprintf(reinterpret_cast<char*>(buffer), 1024, "%u", pref.ports.kademlia));
			}}} else
		if (0 == strcmp(pos, "LISTE_SERVER"    )) {{{
			const struct eServers *s =nextServers (true);
			unsigned cnt = 0 ;
			while (s != NULL) {     // we have reserved the maximum possible size
				cnt++;
				Write(buffer, snprintf(reinterpret_cast<char*>(buffer), 1024,
					"<tr id=server-%i>"
						"<td class=ip  >%s  &nbsp;</td>"
						"<td class=port>%u  &nbsp;</td>"
						"<td class=prio>%u  &nbsp;</td>"
						"<td class=ping>%lu &nbsp;</td>"
						"<td class=user>%u  &nbsp;</td>"
						"<td class=file>%u  &nbsp;</td>"
						"<td class=name>%s  &nbsp;</td>"
						"<td class=desc>%s  &nbsp;</td>"
					"</tr>\n", cnt, inet_ntoa(s->ip), 
						s->port, 
						s->prio, 
						(currentTime - s->last_action),
						s->user,
						s->file,
						s->name,
						s->desc
						));
				s = nextServers (false);
				}
			}}} else
		if (0 == strcmp(pos, "LISTE_SOURCE"    )) {{{
			int cnt = 0;
			SOURCE_LIST {
				cnt ++;
				Write(buffer, snprintf(reinterpret_cast<char*>(buffer), 1024,
					"<tr id=source-%i>"
						"<td class=dir    >%c  </td>"
						"<td class=ip     >%s  &nbsp;</td>"
						"<td class=ratio  >%li </td>"
						"<td class=slot-dl>%i  </td>"
						"<td class=name   >%s &nbsp;</td>"
						"<td class=hash-dl>%s &nbsp;</td>"
						"<td class=hash-ul>%s &nbsp;</td>"
					"</tr>",cnt,
						((akt->this_port == pref.ports.client)?'<':'>'),
						inet_ntoa(akt->peer_ip),
						akt->ratio,
						akt->dl_slot,
						akt->name,
						hash_bin2hex(akt->dl_hash),
						hash_bin2hex(akt->ul_hash)
						));
				}
			}}}
		if (0 == strcmp(pos, "LISTE_SOURCE_RAW")) {{{
			int cnt = 0;
			int err = 0;
			struct eSources tmp;
			bzero (&tmp, sizeof(tmp));
			time_t zeit = 0;
			INIT_DBT(key, &zeit, sizeof(zeit));
			INIT_DBT(val, &tmp , sizeof(tmp ));
			DBC *c3 = NULL;
			DB_cursor2 (src_time, c3);
			while (0 == (err = c3->c_get (c3, &key, &val, DB_NEXT))) { // search next source for connect
				ASSERT(sizeof(tmp ) == val.size);
				ASSERT(sizeof(zeit) == key.size);
				cnt ++;
				char zeit_tmp[100];
				strftime(zeit_tmp, 100, "%m-%d %H:%M.%S", gmtime(&tmp.next_time));
				Write(buffer, snprintf(reinterpret_cast<char*>(buffer), 1024,
					"<tr id=source-%-4i>"
						"<td class=ip>%15s</td>"
						"<td class=aktive >%i</td>"
						"<td class=hash-dl>%32s</td>"
						"<td class=hash-ul>%s</td>"
					"</tr>\n",cnt,
						inet_ntoa(tmp.ip),
						tmp.aktive,
						hash_bin2hex(tmp.hash_dl),
						zeit_tmp
						));
//				if (tmp.next_time - 3600*24*3 > currentTime) Delete_src(tmp.ip);
				}
			if (err != DB_NOTFOUND) DB_error();
			DB_c_close(c3);
			}}}
		if (0 == strcmp(pos, "LIST_FILES")) {{{
			size_t filter_size = 0;
			if (FILTER) {
				i = param.find(std::string("file_size"));
				if (i != param.end()) {
					filter_size = atoi(i->second.c_str());
					}
				}
			int cnt = 0;
			FILE_LIST {
				cnt++;
				if (filter_size != 0 && akt->size != filter_size) continue;
				Write(buffer, snprintf(reinterpret_cast<char*>(buffer), 1024,
					"<tr id=file-%i>"
						"<td class=hash		>%s</td>"
						"<td class=name		>%s</td>"
						"<td class=size		>%u</td>"
						"<td class=transfered	>%u</td>"
					"</div>", cnt, hash_bin2hex(akt->hash),akt->Name(), akt->size, akt->transfered
				      ));
				}
			}}}
		pos = mark + 1;
		}
	Write(reinterpret_cast<unsigned char*>(pos), strlen(pos));
	return 1;
	}}}

struct	sURL2File {
	const char *url;
	const char *file;
	};

const struct sURL2File URLs[] = {
	{"/"			, "http.dat/index.html"},
	{"/files.html"		, "http.dat/files.html"},
	{"/server.html"		, "http.dat/server.html"},
	{"/sources.html"	, "http.dat/sources.html"},
	{"/sources_raw.html"	, "http.dat/sources_raw.html"},
	{"/global.css"		, "http.dat/global.css"},
	{"/main.html"		, "http.dat/main.html"},
	{"/options.html"	, "http.dat/options.html"},
	{NULL,NULL}
	};

int  cHTTP::doRead_high   (void) {{{
//	printf("cHTTP::doRead_high\n");
	size_t len = 0;
	if (state == 3) {{{ // Close
		Close();
		return 0;
		}}}
	while (read_len > 0) {{{
		for (len = 0; len < read_len; len++) {
			if (read_buf[len] == '\n' || read_buf[len] == '\r') break;
			}
		if (len == read_len) return 0;
		if (len> 4095) {{{ // too long line
			Read(read_buf, len);
			return 0;
			}}}
		char buffer[4986];
		Read(reinterpret_cast<unsigned char*>(buffer), len);
		if (read_buf[0] == '\r') Read(read_buf, 1);
		if (read_buf[0] == '\n') Read(read_buf, 1);
		buffer[len]=0;
		if (strncmp(buffer, "GET ", 4) == 0 && state == 0) {{{ // catch the URL
			char *HTTP = strstr(buffer, " HTTP");
			if (HTTP != NULL) *HTTP = 0;
			char *URL = buffer + 4;
			char *query = strchr(URL, '?');
			if (query == NULL) {
				url = STRDUP (URL);
				state = 1;
				continue;
				}
			query[0] = 0;
			url = STRDUP (URL);
			state = 1;
			query++;
			while(0 != *query) {
				char *next = strchr(query, '&');
				if (next != NULL) {
					*next = 0;
					next++;
					}
				char *val = strchr(query, '=');
				if (val != NULL) {
					*val = 0;
					val++;
					param[std::string(url_decode(query))] = std::string(url_decode(val));
					printf("%s => %s\n", url_decode(query), url_decode(val));
					}
				if (next == NULL) break;
				query = next;
				}
			continue;
			}}}
		if (buffer[0] == 0 && state == 1) {{{
			state = 2;
			continue;
			}}}
		if (buffer[0] == 0 && state == 2) break;
		char *p = strstr(buffer, ": ");
		if (p == NULL) continue;
		p[0] = 0;
		p += 2;
		if (0 == strcmp(buffer, "Authorization")) {{{ // User check
			char check1[256];
			char check2[256];
			len = 256;
			snprintf(check1, 256, "%s:%s", ctrl_user, ctrl_pass);
			encode64(check1, strlen(check1), check2, 256, &len);
			check2[len] = 0;
			snprintf(check1, 256, "Basic %s", check2);
			auth = (0 == strcmp(check1, p));
			}}}
		header[std::string(buffer)] = std::string(p);
		continue;
		}}}
	state = 3;
	size_t in_size = 0;
	unsigned char *INPUT = NULL;
	int idx = 0;
	if (param["fkt"] == "Shutdown") {
		running = false;
		}
	if (!auth) {{{ // Auth failed
		Write(reinterpret_cast<unsigned char*>(head401),strlen(head401));
		INPUT = file("http.dat/401.html", &in_size);
		goto ende_1;
		}}}
	Write(reinterpret_cast<unsigned char*>(head200), strlen(head200));
	Write(reinterpret_cast<const unsigned char*>("Content-Type: text/html; charset=iso-8859-1\r\n"), strlen("Content-Type: text/html; charset=iso-8859-1\r\n"));
	if (url == NULL) goto error_1;
	for(idx = 0; URLs[idx].url != NULL; idx++)
		if (0 == strcmp(url, URLs[idx].url)) {{{
			INPUT = file(URLs[idx].file, &in_size);
			if (INPUT == NULL) goto error_1;
			goto ende_1;
			}}}
error_1:if (INPUT != NULL) free (INPUT);
	INPUT = NULL;
ende_1:	if (INPUT == NULL) {
		INPUT = reinterpret_cast<unsigned char*>(STRDUP ("<html><BODY>cDonkey says hello!<br>Internal NULL error!</BODY></html>\n"));
		in_size = strlen(reinterpret_cast<char*>(INPUT));
		}
	Write(reinterpret_cast<const unsigned char*>(CON_CLOSE),strlen(CON_CLOSE));
	Write(reinterpret_cast<const unsigned char*>("\r\n"), strlen("\r\n"));
	Parse(INPUT);
	Write(reinterpret_cast<const unsigned char*>("\0"), 1);
	if (INPUT != NULL) free (INPUT);
	addEvent(32);
	return 1;
	}}}

/*
 * vim600: fdm=marker
 */
