1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2011 Angel Vidal ( kry@amule.org )
5 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
6 // Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net )
7 //
8 // Any parts of this program derived from the xMule, lMule or eMule project,
9 // or contributed by third-party developers are copyrighted by their
10 // respective authors.
11 //
12 // This program is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 2 of the License, or
15 // (at your option) any later version.
16 //
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
25 //
26 
27 
28 #include <cctype>	// Needed for std::toupper()
29 #include <wx/math.h>	// Needed for cos, M_PI
30 #include <string>	// Do_not_auto_remove (g++-4.0.1)
31 
32 #include <wx/datetime.h>
33 
34 //-------------------------------------------------------------------
35 
36 #include <wx/tokenzr.h>		// for wxTokenizer
37 #include <wx/wfstream.h>
38 
39 #include <ec/cpp/ECFileConfig.h>	// Needed for CECFileConfig
40 #include <ec/cpp/ECSpecialTags.h>
41 #include <common/MD5Sum.h>
42 #include <common/Format.h>		// Needed for CFormat
43 
44 #include <protocol/ed2k/Constants.h>	// Needed for PARTSIZE
45 #include "Constants.h"			// Needed for PR_*
46 
47 //-------------------------------------------------------------------
48 
49 #include "WebSocket.h"		// Needed for StopSockets()
50 #include <amuleIPV4Address.h>
51 
52 #include "php_syntree.h"
53 #include "php_core_lib.h"
54 
55 //-------------------------------------------------------------------
56 typedef uint32_t COLORTYPE;
57 
58 #ifdef RGB
59 #undef RGB
60 #endif
61 
RGB(int r,int g,int b)62 inline unsigned long RGB(int r, int g, int b)
63 {
64 	return ((b & 0xff) << 16) | ((g & 0xff) << 8) | (r & 0xff);
65 }
66 
set_rgb_color_val(unsigned char * start,uint32 val,unsigned char mod)67 inline void set_rgb_color_val(unsigned char *start, uint32 val, unsigned char mod)
68 {
69 	unsigned char r = val, g = val >> 8, b = val >> 16;
70 	start[0] = ( r > mod ) ? (r - mod) : 1;
71 	start[1] = ( g > mod ) ? (g - mod) : 1;
72 	start[2] = ( b > mod ) ? (b - mod) : 1;
73 }
74 
_SpecialChars(wxString str)75 wxString _SpecialChars(wxString str) {
76 	str.Replace(wxT("&"),wxT("&amp;"));
77 	str.Replace(wxT("<"),wxT("&lt;"));
78 	str.Replace(wxT(">"),wxT("&gt;"));
79 	str.Replace(wxT("\""),wxT("&quot;"));
80 	return str;
81 }
82 
GetHigherPrio(uint32 prio,bool autoprio)83 static uint8 GetHigherPrio(uint32 prio, bool autoprio)
84 {
85 	if (autoprio) {
86 		return PR_LOW;
87 	} else {
88 		switch (prio) {
89 			case PR_LOW: return PR_NORMAL;
90 			case PR_NORMAL: return PR_HIGH;
91 			case PR_HIGH: return PR_AUTO;
92 			case PR_AUTO: return PR_LOW;
93 			default: return PR_AUTO;
94 		}
95 	}
96 }
97 
GetHigherPrioShared(uint32 prio,bool autoprio)98 static uint8 GetHigherPrioShared(uint32 prio, bool autoprio)
99 {
100 	if (autoprio) {
101 		return PR_VERYLOW;
102 	} else {
103 		switch (prio) {
104 			case PR_VERYLOW: return PR_LOW;
105 			case PR_LOW: return PR_NORMAL;
106 			case PR_NORMAL: return PR_HIGH;
107 			case PR_HIGH: return PR_VERYHIGH;
108 			case PR_VERYHIGH: return PR_POWERSHARE;
109 			case PR_POWERSHARE: return PR_AUTO;
110 			case PR_AUTO: return PR_VERYLOW;
111 			default: return PR_AUTO;
112 		}
113 	}
114 }
115 
116 
GetLowerPrio(uint32 prio,bool autoprio)117 static uint8 GetLowerPrio(uint32 prio, bool autoprio)
118 {
119 	if (autoprio) {
120 		return PR_HIGH;
121 	} else {
122 		switch (prio) {
123 			case PR_LOW: return PR_AUTO;
124 			case PR_NORMAL: return PR_LOW;
125 			case PR_HIGH: return PR_NORMAL;
126 			case PR_AUTO: return PR_HIGH;
127 			default: return PR_AUTO;
128 		}
129 	}
130 }
131 
GetLowerPrioShared(uint32 prio,bool autoprio)132 static uint8 GetLowerPrioShared(uint32 prio, bool autoprio)
133 {
134 	if (autoprio) {
135 		return PR_POWERSHARE;
136 	} else {
137 		switch (prio) {
138 			case PR_VERYLOW: return PR_AUTO;
139 			case PR_LOW: return PR_VERYLOW;
140 			case PR_NORMAL: return PR_LOW;
141 			case PR_HIGH: return PR_NORMAL;
142 			case PR_VERYHIGH: return PR_HIGH;
143 			case PR_POWERSHARE: return PR_VERYHIGH;
144 			case PR_AUTO: return PR_POWERSHARE;
145 			default: return PR_AUTO;
146 		}
147 	}
148 }
149 
150 /*
151  * Url string decoder
152  */
Decode(const wxString & url)153 wxString CURLDecoder::Decode(const wxString& url)
154 {
155 	size_t n = url.length();
156 	std::vector<char> buffer(n + 1);
157 	size_t i, j;
158 
159 	for (i = 0, j = 0; i < n; i++, j++) {
160 		if (url[i] == wxT('+')) {
161 			buffer[j] = ' ';
162 		} else if (url[i] == wxT('%') && i < n - 2) {
163 			char ch1 = std::toupper(url[i+1]);
164 			char ch2 = std::toupper(url[i+2]);
165 			if (((ch1 >= '0' && ch1 <= '9') || (ch1 >= 'A' && ch1 <= 'F')) &&
166 			    ((ch2 >= '0' && ch2 <= '9') || (ch2 >= 'A' && ch2 <= 'F'))) {
167 				i += 2;
168 				buffer[j] = ((ch1 > '9' ? ch1 - 'A' + 10 : ch1 - '0') << 4) | (ch2 > '9' ? ch2 - 'A' + 10 : ch2 - '0');
169 			} else {
170 				// Invalid %-escape sequence
171 				buffer[j] = url[i];
172 			}
173 		} else {
174 			buffer[j] = url[i];
175 		}
176 	}
177 	buffer[j] = '\0';
178 
179 	return UTF82unicode(&buffer[0]);
180 }
181 
CParsedUrl(const wxString & url)182 CParsedUrl::CParsedUrl(const wxString &url)
183 {
184 	if ( url.Find('/') != -1 ) {
185 		m_path = url.BeforeFirst('/');
186 		m_file = url.AfterFirst('/');
187 	}
188 
189 	if ( url.Find('?') != -1 ) {
190 		m_file.Truncate(m_file.Find('?'));
191 
192 		wxString params = url.AfterFirst('?');
193 
194 		wxStringTokenizer tkz(params, wxT("&"));
195 		while ( tkz.HasMoreTokens() ) {
196 			wxString param_val = tkz.GetNextToken();
197 			wxString key = param_val.BeforeFirst('=');
198 			wxString val = param_val.AfterFirst('=');
199 			val = CURLDecoder::Decode(val);
200 			if ( m_params.count(key) ) {
201 				m_params[key] = m_params[key] + wxT("|") + val;
202 			} else {
203 				m_params[key] = val;
204 			}
205 		}
206     }
207 }
208 
ConvertParams(std::map<std::string,std::string> & dst)209 void CParsedUrl::ConvertParams(std::map<std::string, std::string> &dst)
210 {
211 	for(std::map<wxString, wxString>::iterator i = m_params.begin(); i != m_params.end(); ++i) {
212 		std::string key(unicode2char(i->first)), value(unicode2char(i->second));
213 		dst[key] = value;
214 	}
215 }
216 
217 #ifndef ASIO_SOCKETS
BEGIN_EVENT_TABLE(CWebServerBase,wxEvtHandler)218 BEGIN_EVENT_TABLE(CWebServerBase, wxEvtHandler)
219 	EVT_SOCKET(ID_WEBLISTENSOCKET_EVENT, CWebServerBase::OnWebSocketServerEvent)
220 	EVT_SOCKET(ID_WEBCLIENTSOCKET_EVENT, CWebServerBase::OnWebSocketEvent)
221 END_EVENT_TABLE()
222 #endif
223 
224 CWebServerBase::CWebServerBase(CamulewebApp *webApp, const wxString& templateDir) :
225 	m_ServersInfo(webApp), m_SharedFileInfo(webApp), m_DownloadFileInfo(webApp, &m_ImageLib),
226 	m_UploadsInfo(webApp), m_SearchInfo(webApp), m_Stats(500, webApp),
227 	m_ImageLib(templateDir)
228 {
229 	webInterface = webApp;
230 
231 	//
232 	// Init stat graphs
233 #ifdef WITH_LIBPNG
234 	m_ImageLib.AddImage(new CDynStatisticImage(200, true, m_Stats.DownloadSpeed()),
235 		wxT("/amule_stats_download.png"));
236 	m_ImageLib.AddImage(new CDynStatisticImage(200, true, m_Stats.UploadSpeed()),
237 		wxT("/amule_stats_upload.png"));
238 	m_ImageLib.AddImage(new CDynStatisticImage(200, false, m_Stats.ConnCount()),
239 		wxT("/amule_stats_conncount.png"));
240 	m_ImageLib.AddImage(new CDynStatisticImage(200, false, m_Stats.KadCount()),
241 		wxT("/amule_stats_kad.png"));
242 #endif
243 
244 	m_upnpEnabled = webInterface->m_UPnPWebServerEnabled;
245 	m_upnpTCPPort = webInterface->m_UPnPTCPPort;
246 
247 #ifdef ASIO_SOCKETS
248 	m_AsioService = new CAsioService;
249 #endif
250 }
251 
252 
253 // Probably always terminated by Ctrl-C or kill, but make a clean shutdown of the service anyway
~CWebServerBase()254 CWebServerBase::~CWebServerBase()
255 {
256 #ifdef ASIO_SOCKETS
257 	m_AsioService->Stop();
258 	delete m_AsioService;
259 #endif
260 }
261 
262 
263 //sends output to web interface
Print(const wxString & s)264 void CWebServerBase::Print(const wxString &s)
265 {
266 	webInterface->Show(s);
267 }
268 
269 
StartServer()270 void CWebServerBase::StartServer()
271 {
272 #ifdef ENABLE_UPNP
273 	if (m_upnpEnabled) {
274 		m_upnpMappings.resize(1);
275 		m_upnpMappings[0] = CUPnPPortMapping(
276 			webInterface->m_WebserverPort,
277 			"TCP",
278 			true,
279 			"aMule TCP Webserver Socket");
280 		m_upnp = new CUPnPControlPoint(m_upnpTCPPort);
281 		m_upnp->AddPortMappings(m_upnpMappings);
282 	}
283 #endif
284 
285 	amuleIPV4Address addr;
286 	addr.AnyAddress();
287 	addr.Service(webInterface->m_WebserverPort);
288 
289 	m_webserver_socket = new CWebLibSocketServer(addr, MULE_SOCKET_REUSEADDR, this);
290 #ifndef ASIO_SOCKETS
291 	m_webserver_socket->SetEventHandler(*this, ID_WEBLISTENSOCKET_EVENT);
292 	m_webserver_socket->SetNotify(wxSOCKET_CONNECTION_FLAG);
293 #endif
294 	m_webserver_socket->Notify(true);
295 	if (!m_webserver_socket->IsOk()) {
296 		delete m_webserver_socket;
297 		m_webserver_socket = 0;
298 	}
299 
300 }
301 
StopServer()302 void CWebServerBase::StopServer()
303 {
304 	if ( m_webserver_socket ) {
305 		delete m_webserver_socket;
306 	}
307 #ifdef ENABLE_UPNP
308 	if (m_upnpEnabled) {
309 		m_upnp->DeletePortMappings(m_upnpMappings);
310 		delete m_upnp;
311 	}
312 #endif
313 }
314 
315 #ifndef ASIO_SOCKETS
OnWebSocketServerEvent(wxSocketEvent & WXUNUSED (event))316 void CWebServerBase::OnWebSocketServerEvent(wxSocketEvent& WXUNUSED(event))
317 {
318 	m_webserver_socket->OnAccept();
319 }
320 #endif
321 
CWebLibSocketServer(const class amuleIPV4Address & adr,int flags,CWebServerBase * webServerBase)322 CWebLibSocketServer::CWebLibSocketServer(const class amuleIPV4Address& adr, int flags, CWebServerBase * webServerBase)
323 	:	CLibSocketServer(adr, flags),
324 		m_webServerBase(webServerBase)
325 {
326 }
327 
OnAccept()328 void CWebLibSocketServer::OnAccept()
329 {
330 	CWebSocket *client = new CWebSocket(m_webServerBase);
331 
332     if (AcceptWith(*client, false) ) {
333 		m_webServerBase->webInterface->Show(_("web client connection accepted\n"));
334     } else {
335 		delete client;
336 		m_webServerBase->webInterface->Show(_("ERROR: cannot accept web client connection\n"));
337     }
338 }
339 
340 #ifndef ASIO_SOCKETS
OnWebSocketEvent(wxSocketEvent & event)341 void CWebServerBase::OnWebSocketEvent(wxSocketEvent& event)
342 {
343 	CWebSocket *socket = dynamic_cast<CWebSocket *>(event.GetSocket());
344     wxCHECK_RET(socket, wxT("Socket event with a NULL socket!"));
345     switch(event.GetSocketEvent()) {
346     case wxSOCKET_LOST:
347         socket->OnLost();
348         break;
349     case wxSOCKET_INPUT:
350         socket->OnReceive(0);
351         break;
352     case wxSOCKET_OUTPUT:
353         socket->OnSend(0);
354         break;
355     case wxSOCKET_CONNECTION:
356         break;
357     default:
358         wxFAIL;
359         break;
360     }
361 }
362 #endif
363 
ProcessImgFileReq(ThreadData Data)364 void CScriptWebServer::ProcessImgFileReq(ThreadData Data)
365 {
366 	webInterface->DebugShow(wxT("**** imgrequest: ") + Data.sURL + wxT("\n"));
367 
368 	const CSession* session = CheckLoggedin(Data);
369 
370 	// To prevent access to non-template images, we disallow use of paths in filenames.
371 	wxString imgName = wxT("/") + wxFileName(Data.parsedURL.File()).GetFullName();
372 	CAnyImage *img = m_ImageLib.GetImage(imgName);
373 
374 	// Only static images are available to visitors, in order to prevent
375 	// information leakage, but still allowing images on the login page.
376 	if (img && (session->m_loggedin || dynamic_cast<CFileImage*>(img))) {
377 		int img_size = 0;
378 		unsigned char* img_data = img->RequestData(img_size);
379 		// This unicode2char is ok.
380 		Data.pSocket->SendContent(unicode2char(img->GetHTTP()), img_data, img_size);
381 	} else if (!session->m_loggedin) {
382 		webInterface->DebugShow(wxT("**** imgrequest: failed, not logged in\n"));
383 		ProcessURL(Data);
384 	} else {
385 		webInterface->DebugShow(wxT("**** imgrequest: failed\n"));
386 	}
387 }
388 
389 // send EC request and discard output
Send_Discard_V2_Request(CECPacket * request)390 void CWebServerBase::Send_Discard_V2_Request(CECPacket *request)
391 {
392 	const CECPacket *reply = webInterface->SendRecvMsg_v2(request);
393 	const CECTag *tag = NULL;
394 	if (reply) {
395 		if ( reply->GetOpCode() == EC_OP_STRINGS ) {
396 			for (CECPacket::const_iterator it = reply->begin(); it != reply->end(); ++it) {
397 				tag = & *it;
398 				if (tag->GetTagName() == EC_TAG_STRING) {
399 					webInterface->Show(tag->GetStringData());
400 				}
401 			}
402 		} else if (reply->GetOpCode() == EC_OP_FAILED) {
403 			tag = reply->GetFirstTagSafe();
404 			if (tag->IsString()) {
405 				webInterface->Show(
406 					CFormat(_("Request failed with the following error: %s.")) %
407 					wxString(wxGetTranslation(tag->GetStringData())));
408 			} else {
409 				webInterface->Show(_("Request failed with an unknown error."));
410 			}
411 		}
412 		delete reply;
413 	}
414 }
415 
416 //
417 // Command interface
418 //
Send_SharedFile_Cmd(wxString file_hash,wxString cmd,uint32 opt_arg)419 void CWebServerBase::Send_SharedFile_Cmd(wxString file_hash, wxString cmd, uint32 opt_arg)
420 {
421 	CECPacket *ec_cmd = 0;
422 	CMD4Hash fileHash;
423 	wxCHECK2(fileHash.Decode(file_hash), /* Do nothing. */ );
424 
425 	CECTag hashtag(EC_TAG_KNOWNFILE, fileHash);
426 	if (cmd == wxT("prio")) {
427 		ec_cmd = new CECPacket(EC_OP_SHARED_SET_PRIO);
428 		hashtag.AddTag(CECTag(EC_TAG_PARTFILE_PRIO, (uint8)opt_arg));
429 	} else if ( cmd == wxT("prioup") ) {
430 		SharedFile *file = m_SharedFileInfo.GetByHash(fileHash);
431 		if ( file ) {
432 			ec_cmd = new CECPacket(EC_OP_SHARED_SET_PRIO);
433 			hashtag.AddTag(CECTag(EC_TAG_PARTFILE_PRIO,
434 				GetHigherPrioShared(file->nFilePriority, file->bFileAutoPriority)));
435 		}
436 	} else if ( cmd == wxT("priodown") ) {
437 		SharedFile *file = m_SharedFileInfo.GetByHash(fileHash);
438 		if ( file ) {
439 			ec_cmd = new CECPacket(EC_OP_SHARED_SET_PRIO);
440 			hashtag.AddTag(CECTag(EC_TAG_PARTFILE_PRIO,
441 				GetLowerPrioShared(file->nFilePriority, file->bFileAutoPriority)));
442 		}
443 	}
444 
445 	if ( ec_cmd ) {
446 		ec_cmd->AddTag(hashtag);
447 		Send_Discard_V2_Request(ec_cmd);
448 		delete ec_cmd;
449 	}
450 }
451 
Send_ReloadSharedFile_Cmd()452 void CWebServerBase::Send_ReloadSharedFile_Cmd()
453 {
454 	CECPacket ec_cmd(EC_OP_SHAREDFILES_RELOAD);
455 	Send_Discard_V2_Request(&ec_cmd);
456 }
457 
Send_DownloadFile_Cmd(wxString file_hash,wxString cmd,uint32 opt_arg)458 void CWebServerBase::Send_DownloadFile_Cmd(wxString file_hash, wxString cmd, uint32 opt_arg)
459 {
460 	CECPacket *ec_cmd = 0;
461 	CMD4Hash fileHash;
462 	wxCHECK2(fileHash.Decode(file_hash), /* Do nothing. */ );
463 
464 	CECTag hashtag(EC_TAG_PARTFILE, fileHash);
465 	if (cmd == wxT("pause")) {
466 		ec_cmd = new CECPacket(EC_OP_PARTFILE_PAUSE);
467 	} else if (cmd == wxT("resume")) {
468 		ec_cmd = new CECPacket(EC_OP_PARTFILE_RESUME);
469 	} else if (cmd == wxT("cancel")) {
470 		ec_cmd = new CECPacket(EC_OP_PARTFILE_DELETE);
471 	} else if (cmd == wxT("prio")) {
472 		ec_cmd = new CECPacket(EC_OP_PARTFILE_PRIO_SET);
473 		hashtag.AddTag(CECTag(EC_TAG_PARTFILE_PRIO, (uint8)opt_arg));
474 	} else if (cmd == wxT("prioup")) {
475 		DownloadFile *file = m_DownloadFileInfo.GetByHash(fileHash);
476 		if ( file ) {
477 			ec_cmd = new CECPacket(EC_OP_PARTFILE_PRIO_SET);
478 			hashtag.AddTag(CECTag(EC_TAG_PARTFILE_PRIO,
479 				GetHigherPrio(file->lFilePrio, file->bFileAutoPriority)));
480 		}
481 	} else if (cmd == wxT("priodown")) {
482 		DownloadFile *file = m_DownloadFileInfo.GetByHash(fileHash);
483 		if ( file ) {
484 			ec_cmd = new CECPacket(EC_OP_PARTFILE_PRIO_SET);
485 			hashtag.AddTag(CECTag(EC_TAG_PARTFILE_PRIO,
486 				GetLowerPrio(file->lFilePrio, file->bFileAutoPriority)));
487 		}
488 	}
489 
490 	if ( ec_cmd ) {
491 		ec_cmd->AddTag(hashtag);
492 		Send_Discard_V2_Request(ec_cmd);
493 		delete ec_cmd;
494 	}
495 }
496 
Send_DownloadSearchFile_Cmd(wxString file_hash,uint8 cat)497 void CWebServerBase::Send_DownloadSearchFile_Cmd(wxString file_hash, uint8 cat)
498 {
499 	CMD4Hash fileHash;
500 	wxCHECK2(fileHash.Decode(file_hash), /* Do nothing. */ );
501 
502 	CECPacket ec_cmd(EC_OP_DOWNLOAD_SEARCH_RESULT);
503 	CECTag link_tag(EC_TAG_KNOWNFILE, fileHash);
504 
505 	link_tag.AddTag(CECTag(EC_TAG_PARTFILE_CAT, cat));
506 	ec_cmd.AddTag(link_tag);
507 	Send_Discard_V2_Request(&ec_cmd);
508 }
509 
Send_AddServer_Cmd(wxString addr,wxString port,wxString name)510 void CWebServerBase::Send_AddServer_Cmd(wxString addr, wxString port, wxString name)
511 {
512 	CECPacket ec_cmd(EC_OP_SERVER_ADD);
513 
514 	ec_cmd.AddTag(CECTag(EC_TAG_SERVER_ADDRESS, addr.Trim() + wxT(":") + port.Trim()));
515 	ec_cmd.AddTag(CECTag(EC_TAG_SERVER_NAME, name));
516 
517 	Send_Discard_V2_Request(&ec_cmd);
518 }
519 
Send_Server_Cmd(uint32 ip,uint16 port,wxString cmd)520 void CWebServerBase::Send_Server_Cmd(uint32 ip, uint16 port, wxString cmd)
521 {
522 	if ( !ip ) {
523 		return;
524 	}
525 
526 	CECPacket *ec_cmd = 0;
527 	if ( cmd == wxT("connect") ) {
528 		ec_cmd = new CECPacket(EC_OP_SERVER_CONNECT);
529 	} else if ( cmd == wxT("remove") ) {
530 		ec_cmd = new CECPacket(EC_OP_SERVER_REMOVE);
531 	} else if ( cmd == wxT("disconnect") ) {
532 		ec_cmd = new CECPacket(EC_OP_SERVER_DISCONNECT);
533 	}
534 	if ( ec_cmd ) {
535 		ec_cmd->AddTag(CECTag(EC_TAG_SERVER, EC_IPv4_t(ip, port)));
536 		Send_Discard_V2_Request(ec_cmd);
537 		delete ec_cmd;
538 	}
539 }
540 
Send_Search_Cmd(wxString search,wxString extention,wxString type,EC_SEARCH_TYPE search_type,uint32 avail,uint32 min_size,uint32 max_size)541 void CWebServerBase::Send_Search_Cmd(wxString search, wxString extention, wxString type,
542 	EC_SEARCH_TYPE search_type, uint32 avail, uint32 min_size, uint32 max_size)
543 {
544 	CECPacket search_req(EC_OP_SEARCH_START);
545 	search_req.AddTag(CEC_Search_Tag (search, search_type,
546 		type, extention, avail, min_size, max_size));
547 	Send_Discard_V2_Request(&search_req);
548 }
549 
Send_DownloadEd2k_Cmd(wxString link,uint8 cat)550 bool CWebServerBase::Send_DownloadEd2k_Cmd(wxString link, uint8 cat)
551 {
552 	CECPacket req(EC_OP_ADD_LINK);
553 	CECTag link_tag(EC_TAG_STRING, link);
554 	link_tag.AddTag(CECTag(EC_TAG_PARTFILE_CAT, cat));
555 	req.AddTag(link_tag);
556 	const CECPacket *response = webInterface->SendRecvMsg_v2(&req);
557 	bool result = (response->GetOpCode() == EC_OP_FAILED);
558 	delete response;
559 	return result;
560 }
561 
562 // We have to add gz-header and some other stuff
563 // to standard zlib functions in order to use gzip in web pages
GzipCompress(Bytef * dest,uLongf * destLen,const Bytef * source,uLong sourceLen,int level)564 int CWebServerBase::GzipCompress(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen, int level)
565 {
566 	static const int gz_magic[2] = {0x1f, 0x8b}; // gzip magic header
567 	z_stream stream = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
568 	stream.zalloc = (alloc_func)0;
569 	stream.zfree = (free_func)0;
570 	stream.opaque = (voidpf)0;
571 	uLong crc = crc32(0L, Z_NULL, 0);
572 	// init Zlib stream
573 	// NOTE windowBits is passed < 0 to suppress zlib header
574 	int err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
575 	if (err != Z_OK) {
576 		return err;
577 	}
578 
579 	snprintf((char*)dest, *destLen, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1],
580 		Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, 255);
581 
582 	// wire buffers
583 	stream.next_in = const_cast<Bytef*>(source);
584 	stream.avail_in = (uInt)sourceLen;
585 	stream.next_out = ((Bytef*) dest) + 10;
586 	stream.avail_out = *destLen - 18;
587 	// doit
588 	err = deflate(&stream, Z_FINISH);
589 	if (err != Z_STREAM_END) {
590 		deflateEnd(&stream);
591 		return err;
592 	}
593 	err = deflateEnd(&stream);
594 	crc = crc32(crc, (const Bytef *) source ,  sourceLen );
595 	//CRC
596 	*(((Bytef*) dest)+10+stream.total_out) = (Bytef)(crc & 0xFF);
597 	*(((Bytef*) dest)+10+stream.total_out+1) = (Bytef)((crc>>8) & 0xFF);
598 	*(((Bytef*) dest)+10+stream.total_out+2) = (Bytef)((crc>>16) & 0xFF);
599 	*(((Bytef*) dest)+10+stream.total_out+3) = (Bytef)((crc>>24) & 0xFF);
600 	// Length
601 	*(((Bytef*) dest)+10+stream.total_out+4) = (Bytef)( sourceLen  & 0xFF);
602 	*(((Bytef*) dest)+10+stream.total_out+5) = (Bytef)(( sourceLen >>8) & 0xFF);
603 	*(((Bytef*) dest)+10+stream.total_out+6) = (Bytef)(( sourceLen >>16) &	0xFF);
604 	*(((Bytef*) dest)+10+stream.total_out+7) = (Bytef)(( sourceLen >>24) &	0xFF);
605 	// return  destLength
606 	*destLen = 10 + stream.total_out + 8;
607 
608 	return err;
609 }
610 
611 
612 
613 /*
614  * Item container implementation
615  */
616 
GetContainerInstance()617 ServersInfo *ServerEntry::GetContainerInstance()
618 {
619 	return ServersInfo::m_This;
620 }
621 
622 ServersInfo *ServersInfo::m_This = 0;
623 
ServersInfo(CamulewebApp * webApp)624 ServersInfo::ServersInfo(CamulewebApp *webApp) : ItemsContainer<ServerEntry>(webApp)
625 {
626 	m_This = this;
627 
628 }
629 
ReQuery()630 bool ServersInfo::ReQuery()
631 {
632 	CECPacket srv_req(EC_OP_GET_SERVER_LIST);
633 	const CECPacket *srv_reply = m_webApp->SendRecvMsg_v2(&srv_req);
634 	if (!srv_reply) {
635 		return false;
636 	}
637 	//
638 	// query succeded - flush existing values and refill
639 	EraseAll();
640 	for (CECPacket::const_iterator it = srv_reply->begin(); it != srv_reply->end(); ++it) {
641 		const CECTag *tag = & *it;
642 
643 		ServerEntry Entry;
644 		Entry.sServerName =
645 			_SpecialChars(tag->GetTagByNameSafe(EC_TAG_SERVER_NAME)->GetStringData());
646 		Entry.sServerDescription =
647 			_SpecialChars(tag->GetTagByNameSafe(EC_TAG_SERVER_DESC)->GetStringData());
648 		Entry.sServerIP = tag->GetIPv4Data().StringIP(false);
649 		Entry.nServerIP = tag->GetIPv4Data().IP();
650 		Entry.nServerPort = tag->GetIPv4Data().m_port;
651 		Entry.nServerUsers =
652 			tag->GetTagByNameSafe(EC_TAG_SERVER_USERS)->GetInt();
653 		Entry.nServerMaxUsers =
654 			tag->GetTagByNameSafe(EC_TAG_SERVER_USERS_MAX)->GetInt();
655 		Entry.nServerFiles =
656 			tag->GetTagByNameSafe(EC_TAG_SERVER_FILES)->GetInt();
657 		AddItem(Entry);
658 	}
659 	delete srv_reply;
660 
661 	return true;
662 }
663 
664 
SharedFile(CEC_SharedFile_Tag * tag)665 SharedFile::SharedFile(CEC_SharedFile_Tag *tag) : CECID(tag->ID())
666 {
667 		sFileName = _SpecialChars(tag->FileName());
668 		lFileSize = tag->SizeFull();
669 		sED2kLink = _SpecialChars(tag->FileEd2kLink());
670 		nHash = tag->FileHash();
671 
672 		ProcessUpdate(tag);
673 }
674 
ProcessUpdate(CEC_SharedFile_Tag * tag)675 void SharedFile::ProcessUpdate(CEC_SharedFile_Tag *tag)
676 {
677 	nFileTransferred = tag->GetXferred();
678 	nFileAllTimeTransferred = tag->GetAllXferred();
679 	nFileRequests = tag->GetRequests();
680 	nFileAllTimeRequests = tag->GetAllRequests();
681 	nFileAccepts = tag->GetAccepts();
682 	nFileAllTimeAccepts = tag->GetAllAccepts();
683 	sFileHash = nHash.Encode();
684 	nFilePriority = tag->UpPrio();
685 	if ( nFilePriority >= 10 ) {
686 		bFileAutoPriority = true;
687 		nFilePriority -= 10;
688 	} else {
689 		bFileAutoPriority = false;
690 	}
691 }
692 
GetContainerInstance()693 SharedFileInfo *SharedFile::GetContainerInstance()
694 {
695 	return SharedFileInfo::m_This;
696 }
697 
698 SharedFileInfo *SharedFileInfo::m_This = 0;
699 
SharedFileInfo(CamulewebApp * webApp)700 SharedFileInfo::SharedFileInfo(CamulewebApp *webApp) :
701 	UpdatableItemsContainer<SharedFile, CEC_SharedFile_Tag, uint32>(webApp)
702 {
703 	m_This = this;
704 }
705 
706 
ReQuery()707 bool SharedFileInfo::ReQuery()
708 {
709 	DoRequery(EC_OP_GET_SHARED_FILES, EC_TAG_KNOWNFILE);
710 
711 	return true;
712 }
713 
714 
DownloadFile(CEC_PartFile_Tag * tag)715 DownloadFile::DownloadFile(CEC_PartFile_Tag *tag) : CECID(tag->ID())
716 {
717 	nHash = tag->FileHash();
718 	sFileName = _SpecialChars(tag->FileName());
719 	lFileSize = tag->SizeFull();
720 	sFileHash = nHash.Encode();
721 	sED2kLink = _SpecialChars(tag->FileEd2kLink());
722 	lFileCompleted = tag->SizeDone();
723 	lFileTransferred = tag->SizeXfer();
724 	lFileSpeed = tag->Speed();
725 	fCompleted = (100.0*lFileCompleted) / lFileSize;
726 	wxtLastSeenComplete = wxDateTime( tag->LastSeenComplete() );
727 
728 	ProcessUpdate(tag);
729 }
730 
ProcessUpdate(CEC_PartFile_Tag * tag)731 void DownloadFile::ProcessUpdate(CEC_PartFile_Tag *tag)
732 {
733 	if (!tag) {
734 		return;
735 	}
736 
737 	lFilePrio = tag->DownPrio();
738 	if ( lFilePrio >= 10 ) {
739 		lFilePrio -= 10;
740 		bFileAutoPriority = true;
741 	} else {
742 		bFileAutoPriority = false;
743 	}
744 	nCat = tag->FileCat();
745 
746 	nFileStatus = tag->FileStatus();
747 	lSourceCount = tag->SourceCount();
748 	lNotCurrentSourceCount = tag->SourceNotCurrCount();
749 	lTransferringSourceCount = tag->SourceXferCount();
750 	lSourceCountA4AF = tag->SourceCountA4AF();
751 	if ( lTransferringSourceCount > 0 ) {
752 		lFileCompleted = tag->SizeDone();
753 		lFileTransferred = tag->SizeXfer();
754 		lFileSpeed = tag->Speed();
755 		fCompleted = (100.0*lFileCompleted) / lFileSize;
756 	} else {
757 		lFileSpeed = 0;
758 	}
759 	CECTag *gaptag = tag->GetTagByName(EC_TAG_PARTFILE_GAP_STATUS);
760 	CECTag *parttag = tag->GetTagByName(EC_TAG_PARTFILE_PART_STATUS);
761 	CECTag *reqtag = tag->GetTagByName(EC_TAG_PARTFILE_REQ_STATUS);
762 
763 	if (gaptag) {
764 		m_Encoder.DecodeGaps(gaptag, m_Gaps);
765 	}
766 	if (parttag) {
767 		m_Encoder.DecodeParts(parttag, m_PartInfo);
768 	}
769 	if (reqtag) {
770 		ArrayOfUInts64 reqs;
771 		m_Encoder.DecodeReqs(reqtag, reqs);
772 		int reqcount = reqs.size() / 2;
773 		m_ReqParts.resize(reqcount);
774 		for (int i = 0; i < reqcount;i++) {
775 			m_ReqParts[i].start = reqs[2*i];
776 			m_ReqParts[i].end   = reqs[2*i+1];
777 		}
778 	}
779 }
780 
GetContainerInstance()781 DownloadFileInfo *DownloadFile::GetContainerInstance()
782 {
783 	return DownloadFileInfo::m_This;
784 }
785 
786 DownloadFileInfo *DownloadFileInfo::m_This = 0;
787 
DownloadFileInfo(CamulewebApp * webApp,CImageLib * imlib)788 DownloadFileInfo::DownloadFileInfo(CamulewebApp *webApp, CImageLib *imlib) :
789 	UpdatableItemsContainer<DownloadFile, CEC_PartFile_Tag, uint32>(webApp)
790 {
791 	m_This = this;
792 	m_ImageLib = imlib;
793 }
794 
LoadImageParams(wxString & tpl,int width,int height)795 void DownloadFileInfo::LoadImageParams(wxString &tpl, int width, int height)
796 {
797 	m_Template = tpl;
798 	m_width = width;
799 	m_height = height;
800 }
801 
ItemInserted(DownloadFile * item)802 void DownloadFileInfo::ItemInserted(DownloadFile *item)
803 {
804 	item->m_Image = new CDynProgressImage(m_width, m_height, m_Template, item);
805 
806 #ifdef WITH_LIBPNG
807 	m_ImageLib->AddImage(item->m_Image, wxT("/") + item->m_Image->Name());
808 #endif
809 }
810 
ItemDeleted(DownloadFile * item)811 void DownloadFileInfo::ItemDeleted(DownloadFile *item)
812 {
813 #ifdef WITH_LIBPNG
814 	m_ImageLib->RemoveImage(wxT("/") + item->m_Image->Name());
815 #else
816 	delete item->m_Image;
817 #endif
818 }
819 
ReQuery()820 bool DownloadFileInfo::ReQuery()
821 {
822 	DoRequery(EC_OP_GET_DLOAD_QUEUE, EC_TAG_PARTFILE);
823 
824 	return true;
825 }
826 
827 
UploadFile(CEC_UpDownClient_Tag * tag)828 UploadFile::UploadFile(CEC_UpDownClient_Tag *tag) : CECID(tag->ID())
829 {
830 	sUserName = _SpecialChars(tag->ClientName());
831 	nSpeed = tag->SpeedUp();
832 	nTransferredUp = tag->XferUp();
833 	nTransferredDown = tag->XferDown();
834 	nUploadFile = 0;
835 	tag->UploadFile(nUploadFile);
836 }
837 
GetContainerInstance()838 UploadsInfo *UploadFile::GetContainerInstance()
839 {
840 	return UploadsInfo::m_This;
841 }
842 
843 UploadsInfo *UploadsInfo::m_This = 0;
844 
UploadsInfo(CamulewebApp * webApp)845 UploadsInfo::UploadsInfo(CamulewebApp *webApp) : ItemsContainer<UploadFile>(webApp)
846 {
847 	m_This = this;
848 }
849 
ReQuery()850 bool UploadsInfo::ReQuery()
851 {
852 	CECPacket up_req(EC_OP_GET_ULOAD_QUEUE);
853 	const CECPacket *up_reply = m_webApp->SendRecvMsg_v2(&up_req);
854 	if (!up_reply) {
855 		return false;
856 	}
857 	//
858 	// query succeded - flush existing values and refill
859 	EraseAll();
860 	for (CECPacket::const_iterator it = up_reply->begin(); it != up_reply->end(); ++it) {
861 
862 		UploadFile curr((CEC_UpDownClient_Tag *) & *it);
863 
864 		AddItem(curr);
865 	}
866 	delete up_reply;
867 
868 	return true;
869 }
870 
SearchFile(CEC_SearchFile_Tag * tag)871 SearchFile::SearchFile(CEC_SearchFile_Tag *tag)
872 {
873 	nHash = tag->FileHash();
874 	sHash = nHash.Encode();
875 	sFileName = _SpecialChars(tag->FileName());
876 	lFileSize = tag->SizeFull();
877 	lSourceCount = tag->SourceCount();
878 	bPresent = tag->AlreadyHave();
879 }
880 
ProcessUpdate(CEC_SearchFile_Tag * tag)881 void SearchFile::ProcessUpdate(CEC_SearchFile_Tag *tag)
882 {
883 	lSourceCount = tag->SourceCount();
884 }
885 
GetContainerInstance()886 SearchInfo *SearchFile::GetContainerInstance()
887 {
888 	return SearchInfo::m_This;
889 }
890 
891 SearchInfo *SearchInfo::m_This = 0;
892 
SearchInfo(CamulewebApp * webApp)893 SearchInfo::SearchInfo(CamulewebApp *webApp) :
894 	UpdatableItemsContainer<SearchFile, CEC_SearchFile_Tag, uint32>(webApp)
895 {
896 	m_This = this;
897 }
898 
ReQuery()899 bool SearchInfo::ReQuery()
900 {
901 	DoRequery(EC_OP_SEARCH_RESULTS, EC_TAG_SEARCHFILE);
902 
903 	return true;
904 }
905 
906 
907 /*!
908  * Image classes:
909  *
910  * CFileImage: simply represent local file
911  * CDynProgressImage: dynamically generated from gap info
912  */
913 
CAnyImage(int size)914 CAnyImage::CAnyImage(int size)
915 {
916 	m_size = 0;
917 	m_alloc_size = size;
918 	if ( m_alloc_size ) {
919 		m_data = new unsigned char[m_alloc_size];
920 	} else {
921 		m_data = 0;
922 	}
923 }
924 
CAnyImage(int width,int height)925 CAnyImage::CAnyImage(int width, int height) : m_width(width), m_height(height)
926 {
927 	m_size = 0;
928 	// allocate considering image header
929 	m_alloc_size = width * height * sizeof(uint32) + 0x100;
930 	if ( m_alloc_size ) {
931 		m_data = new unsigned char[m_alloc_size];
932 	} else {
933 		m_data = 0;
934 	}
935 }
936 
~CAnyImage()937 CAnyImage::~CAnyImage()
938 {
939 	if ( m_data ) {
940 		delete [] m_data;
941 	}
942 }
943 
Realloc(int size)944 void CAnyImage::Realloc(int size)
945 {
946 	if ( size == m_alloc_size ) {
947 		return;
948 	}
949 	// always grow, but shrink only x2
950 	if ( (size > m_alloc_size) || (size < (m_alloc_size / 2)) ) {
951 		m_alloc_size = size;
952 		if ( m_data ) {
953 			delete [] m_data;
954 		}
955 		m_data = new unsigned char[m_alloc_size];
956 	}
957 }
958 
RequestData(int & size)959 unsigned char *CAnyImage::RequestData(int &size)
960 {
961 	size = m_size;
962 	return m_data;
963 }
964 
SetHttpType(wxString ext)965 void CAnyImage::SetHttpType(wxString ext)
966 {
967 	m_Http = wxT("Content-Type: ") + ext + wxT("\r\n");
968 
969 	time_t t = time(NULL);
970 	char tmp[255];
971 	strftime(tmp, 255, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&t));
972 	m_Http += wxT("Last-Modified: ") + wxString(char2unicode(tmp)) + wxT("\r\n");
973 
974 	m_Http += wxT("ETag: ") + MD5Sum(char2unicode(tmp)).GetHash() + wxT("\r\n");
975 }
976 
CFileImage(const wxString & name)977 CFileImage::CFileImage(const wxString& name) : CAnyImage(0)
978 {
979 	m_size = 0;
980 	m_name = name;
981 #ifdef __WINDOWS__
982 	wxFFile fis(m_name, wxT("rb"));
983 #else
984 	wxFFile fis(m_name);
985 #endif
986 	// FIXME: proper logging is needed
987 	if ( fis.IsOpened() ) {
988 		size_t file_size = fis.Length();
989 		if ( file_size ) {
990 			Realloc(fis.Length());
991 			m_size = fis.Read(m_data,file_size);
992 		} else {
993 			printf("CFileImage: file %s have zero length\n", (const char *)unicode2char(m_name));
994 		}
995 		wxString ext = m_name.Right(3).MakeLower();
996 		if ( ext == wxT("css") ) {
997 			SetHttpType(wxT("text/css"));
998 		} else {
999 			SetHttpType(wxT("image/") + ext);
1000 		}
1001 	} else {
1002 		printf("CFileImage: failed to open %s\n", (const char *)unicode2char(m_name));
1003 	}
1004 }
1005 
1006 /*!
1007  * "Modifiers" for 3D look of progress bar. Those modifiers must be substracted from
1008  * image (with saturation), values, and not multiplied, as amule doesn for some reason.
1009  *
1010  */
CImage3D_Modifiers(int width)1011 CImage3D_Modifiers::CImage3D_Modifiers(int width)
1012 {
1013 	m_width = width;
1014 	m_modifiers = new unsigned char[m_width];
1015 	for(int i = 0; i < m_width; i++) {
1016 		// "70" - webserver uses fixed depth
1017 		double f_curr_mod = 30 * (1 + cos( (2 * M_PI) * ( m_width - (((double)i)/m_width) ) ) );
1018 		m_modifiers[i] = (unsigned char)f_curr_mod;
1019 	}
1020 }
1021 
~CImage3D_Modifiers()1022 CImage3D_Modifiers::~CImage3D_Modifiers()
1023 {
1024 	delete [] m_modifiers;
1025 }
1026 
CProgressImage(int width,int height,wxString & tmpl,DownloadFile * file)1027 CProgressImage::CProgressImage(int width, int height, wxString &tmpl, DownloadFile *file) :
1028 		CAnyImage(width, height), m_template(tmpl)
1029 {
1030 	m_file = file;
1031 	m_ColorLine = new uint32[m_width];
1032 }
1033 
~CProgressImage()1034 CProgressImage::~CProgressImage()
1035 {
1036 	delete [] m_ColorLine;
1037 }
1038 
CreateSpan()1039 void CProgressImage::CreateSpan()
1040 {
1041 	// Step 1: get gap list
1042 	const Gap_Struct * gap_list = (const Gap_Struct *) &(m_file->m_Gaps[0]);
1043 	int gap_list_size = m_file->m_Gaps.size() / 2;
1044 
1045 	// allocate for worst case !
1046 	int color_gaps_alloc = 2 * (2 * gap_list_size + m_file->lFileSize / PARTSIZE + 1);
1047 	Color_Gap_Struct *colored_gaps = new Color_Gap_Struct[color_gaps_alloc];
1048 
1049 	// Step 2: combine gap and part status information
1050 
1051 	// Init first item to dummy info, so we will always have "previous" item
1052 	int colored_gaps_size = 0;
1053 	colored_gaps[0].start = 0;
1054 	colored_gaps[0].end = 0;
1055 	colored_gaps[0].color = 0xffffffff;
1056 	for (int j = 0; j < gap_list_size;j++) {
1057 		uint64 gap_start = gap_list[j].start;
1058 		uint64 gap_end = gap_list[j].end;
1059 
1060 		uint32 start = gap_start / PARTSIZE;
1061 		uint32 end = (gap_end / PARTSIZE) + 1;
1062 
1063 		for (uint32 i = start; i < end; i++) {
1064 			COLORTYPE color = RGB(255, 0, 0);
1065 			if (m_file->m_PartInfo.size() > i && m_file->m_PartInfo[i]) {
1066 				int blue = 210 - ( 22 * ( m_file->m_PartInfo[i] - 1 ) );
1067 				color = RGB( 0, ( blue < 0 ? 0 : blue ), 255 );
1068 			}
1069 
1070 			uint64 fill_gap_begin = ( (i == start)   ? gap_start: PARTSIZE * i );
1071 			uint64 fill_gap_end   = ( (i == (end - 1)) ? gap_end   : PARTSIZE * ( i + 1 ) );
1072 
1073 			wxASSERT(colored_gaps_size < color_gaps_alloc);
1074 
1075 			if ( (colored_gaps[colored_gaps_size].end == fill_gap_begin) &&
1076 				(colored_gaps[colored_gaps_size].color == color) ) {
1077 				colored_gaps[colored_gaps_size].end = fill_gap_end;
1078 			} else {
1079 				colored_gaps_size++;
1080 				colored_gaps[colored_gaps_size].start = fill_gap_begin;
1081 				colored_gaps[colored_gaps_size].end = fill_gap_end;
1082 				colored_gaps[colored_gaps_size].color = color;
1083 			}
1084 		}
1085 
1086 	}
1087 	//
1088 	// Now line rendering
1089 	for(int i = 0; i < m_width; ++i) {
1090 		m_ColorLine[i] = 0x0;
1091 	}
1092 	if (m_file->lFileSize < (uint32)m_width) {
1093 		//
1094 		// if file is that small, draw it in single step
1095 		//
1096 		if (!m_file->m_ReqParts.empty()) {
1097 			for(int i = 0; i < m_width; ++i) {
1098 				m_ColorLine[i] = RGB(255, 208, 0);
1099 			}
1100 		} else if ( colored_gaps_size ) {
1101 			for(int i = 0; i < m_width; ++i) {
1102 				m_ColorLine[i] = colored_gaps[1].color;
1103 			}
1104 		}
1105 	} else {
1106 		uint32 factor = m_file->lFileSize / m_width;
1107 		for (int i = 1; i <= colored_gaps_size;i++) {
1108 			uint32 start = colored_gaps[i].start / factor;
1109 			uint32 end = colored_gaps[i].end / factor;
1110 			if ((int)end > m_width) {
1111 				end = m_width;
1112 			}
1113 			for (uint32 j = start; j < end; j++) {
1114 				m_ColorLine[j] = colored_gaps[i].color;
1115 			}
1116 		}
1117 		// overwrite requested parts
1118 		for (uint32 i = 0; i < m_file->m_ReqParts.size(); i++) {
1119 			uint32 start = m_file->m_ReqParts[i].start / factor;
1120 			uint32 end = m_file->m_ReqParts[i].end / factor;
1121 			if ((int)end > m_width) {
1122 				end = m_width;
1123 			}
1124 			for (uint32 j = start; j < end; j++) {
1125 				m_ColorLine[j] = RGB(255, 208, 0);
1126 			}
1127 		}
1128 	}
1129 	delete [] colored_gaps;
1130 }
1131 
1132 #ifdef WITH_LIBPNG
1133 
CDynPngImage(int w,int h)1134 CDynPngImage::CDynPngImage(int w, int h) : CAnyImage(w, h)
1135 {
1136 
1137 	//
1138 	// Allocate array of "row pointers" - libpng need it in this form
1139 	// Fill it also with the image data
1140 	//
1141 	m_img_data = new png_byte[3*m_width*m_height];
1142 	memset(m_img_data, 0, 3*m_width*m_height);
1143 	m_row_ptrs = new png_bytep [m_height];
1144 	for (int i = 0; i < m_height;i++) {
1145 		m_row_ptrs[i] = &m_img_data[3*m_width*i];
1146 	}
1147 
1148 }
1149 
~CDynPngImage()1150 CDynPngImage::~CDynPngImage()
1151 {
1152 	delete [] m_row_ptrs;
1153 	delete [] m_img_data;
1154 }
1155 
png_write_fn(png_structp png_ptr,png_bytep data,png_size_t length)1156 void CDynPngImage::png_write_fn(png_structp png_ptr, png_bytep data, png_size_t length)
1157 {
1158 	CDynPngImage *This = static_cast<CDynPngImage *>(png_get_io_ptr(png_ptr));
1159 	wxASSERT((png_size_t)(This->m_size + length) <= (png_size_t)This->m_alloc_size);
1160 	memcpy(This->m_data + This->m_size, data, length);
1161 	This->m_size += length;
1162 }
1163 
1164 
RequestData(int & size)1165 unsigned char *CDynPngImage::RequestData(int &size)
1166 {
1167 	// write png into buffer
1168 	png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
1169 	png_infop info_ptr = png_create_info_struct(png_ptr);
1170 	png_set_IHDR(png_ptr, info_ptr, m_width, m_height, 8, PNG_COLOR_TYPE_RGB,
1171 		PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
1172 	png_set_write_fn(png_ptr, this, png_write_fn, 0);
1173 
1174 	m_size = 0;
1175 	png_write_info(png_ptr, info_ptr);
1176 	png_write_image(png_ptr, (png_bytep *)m_row_ptrs);
1177 	png_write_end(png_ptr, 0);
1178 	png_destroy_write_struct(&png_ptr, &info_ptr);
1179 
1180 	return CAnyImage::RequestData(size);
1181 }
1182 
CDynProgressImage(int width,int height,wxString & tmpl,DownloadFile * file)1183 CDynProgressImage::CDynProgressImage(int width, int height, wxString &tmpl, DownloadFile *file) :
1184 	CAnyImage(width, height),
1185 	CProgressImage(width, height, tmpl, file),
1186 	CDynPngImage(width, height),
1187 	m_modifiers(height)
1188 {
1189 	m_name = wxT("dyn_") + m_file->sFileHash + wxT(".png");
1190 }
1191 
~CDynProgressImage()1192 CDynProgressImage::~CDynProgressImage()
1193 {
1194 }
1195 
GetHTML()1196 wxString CDynProgressImage::GetHTML()
1197 {
1198 	// template contain %s (name) %d (width) %s (alt)
1199 	return (CFormat(m_template) % m_name % m_width % wxT("Progress bar")).GetString();
1200 }
1201 
DrawImage()1202 void CDynProgressImage::DrawImage()
1203 {
1204 	CreateSpan();
1205 
1206 	for(int i = 0; i < m_height; i++) {
1207 			memset(m_row_ptrs[i], 0, 3*m_width);
1208 	}
1209 	for(int i = 0; i < m_height/2; i++) {
1210 		png_bytep u_row = m_row_ptrs[i];
1211 		png_bytep d_row = m_row_ptrs[m_height-i-1];
1212 		for(int j = 0; j < m_width; j++) {
1213 			set_rgb_color_val(u_row+3*j, m_ColorLine[j], m_modifiers[i]);
1214 			set_rgb_color_val(d_row+3*j, m_ColorLine[j], m_modifiers[i]);
1215 		}
1216 	}
1217 }
1218 
RequestData(int & size)1219 unsigned char *CDynProgressImage::RequestData(int &size)
1220 {
1221 	// create new one
1222 	DrawImage();
1223 
1224 	return CDynPngImage::RequestData(size);
1225 }
1226 
1227 #else
1228 
CDynProgressImage(int width,int height,wxString & tmpl,DownloadFile * file)1229 CDynProgressImage::CDynProgressImage(int width, int height, wxString &tmpl, DownloadFile *file) :
1230 	CAnyImage(width, height),
1231 	CProgressImage(width, height, tmpl, file)
1232 {
1233 	m_name = wxT("dyn_") + m_file->sFileHash + wxT(".png");
1234 
1235 }
1236 
1237 
GetHTML()1238 wxString CDynProgressImage::GetHTML()
1239 {
1240 	static wxChar *progresscolor[12] = {
1241 		wxT("transparent.gif"), wxT("black.gif"), wxT("yellow.gif"), wxT("red.gif"),
1242 		wxT("blue1.gif"),       wxT("blue2.gif"), wxT("blue3.gif"),  wxT("blue4.gif"),
1243 		wxT("blue5.gif"),       wxT("blue6.gif"), wxT("green.gif"),  wxT("greenpercent.gif") };
1244 
1245 	CreateSpan();
1246 
1247 	wxString str;
1248 	uint32 lastcolor = m_ColorLine[0];
1249 	int lastindex = 0;
1250 	for(int i = 0; i < m_width; i++) {
1251 		if ( (lastcolor != m_ColorLine[i]) || (i == (m_width - 1)) ) {
1252 			int color_idx = -1;
1253 			if ( lastcolor & RGB(0, 0, 0xff) ) { // blue
1254 				int green = (lastcolor >> 8) & 0xff;
1255 				// reverse calculation:  green = 210 - ( 22 * ( part_info[i] - 1 ) )
1256 				wxASSERT( !green || (green < 211) );
1257 				color_idx = (green) ? (210 - green) / 22 + 1 : 11;
1258 				// now calculate it same way as PartFile did
1259 				color_idx = (color_idx > 10) ? 9 : (4 + color_idx / 2);
1260 			} else {
1261 				if ( lastcolor & RGB(0, 0xff, 0) ) { // yellow
1262 					color_idx = 2;
1263 				} else {
1264 					if ( lastcolor & RGB(0xff, 0, 0) ) { // red
1265 						color_idx = 3;
1266 					} else {
1267 						color_idx = 1;
1268 					}
1269 				}
1270 			}
1271 			str += (CFormat(m_template) % progresscolor[color_idx]
1272 						% (i - lastindex) % wxString(progresscolor[color_idx]).BeforeLast(wxT('.'))).GetString();
1273 			lastindex = i;
1274 			lastcolor = m_ColorLine[i];
1275 		}
1276 	}
1277 
1278 	return str;
1279 }
1280 
1281 #endif
1282 
CStatsData(int size)1283 CStatsData::CStatsData(int size)
1284 {
1285 	m_size = size;
1286 	m_data = new uint32[size];
1287 	m_max_value = 0;
1288 	//
1289 	// initial situation: all data is 0's
1290 	//
1291 	memset(m_data, 0, m_size*sizeof(int));
1292 	m_start_index = m_curr_index = 0;
1293 	m_end_index = size - 1;
1294 }
1295 
~CStatsData()1296 CStatsData::~CStatsData()
1297 {
1298 	delete [] m_data;
1299 }
1300 
GetFirst()1301 uint32 CStatsData::GetFirst()
1302 {
1303 	m_curr_index = m_start_index;
1304 	return m_data[m_curr_index];
1305 }
1306 
GetNext()1307 uint32 CStatsData::GetNext()
1308 {
1309 	m_curr_index++;
1310 	m_curr_index %= m_size;
1311 	return m_data[m_curr_index];
1312 }
1313 
PushSample(uint32 sample)1314 void CStatsData::PushSample(uint32 sample)
1315 {
1316 	m_start_index = (m_start_index + 1) % m_size;
1317 	m_end_index = (m_end_index + 1) % m_size;
1318 	m_data[m_start_index] = sample;
1319 
1320 	if ( m_max_value < sample ) {
1321 		m_max_value = sample;
1322 	}
1323 }
1324 
CStatsCollection(int size,CamulewebApp * iface)1325 CStatsCollection::CStatsCollection(int size, CamulewebApp *iface)
1326 {
1327 	m_down_speed = new CStatsData(size);
1328 	m_up_speed = new CStatsData(size);
1329 	m_conn_number = new CStatsData(size);
1330 	m_kad_count = new CStatsData(size);
1331 
1332 	m_iface = iface;
1333 	m_LastTimeStamp = 0.0;
1334 	m_size = size;
1335 }
1336 
~CStatsCollection()1337 CStatsCollection::~CStatsCollection()
1338 {
1339 	delete m_down_speed;
1340 	delete m_up_speed;
1341 	delete m_conn_number;
1342 	delete m_kad_count;
1343 }
1344 
ReQuery()1345 void CStatsCollection::ReQuery()
1346 {
1347 	CECPacket request(EC_OP_GET_STATSGRAPHS);
1348 
1349 	request.AddTag(CECTag(EC_TAG_STATSGRAPH_WIDTH, (uint16)m_size));
1350 
1351 	uint16 m_nGraphScale = 1;
1352 	request.AddTag(CECTag(EC_TAG_STATSGRAPH_SCALE, m_nGraphScale));
1353 	if (m_LastTimeStamp > 0.0) {
1354 		request.AddTag(CECTag(EC_TAG_STATSGRAPH_LAST, m_LastTimeStamp));
1355 	}
1356 
1357 	const CECPacket *response = m_iface->SendRecvMsg_v2(&request);
1358 
1359 	m_LastTimeStamp = response->GetTagByNameSafe(EC_TAG_STATSGRAPH_LAST)->GetDoubleData();
1360 
1361 	const CECTag *dataTag = response->GetTagByName(EC_TAG_STATSGRAPH_DATA);
1362 	const uint32 *data = (const uint32 *)dataTag->GetTagData();
1363 	unsigned int count = dataTag->GetTagDataLen() / sizeof(uint32);
1364 	for (unsigned int i = 0; i < count; i += 4) {
1365 		m_down_speed->PushSample(ENDIAN_NTOHL(data[i+0]));
1366 		m_up_speed->PushSample(ENDIAN_NTOHL(data[i+1]));
1367 		m_conn_number->PushSample(ENDIAN_NTOHL(data[i+2]));
1368 		m_kad_count->PushSample(ENDIAN_NTOHL(data[i+3]));
1369 	}
1370 }
1371 
1372 //
1373 // Dynamically generated statistic images
1374 //
1375 #ifdef WITH_LIBPNG
1376 
CDynStatisticImage(int height,bool scale1024,CStatsData * data)1377 CDynStatisticImage::CDynStatisticImage(int height, bool scale1024, CStatsData *data) :
1378 	CAnyImage(data->Size(), height), CDynPngImage(data->Size(), height)
1379 {
1380 	m_data = data;
1381 	m_scale1024 = scale1024;
1382 
1383 	// actual name doesn't matter, just make it unique
1384 	m_name = CFormat(wxT("dyn_%p_stat.png")) % data;
1385 
1386 	m_num_font_w_size = 8;
1387 	m_num_font_h_size = 16;
1388 
1389 	// leave enough space for 4 digit number
1390 	int img_delta = m_num_font_w_size / 4;
1391 	m_left_margin = 4*(m_num_font_w_size + img_delta) + img_delta;
1392 	// leave enough space for number height
1393 	m_bottom_margin = m_num_font_h_size;
1394 
1395 
1396 	m_y_axis_size = m_height - m_bottom_margin;
1397 	// allocate storage for background. Using 1 chunk to speed up
1398 	// the rendering
1399 	m_background = new png_byte[m_width*m_height*3];
1400 	m_row_bg_ptrs = new png_bytep[m_height];
1401 	for(int i = 0; i < m_height; i++) {
1402 		m_row_bg_ptrs[i] = &m_background[i*m_width*3];
1403 	}
1404 
1405 	//
1406 	// Prepare background
1407 	//
1408 	static const COLORTYPE bg_color = RGB(0x00, 0x00, 0x40);
1409 	for(int i = 0; i < m_height; i++) {
1410 		png_bytep u_row = m_row_bg_ptrs[i];
1411 		for(int j = 0; j < m_width; j++) {
1412 			set_rgb_color_val(u_row+3*j, bg_color, 0);
1413 		}
1414 	}
1415 	//
1416 	// draw axis
1417 	//
1418 	static const COLORTYPE axis_color = RGB(0xff, 0xff, 0xff);
1419 	// Y
1420 	for(int i = m_bottom_margin; i < m_y_axis_size; i++) {
1421 		png_bytep u_row = m_row_bg_ptrs[i];
1422 		set_rgb_color_val(u_row+3*(m_left_margin + 0), axis_color, 0);
1423 		set_rgb_color_val(u_row+3*(m_left_margin + 1), axis_color, 0);
1424 	}
1425 	// X
1426 	for(int j = m_left_margin; j < m_width; j++) {
1427 		set_rgb_color_val(m_row_bg_ptrs[m_y_axis_size - 0]+3*j, axis_color, 0);
1428 		set_rgb_color_val(m_row_bg_ptrs[m_y_axis_size - 1]+3*j, axis_color, 0);
1429 	}
1430 
1431 	// horisontal grid
1432 	int v_grid_size = m_y_axis_size / 4;
1433 	for(int i = m_y_axis_size - v_grid_size; i >= v_grid_size; i -= v_grid_size) {
1434 		png_bytep u_row = m_row_bg_ptrs[i];
1435 		for(int j = m_left_margin; j < m_width; j++) {
1436 			if ( (j % 10) < 5 ) {
1437 				set_rgb_color_val(u_row+3*j, axis_color, 0);
1438 			}
1439 		}
1440 	}
1441 
1442 	//
1443 	// Pre-create masks for digits
1444 	//
1445 	for(int i = 0; i < 10; i++) {
1446 		m_digits[i] = new CNumImageMask(i, m_num_font_w_size, m_num_font_h_size);
1447 	}
1448 }
1449 
~CDynStatisticImage()1450 CDynStatisticImage::~CDynStatisticImage()
1451 {
1452 	delete [] m_row_bg_ptrs;
1453 	delete [] m_background;
1454 	for(int i = 0; i < 10; i++) {
1455 		delete m_digits[i];
1456 	}
1457 }
1458 
DrawImage()1459 void CDynStatisticImage::DrawImage()
1460 {
1461 	// copy background first
1462 	memcpy(m_img_data, m_background, m_width*m_height*3);
1463 
1464 	//
1465 	// Now graph itself
1466 	//
1467 	static const COLORTYPE graph_color = RGB(0xff, 0x00, 0x00);
1468 	int maxval = m_data->Max();
1469 
1470 	if ( m_scale1024 ) {
1471 		if ( maxval > 1024 ) {
1472 			maxval /= 1024;
1473 		} else {
1474 			maxval = 1;
1475 		}
1476 	}
1477 	//
1478 	// Check if we need to scale data up or down
1479 	//
1480 	int m_scale_up = 1, m_scale_down = 1;
1481 	if ( maxval >= (m_height - m_bottom_margin) ) {
1482 		m_scale_down = 1 + (maxval / (m_y_axis_size - 10));
1483 	}
1484 	// if maximum value is 1/5 or less of graph height - scale it UP, to make 2/3
1485 	if ( maxval && (maxval < (m_y_axis_size / 5)) ) {
1486 		m_scale_up = (2*m_y_axis_size / 3) / maxval;
1487 	}
1488 
1489 	//
1490 	// draw axis scale
1491 	//
1492 	int img_delta = m_num_font_w_size / 4;
1493 	// Number "0" is always there
1494 	m_digits[0]->Apply(m_row_ptrs, 3*img_delta+2*m_num_font_w_size, m_y_axis_size-m_num_font_h_size-5);
1495 
1496 	//
1497 	// When data is scaled down, axis are scaled UP and viceversa
1498 	int y_axis_max = m_y_axis_size;
1499 	if ( m_scale_down != 1 ) {
1500 		y_axis_max *= m_scale_down;
1501 	} else if ( m_scale_up != 1 ) {
1502 		y_axis_max /= m_scale_up;
1503 	}
1504 
1505 	// X---
1506 	if ( y_axis_max > 999 ) {
1507 		m_digits[y_axis_max / 1000]->Apply(m_row_ptrs, img_delta, img_delta);
1508 	}
1509 	// -X--
1510 	if ( y_axis_max > 99 ) {
1511 		m_digits[(y_axis_max % 1000) / 100]->Apply(m_row_ptrs,
1512 			2*img_delta+m_num_font_w_size, img_delta);
1513 	}
1514 	// --X-
1515 	if ( y_axis_max > 9 ) {
1516 		m_digits[(y_axis_max % 100) / 10]->Apply(m_row_ptrs,
1517 			3*img_delta+2*m_num_font_w_size, img_delta);
1518 	}
1519 	// ---X
1520 	m_digits[y_axis_max % 10]->Apply(m_row_ptrs, 4*img_delta+3*m_num_font_w_size, img_delta);
1521 
1522 	int prev_data = m_data->GetFirst();
1523 	if ( m_scale_down != 1 ) {
1524 		prev_data /= m_scale_down;
1525 	} else if ( m_scale_up != 1 ) {
1526 		prev_data *= m_scale_up;
1527 	}
1528 	if ( m_scale1024 ) {
1529 		if ( prev_data > 1024) {
1530 			prev_data /= 1024;
1531 		} else {
1532 			prev_data = 1;
1533 		}
1534 	}
1535 	for(int j = m_left_margin + 1, curr_data = m_data->GetNext(); j < m_width; j++, curr_data = m_data->GetNext()) {
1536 		if ( m_scale_down != 1 ) {
1537 			curr_data /= m_scale_down;
1538 		} else if ( m_scale_up != 1 ) {
1539 			curr_data *= m_scale_up;
1540 		}
1541 		if ( m_scale1024 ) {
1542 			if ( curr_data > 1024) {
1543 				curr_data /= 1024;
1544 			} else {
1545 				curr_data = 1;
1546 			}
1547 		}
1548 		//
1549 		// draw between curr_data and prev_data
1550 		//
1551 		int min_y, max_y;
1552 		if ( prev_data > curr_data ) {
1553 			min_y = curr_data; max_y = prev_data;
1554 		} else {
1555 			min_y = prev_data; max_y = curr_data;
1556 		}
1557 		for(int k = min_y; k <= max_y; k++) {
1558 			int i = m_y_axis_size - k;
1559 			png_bytep u_row = m_row_ptrs[i];
1560 			set_rgb_color_val(u_row+3*j, graph_color, 0);
1561 		}
1562 		prev_data = curr_data;
1563 	}
1564 }
1565 
RequestData(int & size)1566 unsigned char *CDynStatisticImage::RequestData(int &size)
1567 {
1568 	DrawImage();
1569 
1570 	return CDynPngImage::RequestData(size);
1571 }
1572 
GetHTML()1573 wxString CDynStatisticImage::GetHTML()
1574 {
1575 	return wxEmptyString;
1576 }
1577 
1578 //
1579 // Imprint numbers on generated png's
1580 //
1581 //                                                 0     1     2     3     4     5     6     7     8     9
1582 const int CNumImageMask::m_num_to_7_decode[] = {0x77, 0x24, 0x5d, 0x6d, 0x2e, 0x5d, 0x7a, 0x25, 0x7f, 0x2f};
1583 
CNumImageMask(int number,int width,int height)1584 CNumImageMask::CNumImageMask(int number, int width, int height)
1585 {
1586 	m_v_segsize = height / 2;
1587 	m_h_segsize = width;
1588 	m_height = height;
1589 	m_width = width;
1590 
1591 	m_row_mask_ptrs = new png_bytep[m_height];
1592 	for(int i = 0; i < m_height; i++) {
1593 		m_row_mask_ptrs[i] = new png_byte[3*m_width];
1594 		memset(m_row_mask_ptrs[i], 0x00, 3*m_width);
1595 	}
1596 
1597 	int seg_status = m_num_to_7_decode[number];
1598 	for(int i = 0; i < 7; i++) {
1599 		if ( seg_status & (1 << i) ) {
1600 			DrawSegment(i);
1601 		}
1602 	}
1603 }
1604 
~CNumImageMask()1605 CNumImageMask::~CNumImageMask()
1606 {
1607 	for(int i = 0; i < m_height; i++) {
1608 		delete [] m_row_mask_ptrs[i];
1609 	}
1610 	delete [] m_row_mask_ptrs;
1611 }
1612 
Apply(png_bytep * image,int offx,int offy)1613 void CNumImageMask::Apply(png_bytep *image, int offx, int offy)
1614 {
1615 	offx *= 3;
1616 	for(int i = 0; i < m_height; i++) {
1617 		png_bytep img_row = image[offy + i];
1618 		png_bytep num_row = m_row_mask_ptrs[i];
1619 		for(int j = 0; j < m_width*3; j++) {
1620 			img_row[offx + j] |= num_row[j];
1621 		}
1622 	}
1623 }
1624 
DrawHorzLine(int off)1625 void CNumImageMask::DrawHorzLine(int off)
1626 {
1627 	png_bytep m_row = m_row_mask_ptrs[off*(m_v_segsize-1)];
1628 	for(int i = 0; i < m_h_segsize; i++) {
1629 		m_row[i*3] = m_row[i*3+1] = m_row[i*3+2] = 0xff;
1630 	}
1631 }
1632 
DrawVertLine(int offx,int offy)1633 void CNumImageMask::DrawVertLine(int offx, int offy)
1634 {
1635 	for(int i = 0; i < m_v_segsize; i++) {
1636 		png_bytep m_row = m_row_mask_ptrs[offy*(m_v_segsize-1)+i];
1637 		int x_idx = offx*(m_h_segsize-1)*3;
1638 		m_row[x_idx] = m_row[x_idx+1] = m_row[x_idx+2] = 0xff;
1639 	}
1640 }
1641 
1642 /*
1643  * Segment id decoding defined as following
1644  *
1645  *   ---- 0 ----
1646  *   |         |
1647  *   1         2
1648  *   |___ 3 ___|
1649  *   |         |
1650  *   4         5
1651  *   |___ 6 ___|
1652  */
DrawSegment(int id)1653 void CNumImageMask::DrawSegment(int id)
1654 {
1655 	switch(id) {
1656 		case 0:
1657 			DrawHorzLine(0);
1658 			break;
1659 		case 1:
1660 			DrawVertLine(0, 0);
1661 			break;
1662 		case 2:
1663 			DrawVertLine(1, 0);
1664 			break;
1665 		case 3:
1666 			DrawHorzLine(1);
1667 			break;
1668 		case 4:
1669 			DrawVertLine(0, 1);
1670 			break;
1671 		case 5:
1672 			DrawVertLine(1, 1);
1673 			break;
1674 		case 6:
1675 			DrawHorzLine(2);
1676 			break;
1677 		default:
1678 			wxFAIL;
1679 			break;
1680 	}
1681 }
1682 
1683 #endif
1684 
CImageLib(wxString image_dir)1685 CImageLib::CImageLib(wxString image_dir) : m_image_dir(image_dir)
1686 {
1687 }
1688 
~CImageLib()1689 CImageLib::~CImageLib()
1690 {
1691 }
1692 
AddImage(CAnyImage * img,const wxString & name)1693 void CImageLib::AddImage(CAnyImage *img, const wxString &name)
1694 {
1695 	ImageMap::iterator it = m_image_map.find(name);
1696 	if (it == m_image_map.end()) {
1697 		m_image_map[name] = img;
1698 	} else {
1699 		delete it->second;
1700 		it->second = img;
1701 	}
1702 }
1703 
RemoveImage(const wxString & name)1704 void CImageLib::RemoveImage(const wxString &name)
1705 {
1706 	ImageMap::iterator it = m_image_map.find(name);
1707 	if (it != m_image_map.end()) {
1708 		delete it->second;
1709 		m_image_map.erase(it);
1710 	}
1711 }
1712 
GetImage(const wxString & name)1713 CAnyImage *CImageLib::GetImage(const wxString &name)
1714 {
1715 	ImageMap::iterator it = m_image_map.find(name);
1716 	if (it != m_image_map.end()) {
1717 		return it->second;
1718 	}
1719 	wxFileName filename(m_image_dir + name);
1720 	CFileImage *fimg = new CFileImage(filename.GetFullPath());
1721 	if ( fimg->OpenOk() ) {
1722 		m_image_map[name] = fimg;
1723 		return fimg;
1724 	} else {
1725 		delete fimg;
1726 		return 0;
1727 	}
1728 }
1729 
1730 /*
1731  * Script-based webserver
1732  */
CScriptWebServer(CamulewebApp * webApp,const wxString & templateDir)1733 CScriptWebServer::CScriptWebServer(CamulewebApp *webApp, const wxString& templateDir)
1734 	: CWebServerBase(webApp, templateDir), m_wwwroot(templateDir)
1735 {
1736 	wxString img_tmpl(wxT("<img src=\"%s\" height=\"20\" width=\"%d\" alt=\"%s\">"));
1737 	m_DownloadFileInfo.LoadImageParams(img_tmpl, 200, 20);
1738 
1739 	if ( ::wxFileExists(wxFileName(m_wwwroot, wxT("index.html")).GetFullPath()) ) {
1740 		m_index = wxT("index.html");
1741 	} else if ( ::wxFileExists(wxFileName(m_wwwroot, wxT("index.php")).GetFullPath()) ) {
1742 		m_index = wxT("index.php");
1743 	} else {
1744 		webInterface->Show(_("Index file not found: ") +
1745 			wxFileName(m_wwwroot, wxT("index.{html,php}")).GetFullPath() + wxT("\n"));
1746 	}
1747 }
1748 
~CScriptWebServer()1749 CScriptWebServer::~CScriptWebServer()
1750 {
1751 }
1752 
GetErrorPage(const char * message,long & size)1753 char *CScriptWebServer::GetErrorPage(const char *message, long &size)
1754 {
1755 	char *buf = new char [1024];
1756 	snprintf(buf, 1024,
1757 		"<html><title> Error -%s </title></html>", message);
1758 
1759 	size = strlen(buf);
1760 
1761 	return buf;
1762 }
1763 
Get_404_Page(long & size)1764 char *CScriptWebServer::Get_404_Page(long &size)
1765 {
1766 		char *buf = new char [1024];
1767 		strcpy(buf, "<html><title> Error - requested page not found </title></html>");
1768 
1769 		size = strlen(buf);
1770 
1771 		return buf;
1772 }
1773 
ProcessHtmlRequest(const char * filename,long & size)1774 char *CScriptWebServer::ProcessHtmlRequest(const char *filename, long &size)
1775 {
1776 	FILE *f = fopen(filename, "r");
1777 	if ( !f ) {
1778 		return Get_404_Page(size);
1779 	}
1780 	if ( fseek(f, 0, SEEK_END) != 0 ) {
1781 		fclose(f);
1782 		return GetErrorPage("fseek failed", size);
1783 	}
1784 
1785 	size = ftell(f);
1786 	char *buf = new char [size+1];
1787 	rewind(f);
1788 	// fread may actually read less if it is a CR-LF-file in Windows
1789 	size = fread(buf, 1, size, f);
1790 	buf[size] = 0;
1791 	fclose(f);
1792 
1793 	return buf;
1794 }
1795 
ProcessPhpRequest(const char * filename,CSession * sess,long & size)1796 char *CScriptWebServer::ProcessPhpRequest(const char *filename, CSession *sess, long &size)
1797 {
1798 	FILE *f = fopen(filename, "r");
1799 	if ( !f ) {
1800 		return Get_404_Page(size);
1801 	}
1802 
1803 	CWriteStrBuffer buffer;
1804 	CPhpFilter(this, sess, filename, &buffer);
1805 
1806 	size = buffer.Length();
1807 	char *buf = new char [size+1];
1808 	buffer.CopyAll(buf);
1809 
1810 	fclose(f);
1811 
1812 	return buf;
1813 }
1814 
CheckLoggedin(ThreadData & Data)1815 CSession *CScriptWebServer::CheckLoggedin(ThreadData &Data)
1816 {
1817 	time_t curr_time = time(0);
1818 	CSession *session = 0;
1819 	if ( Data.SessionID && m_sessions.count(Data.SessionID) ) {
1820 		session = &m_sessions[Data.SessionID];
1821 		// session times out in 2 hours
1822 		if ( (curr_time - session->m_last_access) > 7200 ) {
1823 			Print(_("Session expired - requesting login\n"));
1824 			m_sessions.erase(Data.SessionID);
1825 			session = 0;
1826 		} else {
1827 			if ( session->m_loggedin ) {
1828 				Print(_("Session ok, logged in\n"));
1829 			} else {
1830 				Print(_("Session ok, not logged in\n"));
1831 			}
1832 			session->m_last_access = curr_time;
1833 		}
1834 	} else {
1835 		Print(_("No session opened - will request login\n"));
1836 	}
1837 	if ( !session ) {
1838 		while ( !Data.SessionID || m_sessions.count(Data.SessionID) ) {
1839 			Data.SessionID = rand();
1840 		}
1841 		session = &m_sessions[Data.SessionID];
1842 		session->m_last_access = curr_time;
1843 		session->m_loggedin = false;
1844 		Print(_("Session created - requesting login\n"));
1845 	}
1846 	Data.parsedURL.ConvertParams(session->m_get_vars);
1847 	return session;
1848 }
1849 
1850 
ProcessURL(ThreadData Data)1851 void CScriptWebServer::ProcessURL(ThreadData Data)
1852 {
1853 	long httpOutLen;
1854 	char *httpOut = 0;
1855 
1856 	// Strip out any path-component to prevent information leakage.
1857 	wxString filename = wxFileName(Data.parsedURL.File()).GetFullName();
1858 
1859 	Print(_("Processing request [original]: ") + filename + wxT("\n"));
1860 
1861 	if ( filename.Length() == 0 ) {
1862 		filename = m_index;
1863 	}
1864 
1865 	CSession *session = CheckLoggedin(Data);
1866 
1867 	session->m_vars["login_error"] = "";
1868 	if ( !session->m_loggedin ) {
1869 		filename = wxT("login.php");
1870 
1871 		wxString PwStr(Data.parsedURL.Param(wxT("pass")));
1872 		if (webInterface->m_AdminPass.IsEmpty() && webInterface->m_GuestPass.IsEmpty()) {
1873 			session->m_vars["login_error"] = "No password specified, login will not be allowed.";
1874 			Print(_("No password specified, login will not be allowed."));
1875 		} else if ( PwStr.Length() ) {
1876 			Print(_("Checking password\n"));
1877 			session->m_loggedin = false;
1878 
1879 			CMD4Hash PwHash;
1880 			if (!PwHash.Decode(MD5Sum(PwStr).GetHash())) {
1881 				Print(_("Password hash invalid\n"));
1882 				session->m_vars["login_error"] = "Invalid password hash, please report on http://forum.amule.org";
1883 			} else if ( PwHash == webInterface->m_AdminPass ) {
1884 				session->m_loggedin = true;
1885 				// m_vars is map<string, string> - so _() will not work here !
1886 				session->m_vars["guest_login"] = "0";
1887 			} else if ( PwHash == webInterface->m_GuestPass ) {
1888 				session->m_loggedin = true;
1889 				session->m_vars["guest_login"] = "1";
1890 			} else {
1891 				session->m_vars["login_error"] = "Password incorrect, please try again.";
1892 			}
1893 
1894 			if ( session->m_loggedin ) {
1895 				filename = m_index;
1896 				Print(_("Password ok\n"));
1897 			} else {
1898 				Print(_("Password bad\n"));
1899 			}
1900 		} else {
1901 			Print(_("You did not enter any password. Blank password is not allowed.\n"));
1902 		}
1903 	} else {
1904 		//
1905 		// if logged in, but requesting login page again,
1906 		// means logout command
1907 		//
1908 		if ( filename == wxT("login.php") ) {
1909 			Print(_("Logout requested\n"));
1910 			session->m_loggedin = false;
1911 		}
1912 	}
1913 
1914 	Print(_("Processing request [redirected]: ") + filename + wxT("\n"));
1915 
1916 	session->m_vars["auto_refresh"] = (const char *)unicode2char(
1917 		wxString(CFormat(wxT("%d")) % webInterface->m_PageRefresh));
1918 	session->m_vars["content_type"] = "text/html";
1919 
1920 	wxString req_file(wxFileName(m_wwwroot, filename).GetFullPath());
1921 	if (req_file.EndsWith(wxT(".html"))) {
1922 		httpOut = ProcessHtmlRequest(unicode2char(req_file), httpOutLen);
1923 	} else if (req_file.EndsWith(wxT(".php"))) {
1924 		httpOut = ProcessPhpRequest(unicode2char(req_file), session, httpOutLen);
1925 	} else if (req_file.EndsWith(wxT(".css"))) {
1926 		session->m_vars["content_type"] = "text/css";
1927 		httpOut = ProcessHtmlRequest(unicode2char(req_file), httpOutLen);
1928 	} else if (req_file.EndsWith(wxT(".js"))) {
1929 		session->m_vars["content_type"] = "text/javascript";
1930 		httpOut = ProcessHtmlRequest(unicode2char(req_file), httpOutLen);
1931 	} else if (	req_file.EndsWith(wxT(".dtd"))
1932 				|| req_file.EndsWith(wxT(".xsd"))
1933 				|| req_file.EndsWith(wxT(".xsl"))) {
1934 		session->m_vars["content_type"] = "text/xml";
1935 		httpOut = ProcessHtmlRequest(unicode2char(req_file), httpOutLen);
1936 	} else if (req_file.EndsWith(wxT(".appcache"))
1937 		   || req_file.EndsWith(wxT(".manifest"))) {
1938 		session->m_vars["content_type"] = "text/cache-manifest";
1939 		httpOut = ProcessHtmlRequest(unicode2char(req_file), httpOutLen);
1940 	} else if (req_file.EndsWith(wxT(".json"))) {
1941 		session->m_vars["content_type"] = "application/json";
1942 		httpOut = ProcessHtmlRequest(unicode2char(req_file), httpOutLen);
1943 	} else {
1944 		httpOut = GetErrorPage("aMuleweb doesn't handle the requested file type ", httpOutLen);
1945 	}
1946 
1947 	bool isUseGzip = webInterface->m_UseGzip;
1948 
1949 	if (isUseGzip)	{
1950 		bool bOk = false;
1951 		uLongf destLen = httpOutLen + 1024;
1952 		char *gzipOut = new char[destLen];
1953 		if( GzipCompress((Bytef*)gzipOut, &destLen,
1954 		   (const Bytef*)httpOut, httpOutLen, Z_DEFAULT_COMPRESSION) == Z_OK) {
1955 			bOk = true;
1956 		}
1957 		if ( bOk ) {
1958 			delete [] httpOut;
1959 			httpOut = gzipOut;
1960 			httpOutLen = destLen;
1961 		} else {
1962 			isUseGzip = false;
1963 			delete[] gzipOut;
1964 		}
1965 	}
1966 
1967 	if ( httpOut ) {
1968 		Data.pSocket->SendHttpHeaders(session->m_vars["content_type"].c_str(), isUseGzip, httpOutLen, Data.SessionID);
1969 		Data.pSocket->SendData(httpOut, httpOutLen);
1970 		delete [] httpOut;
1971 	}
1972 }
1973 
CNoTemplateWebServer(CamulewebApp * webApp)1974 CNoTemplateWebServer::CNoTemplateWebServer(CamulewebApp *webApp) : CScriptWebServer(webApp, wxEmptyString)
1975 {
1976 }
1977 
~CNoTemplateWebServer()1978 CNoTemplateWebServer::~CNoTemplateWebServer()
1979 {
1980 }
1981 
ProcessURL(ThreadData Data)1982 void CNoTemplateWebServer::ProcessURL(ThreadData Data)
1983 {
1984 	/*
1985 	 * Since template has not been found, I suspect that installation is broken. Falling back
1986 	 * into hardcoded page as last resort.
1987 	 */
1988 	const char *httpOut = ""
1989 	"<html>"
1990 		"<head>"
1991 			"<title>aMuleWeb error page</title>"
1992 			"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">"
1993 		"</head>"
1994 		"<body>"
1995 			"<p>You are seeing this page instead of aMuleWeb login page because a valid template has not been found.</p>"
1996 			"<p>This probably means that there's a problem with your aMule installation </p>"
1997 			"<ul>"
1998 				"<li>Before installing new versions, please ensure that you uninstalled older versions of aMule.</li>"
1999 				"<li>If you are installing by compiling from source, check configuration and run &quot;make&quot; and &quot;make install&quot; again </li>"
2000 				"<li>If you are installing by using a precompiled package, you may need to contact the package maintainer </li>"
2001 			"</ul>"
2002 			"<p>For more information please visit</p>"
2003 			"<p><a href=\"http://www.amule.org\">aMule main site</a> or <a href=\"http://forum.amule.org\">aMule forums</a></p>"
2004 		"</body>"
2005 	"</html>";
2006 
2007 	long httpOutLen = strlen(httpOut);
2008 
2009 	Data.pSocket->SendHttpHeaders("text/html", false, httpOutLen, 0);
2010 	Data.pSocket->SendData(httpOut, httpOutLen);
2011 }
2012 
2013 // File_checked_for_headers
2014