1//
2// This file is part of the aMule Project.
3//
4// Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5// Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net )
6//
7// Any parts of this program derived from the xMule, lMule or eMule project,
8// or contributed by third-party developers are copyrighted by their
9// respective authors.
10//
11// This program is free software; you can redistribute it and/or modify
12// it under the terms of the GNU General Public License as published by
13// the Free Software Foundation; either version 2 of the License, or
14// (at your option) any later version.
15//
16// This program is distributed in the hope that it will be useful,
17// but WITHOUT ANY WARRANTY; without even the implied warranty of
18// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19// GNU General Public License for more details.
20//
21// You should have received a copy of the GNU General Public License
22// along with this program; if not, write to the Free Software
23// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
24//
25
26#include "updownclient.h"		// Needed for CUpDownClient
27
28#include <protocol/Protocols.h>
29#include <protocol/ed2k/Client2Client/TCP.h>
30
31#include <zlib.h>
32
33#include "ClientCredits.h"	// Needed for CClientCredits
34#include "Packet.h"		// Needed for CPacket
35#include "MemFile.h"		// Needed for CMemFile
36#include "UploadQueue.h"	// Needed for CUploadQueue
37#include "DownloadQueue.h"	// Needed for CDownloadQueue
38#include "PartFile.h"		// Needed for PR_POWERSHARE
39#include "ClientTCPSocket.h"	// Needed for CClientTCPSocket
40#include "SharedFileList.h"	// Needed for CSharedFileList
41#include "amule.h"		// Needed for theApp
42#include "ClientList.h"
43#include "Statistics.h"		// Needed for theStats
44#include "Logger.h"
45#include "ScopedPtr.h"		// Needed for CScopedArray
46#include "GuiEvents.h"		// Needed for Notify_*
47#include "FileArea.h"		// Needed for CFileArea
48
49
50//	members of CUpDownClient
51//	which are mainly used for uploading functions
52
53void CUpDownClient::SetUploadState(uint8 eNewState)
54{
55	if (eNewState != m_nUploadState) {
56		if (m_nUploadState == US_UPLOADING) {
57			// Reset upload data rate computation
58			m_nUpDatarate = 0;
59			m_nSumForAvgUpDataRate = 0;
60			m_AvarageUDR_list.clear();
61		}
62		if (eNewState == US_UPLOADING) {
63			m_fSentOutOfPartReqs = 0;
64		}
65
66		// don't add any final cleanups for US_NONE here
67		m_nUploadState = eNewState;
68		UpdateDisplayedInfo(true);
69	}
70}
71
72uint32 CUpDownClient::CalculateScoreInternal()
73{
74	//TODO: complete this (friends, uploadspeed, amuleuser etc etc)
75	if (m_Username.IsEmpty()) {
76		return 0;
77	}
78
79	if (credits == 0) {
80		return 0;
81	}
82
83	const CKnownFile* pFile = GetUploadFile();
84	if ( !pFile ) {
85		return 0;
86	}
87
88	// bad clients (see note in function)
89	if (IsBadGuy()) {
90		return 0;
91	}
92
93	// friend slot
94	if (IsFriend() && GetFriendSlot() && !HasLowID()) {
95		return 0x0FFFFFFF;
96	}
97
98	if (IsBanned())
99		return 0;
100
101	// score applies only to waiting clients, not to downloading clients
102	if (IsDownloading()) {
103		return 0;
104	}
105
106	// calculate score, based on waitingtime and other factors
107	float fBaseValue = (float)(::GetTickCount()-GetWaitStartTime())/1000;
108
109	fBaseValue *= GetScoreRatio();	// credits
110
111	// Take file upload priority into account
112	//
113	// One yet unsolved problem here:
114	// sometimes a client asks for 2 files and there is no way to decide, which file the
115	// client finally gets. so it could happen that he is queued first because of a
116	// high prio file, but then asks for something completely different.
117	float filepriority;
118	switch (pFile->GetUpPriority()) {
119		case PR_POWERSHARE:
120			filepriority = 250.0f;
121			break;
122		case PR_VERYHIGH:
123			filepriority = 1.8f;
124			break;
125		case PR_HIGH:
126			filepriority = 0.9f;
127			break;
128		case PR_LOW:
129			filepriority = 0.6f;
130			break;
131		case PR_VERYLOW:
132			filepriority = 0.2f;
133			break;
134		case PR_NORMAL:
135		default:
136			filepriority = 0.7f;
137			break;
138	}
139	fBaseValue *= filepriority;
140
141	if ( (IsEmuleClient() || GetClientSoft() < 10) && m_byEmuleVersion <= 0x19) {
142		fBaseValue *= 0.5f;
143	}
144	return (uint32)fBaseValue;
145}
146
147
148// Checks if it is next requested block from another chunk of the actual file or from another file
149//
150// [Returns]
151//   true : Next requested block is from another different chunk or file than last downloaded block
152//   false: Next requested block is from same chunk that last downloaded block
153bool CUpDownClient::IsDifferentPartBlock() const // [Tarod 12/22/2002]
154{
155	bool different_part = false;
156
157	// Check if we have good lists and proceed to check for different chunks
158	if ((!m_BlockRequests_queue.empty()) && !m_DoneBlocks_list.empty())
159	{
160		Requested_Block_Struct* last_done_block = NULL;
161		Requested_Block_Struct* next_requested_block = NULL;
162		uint64 last_done_part = 0xffffffff;
163		uint64 next_requested_part = 0xffffffff;
164
165
166		// Get last block and next pending
167		last_done_block = m_DoneBlocks_list.front();
168		next_requested_block = m_BlockRequests_queue.front();
169
170		// Calculate corresponding parts to blocks
171		last_done_part = last_done_block->StartOffset / PARTSIZE;
172		next_requested_part = next_requested_block->StartOffset / PARTSIZE;
173
174		// Test is we are asking same file and same part
175		if ( last_done_part != next_requested_part) {
176			different_part = true;
177			AddDebugLogLineN(logClient, wxT("Session ended due to new chunk."));
178		}
179
180		if (md4cmp(last_done_block->FileID, next_requested_block->FileID) != 0) {
181			different_part = true;
182			AddDebugLogLineN(logClient, wxT("Session ended due to different file."));
183		}
184	}
185
186	return different_part;
187}
188
189
190void CUpDownClient::CreateNextBlockPackage()
191{
192	try {
193		// Buffer new data if current buffer is less than 100 KBytes
194		while (!m_BlockRequests_queue.empty()
195			   && m_addedPayloadQueueSession - m_nCurQueueSessionPayloadUp < 100*1024) {
196
197			Requested_Block_Struct* currentblock = m_BlockRequests_queue.front();
198			CKnownFile* srcfile = theApp->sharedfiles->GetFileByID(CMD4Hash(currentblock->FileID));
199
200			if (!srcfile) {
201				throw wxString(wxT("requested file not found"));
202			}
203
204			// Check if this know file is a CPartFile.
205			// For completed part files IsPartFile() returns false, so they are
206			// correctly treated as plain CKnownFile.
207			CPartFile* srcPartFile = srcfile->IsPartFile() ? (CPartFile*)srcfile : NULL;
208
209			// THIS EndOffset points BEHIND the last byte requested
210			// (other than the offsets used in the PartFile code)
211			if (currentblock->EndOffset > srcfile->GetFileSize()) {
212				throw wxString(CFormat(wxT("Asked for data up to %d beyond end of file (%d)"))
213									% currentblock->EndOffset % srcfile->GetFileSize());
214			} else if (currentblock->StartOffset > currentblock->EndOffset) {
215				throw wxString(CFormat(wxT("Asked for invalid block (start %d > end %d)"))
216									% currentblock->StartOffset % currentblock->EndOffset);
217			}
218
219			uint64 togo = currentblock->EndOffset - currentblock->StartOffset;
220
221			if (togo > EMBLOCKSIZE * 3) {
222				throw wxString(CFormat(wxT("Client requested too large block (%d > %d)"))
223									% togo % (EMBLOCKSIZE * 3));
224			}
225
226			CFileArea area;
227			if (srcPartFile) {
228				if (!srcPartFile->IsComplete(currentblock->StartOffset,currentblock->EndOffset-1)) {
229					throw wxString(CFormat(wxT("Asked for incomplete block (%d - %d)"))
230									% currentblock->StartOffset % (currentblock->EndOffset-1));
231				}
232				if (!srcPartFile->ReadData(area, currentblock->StartOffset, togo)) {
233					throw wxString(wxT("Failed to read from requested partfile"));
234				}
235			} else {
236				CFileAutoClose file;
237				CPath fullname = srcfile->GetFilePath().JoinPaths(srcfile->GetFileName());
238				if ( !file.Open(fullname, CFile::read) ) {
239					// The file was most likely moved/deleted. So remove it from the list of shared files.
240					AddLogLineN(CFormat( _("Failed to open file (%s), removing from list of shared files.") ) % srcfile->GetFileName() );
241					theApp->sharedfiles->RemoveFile(srcfile);
242
243					throw wxString(wxT("Failed to open requested file: Removing from list of shared files!"));
244				}
245				area.ReadAt(file, currentblock->StartOffset, togo);
246			}
247			area.CheckError();
248
249			SetUploadFileID(srcfile);
250
251			// check extention to decide whether to compress or not
252			if (m_byDataCompVer == 1 && GetFiletype(srcfile->GetFileName()) != ftArchive) {
253				CreatePackedPackets(area.GetBuffer(), togo, currentblock);
254			} else {
255				CreateStandardPackets(area.GetBuffer(), togo, currentblock);
256			}
257
258			// file statistic
259			srcfile->statistic.AddTransferred(togo);
260
261			m_addedPayloadQueueSession += togo;
262
263			Requested_Block_Struct* block = m_BlockRequests_queue.front();
264
265			m_BlockRequests_queue.pop_front();
266			m_DoneBlocks_list.push_front(block);
267		}
268
269		return;
270	} catch (const wxString& DEBUG_ONLY(error)) {
271		AddDebugLogLineN(logClient,
272			CFormat(wxT("Client '%s' (%s) caused error while creating packet (%s) - disconnecting client"))
273				% GetUserName() % GetFullIP() % error);
274	} catch (const CIOFailureException& error) {
275		AddDebugLogLineC(logClient, wxT("IO failure while reading requested file: ") + error.what());
276	} catch (const CEOFException& WXUNUSED(error)) {
277		AddDebugLogLineC(logClient, GetClientFullInfo() + wxT(" requested file-data at an invalid position - disconnecting"));
278	}
279
280	// Error occured.
281	theApp->uploadqueue->RemoveFromUploadQueue(this);
282}
283
284
285void CUpDownClient::CreateStandardPackets(const byte* buffer, uint32 togo, Requested_Block_Struct* currentblock)
286{
287	uint32 nPacketSize;
288
289	CMemFile memfile(buffer, togo);
290	if (togo > 10240) {
291		nPacketSize = togo/(uint32)(togo/10240);
292	} else {
293		nPacketSize = togo;
294	}
295
296	while (togo){
297		if (togo < nPacketSize*2) {
298			nPacketSize = togo;
299		}
300
301		wxASSERT(nPacketSize);
302		togo -= nPacketSize;
303
304		uint64 endpos = (currentblock->EndOffset - togo);
305		uint64 startpos = endpos - nPacketSize;
306
307		bool bLargeBlocks = (startpos > 0xFFFFFFFF) || (endpos > 0xFFFFFFFF);
308
309		CMemFile data(nPacketSize + 16 + 2 * (bLargeBlocks ? 8 :4));
310		data.WriteHash(GetUploadFileID());
311		if (bLargeBlocks) {
312			data.WriteUInt64(startpos);
313			data.WriteUInt64(endpos);
314		} else {
315			data.WriteUInt32(startpos);
316			data.WriteUInt32(endpos);
317		}
318		char *tempbuf = new char[nPacketSize];
319		memfile.Read(tempbuf, nPacketSize);
320		data.Write(tempbuf, nPacketSize);
321		delete [] tempbuf;
322		CPacket* packet = new CPacket(data, (bLargeBlocks ? OP_EMULEPROT : OP_EDONKEYPROT), (bLargeBlocks ? (uint8)OP_SENDINGPART_I64 : (uint8)OP_SENDINGPART));
323		theStats::AddUpOverheadFileRequest(16 + 2 * (bLargeBlocks ? 8 :4));
324		theStats::AddUploadToSoft(GetClientSoft(), nPacketSize);
325		AddDebugLogLineN(logLocalClient,
326			CFormat(wxT("Local Client: %s to %s"))
327				% (bLargeBlocks ? wxT("OP_SENDINGPART_I64") : wxT("OP_SENDINGPART")) % GetFullIP() );
328		m_socket->SendPacket(packet,true,false, nPacketSize);
329	}
330}
331
332
333void CUpDownClient::CreatePackedPackets(const byte* buffer, uint32 togo, Requested_Block_Struct* currentblock)
334{
335	uLongf newsize = togo+300;
336	CScopedArray<byte> output(newsize);
337	uint16 result = compress2(output.get(), &newsize, buffer, togo, 9);
338	if (result != Z_OK || togo <= newsize){
339		CreateStandardPackets(buffer, togo, currentblock);
340		return;
341	}
342
343	CMemFile memfile(output.get(), newsize);
344
345	uint32 totalPayloadSize = 0;
346	uint32 oldSize = togo;
347	togo = newsize;
348	uint32 nPacketSize;
349	if (togo > 10240) {
350		nPacketSize = togo/(uint32)(togo/10240);
351	} else {
352		nPacketSize = togo;
353	}
354
355	while (togo) {
356		if (togo < nPacketSize*2) {
357			nPacketSize = togo;
358		}
359		togo -= nPacketSize;
360
361		bool isLargeBlock = (currentblock->StartOffset > 0xFFFFFFFF) || (currentblock->EndOffset > 0xFFFFFFFF);
362
363		CMemFile data(nPacketSize + 16 + (isLargeBlock ? 12 : 8));
364		data.WriteHash(GetUploadFileID());
365		if (isLargeBlock) {
366			data.WriteUInt64(currentblock->StartOffset);
367		} else {
368			data.WriteUInt32(currentblock->StartOffset);
369		}
370		data.WriteUInt32(newsize);
371		char *tempbuf = new char[nPacketSize];
372		memfile.Read(tempbuf, nPacketSize);
373		data.Write(tempbuf,nPacketSize);
374		delete [] tempbuf;
375		CPacket* packet = new CPacket(data, OP_EMULEPROT, (isLargeBlock ? OP_COMPRESSEDPART_I64 : OP_COMPRESSEDPART));
376
377		// approximate payload size
378		uint32 payloadSize = nPacketSize*oldSize/newsize;
379
380		if (togo == 0 && totalPayloadSize+payloadSize < oldSize) {
381			payloadSize = oldSize-totalPayloadSize;
382		}
383
384		totalPayloadSize += payloadSize;
385
386		// put packet directly on socket
387		theStats::AddUpOverheadFileRequest(24);
388		theStats::AddUploadToSoft(GetClientSoft(), nPacketSize);
389		AddDebugLogLineN(logLocalClient,
390			CFormat(wxT("Local Client: %s to %s"))
391				% (isLargeBlock ? wxT("OP_COMPRESSEDPART_I64") : wxT("OP_COMPRESSEDPART")) % GetFullIP() );
392		m_socket->SendPacket(packet,true,false, payloadSize);
393	}
394}
395
396
397void CUpDownClient::ProcessExtendedInfo(const CMemFile *data, CKnownFile *tempreqfile)
398{
399	m_uploadingfile->UpdateUpPartsFrequency( this, false ); // Decrement
400	m_upPartStatus.clear();
401	m_nUpCompleteSourcesCount= 0;
402
403	if( GetExtendedRequestsVersion() == 0 ) {
404		// Something is coded wrong on this client if he's sending something it doesn't advertise.
405		return;
406	}
407
408	if (data->GetLength() == 16) {
409		// Wrong again. Advertised >0 but send a 0-type packet.
410		// But this time we'll disconnect it.
411		throw CInvalidPacket(wxT("Wrong size on extended info packet"));
412	}
413
414	uint16 nED2KUpPartCount = data->ReadUInt16();
415	if (!nED2KUpPartCount) {
416		m_upPartStatus.setsize( tempreqfile->GetPartCount(), 0 );
417	} else {
418		if (tempreqfile->GetED2KPartCount() != nED2KUpPartCount) {
419			// We already checked if we are talking about the same file.. So if we get here, something really strange happened!
420			m_upPartStatus.clear();
421			return;
422		}
423
424		m_upPartStatus.setsize( tempreqfile->GetPartCount(), 0 );
425
426		try {
427			uint16 done = 0;
428			while (done != m_upPartStatus.size()) {
429				uint8 toread = data->ReadUInt8();
430				for (sint32 i = 0;i != 8;i++){
431					m_upPartStatus.set(done, (toread>>i)&1);
432					//	We may want to use this for another feature..
433					//	if (m_upPartStatus[done] && !tempreqfile->IsComplete(done*PARTSIZE,((done+1)*PARTSIZE)-1))
434					// bPartsNeeded = true;
435					done++;
436					if (done == m_upPartStatus.size()) {
437						break;
438					}
439				}
440			}
441		} catch (...) {
442			// We want the increment the frequency even if we didn't read everything
443			m_uploadingfile->UpdateUpPartsFrequency( this, true ); // Increment
444
445			throw;
446		}
447
448		if (GetExtendedRequestsVersion() > 1) {
449			uint16 nCompleteCountLast = GetUpCompleteSourcesCount();
450			uint16 nCompleteCountNew = data->ReadUInt16();
451			SetUpCompleteSourcesCount(nCompleteCountNew);
452			if (nCompleteCountLast != nCompleteCountNew) {
453				tempreqfile->UpdatePartsInfo();
454			}
455		}
456	}
457
458	m_uploadingfile->UpdateUpPartsFrequency( this, true ); // Increment
459
460	Notify_SharedCtrlRefreshClient(ECID(), AVAILABLE_SOURCE);
461}
462
463
464void CUpDownClient::SetUploadFileID(CKnownFile* newreqfile)
465{
466	if (m_uploadingfile == newreqfile) {
467		return;
468	} else if (m_uploadingfile) {
469		m_uploadingfile->RemoveUploadingClient(this);
470		m_uploadingfile->UpdateUpPartsFrequency(this, false); // Decrement
471	}
472
473	if (newreqfile) {
474		// This is a new file! update info
475		newreqfile->AddUploadingClient(this);
476
477		if (m_requpfileid != newreqfile->GetFileHash()) {
478			m_requpfileid = newreqfile->GetFileHash();
479			m_upPartStatus.setsize( newreqfile->GetPartCount(), 0 );
480		} else {
481			// this is the same file we already had assigned. Only update data.
482			newreqfile->UpdateUpPartsFrequency(this, true); // Increment
483 		}
484
485		m_uploadingfile = newreqfile;
486	} else {
487		m_upPartStatus.clear();
488		m_nUpCompleteSourcesCount = 0;
489		// This clears m_uploadingfile and m_requpfileid
490		ClearUploadFileID();
491	}
492}
493
494
495void CUpDownClient::AddReqBlock(Requested_Block_Struct* reqblock)
496{
497	if (GetUploadState() != US_UPLOADING) {
498		AddDebugLogLineN(logRemoteClient, wxT("UploadClient: Client tried to add requested block when not in upload slot! Prevented requested blocks from being added."));
499		delete reqblock;
500		return;
501	}
502
503	{
504		std::list<Requested_Block_Struct*>::iterator it = m_DoneBlocks_list.begin();
505		for (; it != m_DoneBlocks_list.end(); ++it) {
506			if (reqblock->StartOffset == (*it)->StartOffset && reqblock->EndOffset == (*it)->EndOffset) {
507				delete reqblock;
508				return;
509			}
510		}
511	}
512
513	{
514		std::list<Requested_Block_Struct*>::iterator it = m_BlockRequests_queue.begin();
515		for (; it != m_BlockRequests_queue.end(); ++it) {
516			if (reqblock->StartOffset == (*it)->StartOffset && reqblock->EndOffset == (*it)->EndOffset) {
517				delete reqblock;
518				return;
519			}
520		}
521	}
522
523	m_BlockRequests_queue.push_back(reqblock);
524}
525
526
527uint32 CUpDownClient::GetWaitStartTime() const
528{
529	uint32 dwResult = 0;
530
531	if ( credits ) {
532		dwResult = credits->GetSecureWaitStartTime(GetIP());
533
534		if (dwResult > m_dwUploadTime && IsDownloading()) {
535			// This happens only if two clients with invalid securehash are in the queue - if at all
536			dwResult = m_dwUploadTime - 1;
537		}
538	}
539
540	return dwResult;
541}
542
543
544void CUpDownClient::SetWaitStartTime()
545{
546	if ( credits ) {
547		credits->SetSecWaitStartTime(GetIP());
548	}
549}
550
551
552void CUpDownClient::ClearWaitStartTime()
553{
554	if ( credits ) {
555		credits->ClearWaitStartTime();
556	}
557}
558
559
560void CUpDownClient::ResetSessionUp()
561{
562	m_nCurSessionUp = m_nTransferredUp;
563	m_addedPayloadQueueSession = 0;
564	m_nCurQueueSessionPayloadUp = 0;
565	// If upload was resumed there can be a remaining payload in the socket
566	// causing (prepared - sent) getting negative. So reset the counter here.
567	if (m_socket) {
568		CEMSocket* s = m_socket;
569		s->GetSentPayloadSinceLastCallAndReset();
570	}
571}
572
573
574uint32 CUpDownClient::SendBlockData()
575{
576    uint32 curTick = ::GetTickCount();
577    uint64 sentBytesCompleteFile = 0;
578    uint64 sentBytesPartFile = 0;
579    uint64 sentBytesPayload = 0;
580
581    if (m_socket) {
582		CEMSocket* s = m_socket;
583//		uint32 uUpStatsPort = GetUserPort();
584
585	    // Extended statistics information based on which client software and which port we sent this data to...
586	    // This also updates the grand total for sent bytes, etc.  And where this data came from.
587        sentBytesCompleteFile = s->GetSentBytesCompleteFileSinceLastCallAndReset();
588        sentBytesPartFile = s->GetSentBytesPartFileSinceLastCallAndReset();
589//		thePrefs.Add2SessionTransferData(GetClientSoft(), uUpStatsPort, false, true, sentBytesCompleteFile, (IsFriend() && GetFriendSlot()));
590//		thePrefs.Add2SessionTransferData(GetClientSoft(), uUpStatsPort, true, true, sentBytesPartFile, (IsFriend() && GetFriendSlot()));
591
592		m_nTransferredUp += sentBytesCompleteFile + sentBytesPartFile;
593        credits->AddUploaded(sentBytesCompleteFile + sentBytesPartFile, GetIP(), theApp->CryptoAvailable());
594
595        sentBytesPayload = s->GetSentPayloadSinceLastCallAndReset();
596        m_nCurQueueSessionPayloadUp += sentBytesPayload;
597
598        if (theApp->uploadqueue->CheckForTimeOver(this)) {
599            theApp->uploadqueue->RemoveFromUploadQueue(this);
600			SendOutOfPartReqsAndAddToWaitingQueue();
601        } else {
602            // read blocks from file and put on socket
603            CreateNextBlockPackage();
604        }
605    }
606
607    if(sentBytesCompleteFile + sentBytesPartFile > 0 ||
608        m_AvarageUDR_list.empty() || (curTick - m_AvarageUDR_list.back().timestamp) > 1*1000) {
609        // Store how much data we've transferred this round,
610        // to be able to calculate average speed later
611        // keep sum of all values in list up to date
612        TransferredData newitem = {sentBytesCompleteFile + sentBytesPartFile, curTick};
613        m_AvarageUDR_list.push_back(newitem);
614        m_nSumForAvgUpDataRate += sentBytesCompleteFile + sentBytesPartFile;
615    }
616
617    // remove to old values in list
618    while ((!m_AvarageUDR_list.empty()) && (curTick - m_AvarageUDR_list.front().timestamp) > 10*1000) {
619        // keep sum of all values in list up to date
620        m_nSumForAvgUpDataRate -= m_AvarageUDR_list.front().datalen;
621		m_AvarageUDR_list.pop_front();
622    }
623
624    // Calculate average speed for this slot
625    if ((!m_AvarageUDR_list.empty()) && (curTick - m_AvarageUDR_list.front().timestamp) > 0 && GetUpStartTimeDelay() > 2*1000) {
626        m_nUpDatarate = ((uint64)m_nSumForAvgUpDataRate*1000) / (curTick-m_AvarageUDR_list.front().timestamp);
627    } else {
628        // not enough values to calculate trustworthy speed. Use -1 to tell this
629        m_nUpDatarate = 0; //-1;
630    }
631
632    // Check if it's time to update the display.
633	m_cSendblock++;
634	if (m_cSendblock == 30){
635		m_cSendblock = 0;
636		Notify_SharedCtrlRefreshClient(ECID(), AVAILABLE_SOURCE);
637	}
638
639    return sentBytesCompleteFile + sentBytesPartFile;
640}
641
642
643void CUpDownClient::SendOutOfPartReqsAndAddToWaitingQueue()
644{
645	// Kry - this is actually taken from eMule, but makes a lot of sense ;)
646
647	//OP_OUTOFPARTREQS will tell the downloading client to go back to OnQueue..
648	//The main reason for this is that if we put the client back on queue and it goes
649	//back to the upload before the socket times out... We get a situation where the
650	//downloader thinks it already sent the requested blocks and the uploader thinks
651	//the downloader didn't send any request blocks. Then the connection times out..
652	//I did some tests with eDonkey also and it seems to work well with them also..
653
654	// Send this inmediately, don't queue.
655	CPacket* pPacket = new CPacket(OP_OUTOFPARTREQS, 0, OP_EDONKEYPROT);
656	theStats::AddUpOverheadFileRequest(pPacket->GetPacketSize());
657	AddDebugLogLineN( logLocalClient, wxT("Local Client: OP_OUTOFPARTREQS to ") + GetFullIP() );
658	SendPacket(pPacket, true, true);
659
660	theApp->uploadqueue->AddClientToQueue(this);
661}
662
663
664/**
665 * See description for CEMSocket::TruncateQueues().
666 */
667void CUpDownClient::FlushSendBlocks()
668{
669	// Call this when you stop upload, or the socket might be not able to send
670    if (m_socket) {    //socket may be NULL...
671        m_socket->TruncateQueues();
672	}
673}
674
675
676void CUpDownClient::SendHashsetPacket(const CMD4Hash& forfileid)
677{
678	CKnownFile* file = theApp->sharedfiles->GetFileByID( forfileid );
679	bool from_dq = false;
680	if ( !file ) {
681		from_dq = true;
682		if ((file = theApp->downloadqueue->GetFileByID(forfileid)) == NULL) {
683			AddLogLineN(CFormat( _("Hashset requested for unknown file: %s") ) % forfileid.Encode() );
684
685			return;
686		}
687	}
688
689	if ( !file->GetHashCount() ) {
690		if (from_dq) {
691			AddDebugLogLineN(logRemoteClient, wxT("Requested hashset could not be found"));
692			return;
693		} else {
694			file = theApp->downloadqueue->GetFileByID(forfileid);
695			if (!(file && file->GetHashCount())) {
696				AddDebugLogLineN(logRemoteClient, wxT("Requested hashset could not be found"));
697				return;
698			}
699		}
700	}
701
702	CMemFile data(1024);
703	data.WriteHash(file->GetFileHash());
704	uint16 parts = file->GetHashCount();
705	data.WriteUInt16(parts);
706	for (int i = 0; i != parts; i++) {
707		data.WriteHash(file->GetPartHash(i));
708	}
709	CPacket* packet = new CPacket(data, OP_EDONKEYPROT, OP_HASHSETANSWER);
710	theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
711	AddDebugLogLineN(logLocalClient, wxT("Local Client: OP_HASHSETANSWER to ") + GetFullIP());
712	SendPacket(packet,true,true);
713}
714
715
716void CUpDownClient::ClearUploadBlockRequests()
717{
718	FlushSendBlocks();
719	DeleteContents(m_BlockRequests_queue);
720	DeleteContents(m_DoneBlocks_list);
721}
722
723void CUpDownClient::SendRankingInfo(){
724	if (!ExtProtocolAvailable()) {
725		return;
726	}
727
728	uint16 nRank = GetUploadQueueWaitingPosition();
729	if (!nRank) {
730		return;
731	}
732
733	CMemFile data;
734	data.WriteUInt16(nRank);
735	// Kry: what are these zero bytes for. are they really correct?
736	// Kry - Well, eMule does like that. I guess they're ok.
737	data.WriteUInt32(0); data.WriteUInt32(0); data.WriteUInt16(0);
738	CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_QUEUERANKING);
739	theStats::AddUpOverheadOther(packet->GetPacketSize());
740	AddDebugLogLineN(logLocalClient, wxT("Local Client: OP_QUEUERANKING to ") + GetFullIP());
741	SendPacket(packet,true,true);
742}
743
744
745void CUpDownClient::SendCommentInfo(CKnownFile* file)
746{
747	if (!m_bCommentDirty || file == NULL || !ExtProtocolAvailable() || m_byAcceptCommentVer < 1) {
748		return;
749	}
750	m_bCommentDirty = false;
751
752	// Truncate to max len.
753	wxString desc = file->GetFileComment().Left(MAXFILECOMMENTLEN);
754	uint8 rating = file->GetFileRating();
755
756	if ( file->GetFileRating() == 0 && desc.IsEmpty() ) {
757		return;
758	}
759
760	CMemFile data(256);
761	data.WriteUInt8(rating);
762	data.WriteString(desc, GetUnicodeSupport(), 4 /* size it's uint32 */);
763
764	CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_FILEDESC);
765	theStats::AddUpOverheadOther(packet->GetPacketSize());
766	AddDebugLogLineN(logLocalClient, wxT("Local Client: OP_FILEDESC to ") + GetFullIP());
767	SendPacket(packet,true);
768}
769
770void  CUpDownClient::UnBan(){
771	m_Aggressiveness = 0;
772
773	theApp->clientlist->AddTrackClient(this);
774	theApp->clientlist->RemoveBannedClient( GetIP() );
775	SetUploadState(US_NONE);
776	ClearWaitStartTime();
777}
778
779void CUpDownClient::Ban(){
780	theApp->clientlist->AddTrackClient(this);
781	theApp->clientlist->AddBannedClient( GetIP() );
782
783	AddDebugLogLineN(logClient, wxT("Client '") + GetUserName() + wxT("' seems to be an aggressive client and is banned from the uploadqueue"));
784
785	SetUploadState(US_BANNED);
786
787	Notify_SharedCtrlRefreshClient(ECID(), UNAVAILABLE_SOURCE);
788}
789
790bool CUpDownClient::IsBanned() const
791{
792	return ( (theApp->clientlist->IsBannedClient(GetIP()) ) && m_nDownloadState != DS_DOWNLOADING);
793}
794
795void CUpDownClient::CheckForAggressive()
796{
797	uint32 cur_time = ::GetTickCount();
798
799	// First call, initalize
800	if ( !m_LastFileRequest ) {
801		m_LastFileRequest = cur_time;
802		return;
803	}
804
805	// Is this an aggressive request?
806	if ( ( cur_time - m_LastFileRequest ) < MIN_REQUESTTIME ) {
807		m_Aggressiveness += 3;
808
809		// Is the client EVIL?
810		if ( m_Aggressiveness >= 10 && (!IsBanned() && m_nDownloadState != DS_DOWNLOADING )) {
811			AddDebugLogLineN(logClient, CFormat( wxT("Aggressive client banned (score: %d): %s -- %s -- %s") )
812				% m_Aggressiveness
813				% m_Username
814				% m_strModVersion
815				% m_fullClientVerString );
816			Ban();
817		}
818	} else {
819		// Polite request, reward client
820		if ( m_Aggressiveness )
821			m_Aggressiveness--;
822	}
823
824	m_LastFileRequest = cur_time;
825}
826
827
828void CUpDownClient::SetUploadFileID(const CMD4Hash& new_id)
829{
830	// Update the uploading file found
831	CKnownFile* uploadingfile = theApp->sharedfiles->GetFileByID(new_id);
832	if ( !uploadingfile ) {
833		// Can this really happen?
834		uploadingfile = theApp->downloadqueue->GetFileByID(new_id);
835	}
836	SetUploadFileID(uploadingfile); // This will update queue count on old and new file.
837}
838
839void CUpDownClient::ProcessRequestPartsPacket(const byte* pachPacket, uint32 nSize, bool largeblocks) {
840
841	CMemFile data(pachPacket, nSize);
842
843	CMD4Hash reqfilehash = data.ReadHash();
844
845	uint64 auStartOffsets[3];
846	uint64 auEndOffsets[3];
847
848	if (largeblocks) {
849		auStartOffsets[0] = data.ReadUInt64();
850		auStartOffsets[1] = data.ReadUInt64();
851		auStartOffsets[2] = data.ReadUInt64();
852
853		auEndOffsets[0] = data.ReadUInt64();
854		auEndOffsets[1] = data.ReadUInt64();
855		auEndOffsets[2] = data.ReadUInt64();
856	} else {
857		auStartOffsets[0] = data.ReadUInt32();
858		auStartOffsets[1] = data.ReadUInt32();
859		auStartOffsets[2] = data.ReadUInt32();
860
861		auEndOffsets[0] = data.ReadUInt32();
862		auEndOffsets[1] = data.ReadUInt32();
863		auEndOffsets[2] = data.ReadUInt32();
864	}
865
866	for (unsigned int i = 0; i < itemsof(auStartOffsets); i++) {
867		AddDebugLogLineN(logClient,
868			CFormat(wxT("Client %s requests %d File block %d-%d (%d bytes):"))
869				% GetFullIP() % i % auStartOffsets[i] % auEndOffsets[i]
870				% (auEndOffsets[i] - auStartOffsets[i]));
871		if (auEndOffsets[i] > auStartOffsets[i]) {
872			Requested_Block_Struct* reqblock = new Requested_Block_Struct;
873			reqblock->StartOffset = auStartOffsets[i];
874			reqblock->EndOffset = auEndOffsets[i];
875			md4cpy(reqblock->FileID, reqfilehash.GetHash());
876			reqblock->transferred = 0;
877			AddReqBlock(reqblock);
878		} else {
879			if (auEndOffsets[i] != 0 || auStartOffsets[i] != 0) {
880				AddDebugLogLineN(logClient, wxT("Client request is invalid!"));
881			}
882		}
883	}
884}
885
886void CUpDownClient::ProcessRequestPartsPacketv2(const CMemFile& data) {
887
888	CMD4Hash reqfilehash = data.ReadHash();
889
890	uint8 numblocks = data.ReadUInt8();
891
892	for (int i = 0; i < numblocks; i++) {
893		Requested_Block_Struct* reqblock = new Requested_Block_Struct;
894		try {
895			reqblock->StartOffset = data.GetIntTagValue();
896			// We have to do +1, because the block matching uses that.
897			reqblock->EndOffset = data.GetIntTagValue() + 1;
898			if ((reqblock->StartOffset || reqblock->EndOffset) && (reqblock->StartOffset > reqblock->EndOffset)) {
899				AddDebugLogLineN(logClient, CFormat(wxT("Client %s request is invalid! %d / %d"))
900					% GetFullIP() % reqblock->StartOffset % reqblock->EndOffset);
901				throw wxString(wxT("Client request is invalid!"));
902			}
903
904			AddDebugLogLineN(logClient,
905				CFormat(wxT("Client %s requests %d File block %d-%d (%d bytes):"))
906					% GetFullIP() % i % reqblock->StartOffset % reqblock->EndOffset
907					% (reqblock->EndOffset - reqblock->StartOffset));
908
909			md4cpy(reqblock->FileID, reqfilehash.GetHash());
910			reqblock->transferred = 0;
911			AddReqBlock(reqblock);
912		} catch (...) {
913			delete reqblock;
914			throw;
915		}
916	}
917}
918// File_checked_for_headers
919