forked from SomeSupport/eMule
-
Notifications
You must be signed in to change notification settings - Fork 96
/
SearchFile.cpp
452 lines (412 loc) · 14.2 KB
/
SearchFile.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
// parts of this file are based on work from pan One (http://home-3.tiscali.nl/~meost/pms/)
//this file is part of eMule
//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either
//version 2 of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#include "stdafx.h"
#include "SearchFile.h"
#include "OtherFunctions.h"
#include "opcodes.h"
#include "Packets.h"
#include "Preferences.h"
#include "CxImage/xImage.h"
#include "Kademlia/Kademlia/Entry.h"
#include "emule.h"
#include "emuledlg.h"
#include "Searchdlg.h"
#ifdef _DEBUG
#include "DebugHelpers.h"
#endif
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
bool IsValidSearchResultClientIPPort(uint32 nIP, uint16 nPort)
{
return nIP != 0
&& nPort != 0
&& (ntohl(nIP) != nPort) // this filters most of the false data
&& ((nIP & 0x000000FF) != 0)
&& ((nIP & 0x0000FF00) != 0)
&& ((nIP & 0x00FF0000) != 0)
&& ((nIP & 0xFF000000) != 0);
}
void ConvertED2KTag(CTag*& pTag)
{
if (pTag->GetNameID() == 0 && pTag->GetName() != NULL)
{
static const struct
{
uint8 nID;
uint8 nED2KType;
LPCSTR pszED2KName;
} _aEmuleToED2KMetaTagsMap[] =
{
// Artist, Album and Title are disabled because they should be already part of the filename
// and would therefore be redundant information sent to the servers.. and the servers count the
// amount of sent data!
{ FT_MEDIA_ARTIST, TAGTYPE_STRING, FT_ED2K_MEDIA_ARTIST },
{ FT_MEDIA_ALBUM, TAGTYPE_STRING, FT_ED2K_MEDIA_ALBUM },
{ FT_MEDIA_TITLE, TAGTYPE_STRING, FT_ED2K_MEDIA_TITLE },
{ FT_MEDIA_LENGTH, TAGTYPE_STRING, FT_ED2K_MEDIA_LENGTH },
{ FT_MEDIA_LENGTH, TAGTYPE_UINT32, FT_ED2K_MEDIA_LENGTH },
{ FT_MEDIA_BITRATE, TAGTYPE_UINT32, FT_ED2K_MEDIA_BITRATE },
{ FT_MEDIA_CODEC, TAGTYPE_STRING, FT_ED2K_MEDIA_CODEC }
};
for (int j = 0; j < ARRSIZE(_aEmuleToED2KMetaTagsMap); j++)
{
if ( CmpED2KTagName(pTag->GetName(), _aEmuleToED2KMetaTagsMap[j].pszED2KName) == 0
&& ( (pTag->IsStr() && _aEmuleToED2KMetaTagsMap[j].nED2KType == TAGTYPE_STRING)
|| (pTag->IsInt() && _aEmuleToED2KMetaTagsMap[j].nED2KType == TAGTYPE_UINT32)))
{
if (pTag->IsStr())
{
if (_aEmuleToED2KMetaTagsMap[j].nID == FT_MEDIA_LENGTH)
{
UINT nMediaLength = 0;
UINT hour = 0, min = 0, sec = 0;
if (_stscanf(pTag->GetStr(), _T("%u : %u : %u"), &hour, &min, &sec) == 3)
nMediaLength = hour * 3600 + min * 60 + sec;
else if (_stscanf(pTag->GetStr(), _T("%u : %u"), &min, &sec) == 2)
nMediaLength = min * 60 + sec;
else if (_stscanf(pTag->GetStr(), _T("%u"), &sec) == 1)
nMediaLength = sec;
CTag* tag = (nMediaLength != 0) ? new CTag(_aEmuleToED2KMetaTagsMap[j].nID, nMediaLength) : NULL;
delete pTag;
pTag = tag;
}
else
{
CTag* tag = (!pTag->GetStr().IsEmpty())
? new CTag(_aEmuleToED2KMetaTagsMap[j].nID, pTag->GetStr())
: NULL;
delete pTag;
pTag = tag;
}
}
else if (pTag->IsInt())
{
CTag* tag = (pTag->GetInt() != 0)
? new CTag(_aEmuleToED2KMetaTagsMap[j].nID, pTag->GetInt())
: NULL;
delete pTag;
pTag = tag;
}
break;
}
}
}
}
///////////////////////////////////////////////////////////////////////////////
// CSearchFile
IMPLEMENT_DYNAMIC(CSearchFile, CAbstractFile)
CSearchFile::CSearchFile(const CSearchFile* copyfrom)
: CAbstractFile(copyfrom)
{
UpdateFileRatingCommentAvail();
m_nClientServerIP = copyfrom->GetClientServerIP();
m_nClientServerPort = copyfrom->GetClientServerPort();
m_nClientID = copyfrom->GetClientID();
m_nClientPort = copyfrom->GetClientPort();
m_pszDirectory = copyfrom->GetDirectory()? _tcsdup(copyfrom->GetDirectory()) : NULL;
m_nSearchID = copyfrom->GetSearchID();
m_bKademlia = copyfrom->IsKademlia();
const CSimpleArray<SClient>& clients = copyfrom->GetClients();
for (int i = 0; i < clients.GetSize(); i++)
AddClient(clients[i]);
const CSimpleArray<SServer>& servers = copyfrom->GetServers();
for (int i = 0; i < servers.GetSize(); i++)
AddServer(servers[i]);
m_list_bExpanded = false;
m_list_parent = const_cast<CSearchFile*>(copyfrom);
m_list_childcount = 0;
m_bPreviewPossible = false;
m_eKnown = copyfrom->m_eKnown;
m_strNameWithoutKeywords = copyfrom->GetNameWithoutKeyword();
m_bServerUDPAnswer = copyfrom->m_bServerUDPAnswer;
m_nSpamRating = copyfrom->GetSpamRating();
m_nKadPublishInfo = copyfrom->GetKadPublishInfo();
m_bMultipleAICHFound = copyfrom->m_bMultipleAICHFound;
}
CSearchFile::CSearchFile(CFileDataIO* in_data, bool bOptUTF8,
uint32 nSearchID, uint32 nServerIP, uint16 nServerPort, LPCTSTR pszDirectory, bool bKademlia, bool bServerUDPAnswer)
{
m_bMultipleAICHFound = false;
m_bKademlia = bKademlia;
m_bServerUDPAnswer = bServerUDPAnswer;
m_nSearchID = nSearchID;
m_FileIdentifier.SetMD4Hash(in_data);
m_nClientID = in_data->ReadUInt32();
m_nClientPort = in_data->ReadUInt16();
if ((m_nClientID || m_nClientPort) && !IsValidSearchResultClientIPPort(m_nClientID, m_nClientPort)){
if (thePrefs.GetDebugServerSearchesLevel() > 1)
Debug(_T("Filtered source from search result %s:%u\n"), DbgGetClientID(m_nClientID), m_nClientPort);
m_nClientID = 0;
m_nClientPort = 0;
}
UINT tagcount = in_data->ReadUInt32();
// NSERVER2.EXE (lugdunum v16.38 patched for Win32) returns the ClientIP+Port of the client which offered that
// file, even if that client has not filled the according fields in the OP_OFFERFILES packet with its IP+Port.
//
// 16.38.p73 (lugdunum) (propenprinz)
// *) does not return ClientIP+Port if the OP_OFFERFILES packet does not also contain it.
// *) if the OP_OFFERFILES packet does contain our HighID and Port the server returns that data at least when
// returning search results via TCP.
if (thePrefs.GetDebugServerSearchesLevel() > 1)
Debug(_T("Search Result: %s Client=%u.%u.%u.%u:%u Tags=%u\n"), md4str(m_FileIdentifier.GetMD4Hash()), (uint8)m_nClientID,(uint8)(m_nClientID>>8),(uint8)(m_nClientID>>16),(uint8)(m_nClientID>>24), m_nClientPort, tagcount);
// Copy/Convert ED2K-server tags to local tags
//
for (UINT i = 0; i < tagcount; i++)
{
CTag* tag = new CTag(in_data, bOptUTF8);
if (thePrefs.GetDebugServerSearchesLevel() > 1)
Debug(_T(" %s\n"), tag->GetFullInfo(DbgGetFileMetaTagName));
ConvertED2KTag(tag);
if (tag)
{
// Convert ED2K-server file rating tag
//
// NOTE: Feel free to do more with the received numbers here, but please do not add that particular
// received tag to the local tag list with the received tag format (packed rating). Either create
// a local tag with an eMule known rating value and drop the percentage (which is currently done),
// or add a second tag which holds the percentage in addition to the eMule-known rating value.
// Be aware, that adding that tag in packed-rating format will create troubles in other code parts!
if (tag->GetNameID() == FT_FILERATING && tag->IsInt())
{
uint16 nPackedRating = (uint16)tag->GetInt();
// Percent of clients (related to 'Availability') which rated on that file
UINT uPercentClientRatings = HIBYTE(nPackedRating);
(void)uPercentClientRatings;
// Average rating used by clients
UINT uAvgRating = LOBYTE(nPackedRating);
m_uUserRating = uAvgRating / (255/5/*RatingExcellent*/);
tag->SetInt(m_uUserRating);
}
else if (tag->GetNameID() == FT_AICH_HASH && tag->IsStr())
{
CAICHHash hash;
if (DecodeBase32(tag->GetStr(),hash) == (UINT)CAICHHash::GetHashSize())
m_FileIdentifier.SetAICHHash(hash);
else
ASSERT( false );
delete tag;
tag = NULL;
continue;
}
taglist.Add(tag);
}
}
// here we have two choices
// - if the server/client sent us a filetype, we could use it (though it could be wrong)
// - we always trust our filetype list and determine the filetype by the extension of the file
//
// if we received a filetype from server, we use it.
// if we did not receive a filetype, we determine it by examining the file's extension.
//
// but, in no case, we will use the receive file type when adding this search result to the download queue, to avoid
// that we are using 'wrong' file types in part files. (this has to be handled when creating the part files)
const CString& rstrFileType = GetStrTagValue(FT_FILETYPE);
SetFileName(GetStrTagValue(FT_FILENAME), false, rstrFileType.IsEmpty(), true);
uint64 ui64FileSize = 0;
CTag* pTagFileSize = GetTag(FT_FILESIZE);
if (pTagFileSize)
{
if (pTagFileSize->IsInt())
{
ui64FileSize = pTagFileSize->GetInt();
CTag* pTagFileSizeHi = GetTag(FT_FILESIZE_HI);
if (pTagFileSizeHi) {
if (pTagFileSizeHi->IsInt())
ui64FileSize |= (uint64)pTagFileSizeHi->GetInt() << 32;
DeleteTag(pTagFileSizeHi);
}
pTagFileSize->SetInt64(ui64FileSize);
}
else if (pTagFileSize->IsInt64(false))
{
ui64FileSize = pTagFileSize->GetInt64();
DeleteTag(FT_FILESIZE_HI);
}
}
SetFileSize(ui64FileSize);
if (!rstrFileType.IsEmpty())
{
if (_tcscmp(rstrFileType, _T(ED2KFTSTR_PROGRAM))==0)
{
CString strDetailFileType = GetFileTypeByName(GetFileName());
if (!strDetailFileType.IsEmpty())
SetFileType(strDetailFileType);
else
SetFileType(rstrFileType);
}
else
SetFileType(rstrFileType);
}
m_nClientServerIP = nServerIP;
m_nClientServerPort = nServerPort;
if (m_nClientServerIP && m_nClientServerPort){
SServer server(m_nClientServerIP, m_nClientServerPort, bServerUDPAnswer);
server.m_uAvail = GetIntTagValue(FT_SOURCES);
AddServer(server);
}
m_pszDirectory = pszDirectory ? _tcsdup(pszDirectory) : NULL;
m_list_bExpanded = false;
m_list_parent = NULL;
m_list_childcount = 0;
m_bPreviewPossible = false;
m_eKnown = NotDetermined;
m_nSpamRating = 0;
m_nKadPublishInfo = 0;
}
CSearchFile::~CSearchFile()
{
free(m_pszDirectory);
for (int i = 0; i < m_listImages.GetSize(); i++)
delete m_listImages[i];
}
void CSearchFile::StoreToFile(CFileDataIO& rFile) const
{
rFile.WriteHash16(m_FileIdentifier.GetMD4Hash());
rFile.WriteUInt32(m_nClientID);
rFile.WriteUInt16(m_nClientPort);
uint32 nTagCount = taglist.GetCount();
if (m_FileIdentifier.HasAICHHash())
nTagCount++;
rFile.WriteUInt32(nTagCount);
INT_PTR pos;
for (pos = 0; pos < taglist.GetCount(); pos++){
CTag* tag = taglist.GetAt(pos);
if (tag->GetNameID() == FT_FILERATING && tag->IsInt())
{
CTag temp(FT_FILERATING, (tag->GetInt() * (255/5)) & 0xFF);
temp.WriteNewEd2kTag(&rFile);
continue;
}
tag->WriteNewEd2kTag(&rFile, utf8strRaw);
}
if (m_FileIdentifier.HasAICHHash())
{
CTag aichtag(FT_AICH_HASH, m_FileIdentifier.GetAICHHash().GetString());
aichtag.WriteNewEd2kTag(&rFile);
}
}
void CSearchFile::UpdateFileRatingCommentAvail(bool bForceUpdate)
{
bool bOldHasComment = m_bHasComment;
UINT uOldUserRatings = m_uUserRating;
m_bHasComment = false;
UINT uRatings = 0;
UINT uUserRatings = 0;
for(POSITION pos = m_kadNotes.GetHeadPosition(); pos != NULL; )
{
Kademlia::CEntry* entry = m_kadNotes.GetNext(pos);
if (!m_bHasComment && !entry->GetStrTagValue(TAG_DESCRIPTION).IsEmpty())
m_bHasComment = true;
UINT rating = (UINT)entry->GetIntTagValue(TAG_FILERATING);
if (rating != 0)
{
uRatings++;
uUserRatings += rating;
}
}
// searchfile specific
// the file might have had a serverrating, don't change the rating if no kad ratings were found
if (uRatings)
m_uUserRating = (uint32)ROUND((float)uUserRatings / uRatings);
if (bOldHasComment != m_bHasComment || uOldUserRatings != m_uUserRating || bForceUpdate)
theApp.emuledlg->searchwnd->UpdateSearch(this);
}
uint32 CSearchFile::AddSources(uint32 count)
{
for (int i = 0; i < taglist.GetSize(); i++)
{
CTag* pTag = taglist[i];
if (pTag->GetNameID() == FT_SOURCES)
{
if (m_bKademlia)
{
if (count > pTag->GetInt())
pTag->SetInt(count);
}
else
pTag->SetInt(pTag->GetInt() + count);
return pTag->GetInt();
}
}
// FT_SOURCES is not yet supported by clients, we may have to create such a tag..
CTag* pTag = new CTag(FT_SOURCES, count);
taglist.Add(pTag);
return count;
}
uint32 CSearchFile::GetSourceCount() const
{
return GetIntTagValue(FT_SOURCES);
}
uint32 CSearchFile::AddCompleteSources(uint32 count)
{
for (int i = 0; i < taglist.GetSize(); i++)
{
CTag* pTag = taglist[i];
if (pTag->GetNameID() == FT_COMPLETE_SOURCES)
{
if (m_bKademlia)
{
if (count > pTag->GetInt())
pTag->SetInt(count);
}
else
pTag->SetInt(pTag->GetInt() + count);
return pTag->GetInt();
}
}
// FT_COMPLETE_SOURCES is not yet supported by all servers, we may have to create such a tag..
CTag* pTag = new CTag(FT_COMPLETE_SOURCES, count);
taglist.Add(pTag);
return count;
}
uint32 CSearchFile::GetCompleteSourceCount() const
{
return GetIntTagValue(FT_COMPLETE_SOURCES);
}
int CSearchFile::IsComplete() const
{
return IsComplete(GetSourceCount(), GetIntTagValue(FT_COMPLETE_SOURCES));
}
int CSearchFile::IsComplete(UINT uSources, UINT uCompleteSources) const
{
if (IsKademlia()) {
return -1; // unknown
}
else if (GetDirectory() != NULL && uSources == 1 && uCompleteSources == 0) {
// If this 'search' result is from a remote client 'View Shared Files' answer, we don't yet have
// any 'complete' information (could though be implemented some day) -> don't show the file as
// incomplete though. Treat it as 'unknown'.
return -1; // unknown
}
else if (uSources > 0 && uCompleteSources > 0) {
return 1; // complete
}
else {
return 0; // not complete
}
}
time_t CSearchFile::GetLastSeenComplete() const
{
return GetIntTagValue(FT_LASTSEENCOMPLETE);
}
bool CSearchFile::IsConsideredSpam() const{
return GetSpamRating() >= SEARCH_SPAM_THRESHOLD;
}