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("&"));
77 str.Replace(wxT("<"),wxT("<"));
78 str.Replace(wxT(">"),wxT(">"));
79 str.Replace(wxT("\""),wxT("""));
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 "make" and "make install" 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