1// 2// This file is part of the aMule Project. 3// 4// Copyright (c) 2008-2011 D��vai Tam��s ( gonosztopi@amule.org ) 5// Copyright (c) 2004-2011 aMule Team ( admin@amule.org / http://www.amule.org ) 6// Copyright (c) 2003-2011 Barry Dunne (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// Note To Mods // 28/* 29Please do not change anything here and release it.. 30There is going to be a new forum created just for the Kademlia side of the client.. 31If you feel there is an error or a way to improve something, please 32post it in the forum first and let us look at it.. If it is a real improvement, 33it will be added to the offical client.. Changing something without knowing 34what all it does can cause great harm to the network if released in mass form.. 35Any mod that changes anything within the Kademlia side will not be allowed to advertise 36there client on the eMule forum.. 37*/ 38 39#include "Entry.h" 40#include <common/Macros.h> 41#include <tags/FileTags.h> 42#include <protocol/kad/Constants.h> 43#include "Indexed.h" 44#include "../../SafeFile.h" 45#include "../../GetTickCount.h" 46#include "../../Logger.h" 47#include "../../NetworkFunctions.h" 48 49using namespace Kademlia; 50 51CKeyEntry::GlobalPublishIPMap CKeyEntry::s_globalPublishIPs; 52 53 54///////////////////////////////////////////////////////////////////////////////////////////////////////////////// 55////// CEntry 56CEntry::~CEntry() 57{ 58 deleteTagPtrListEntries(&m_taglist); 59} 60 61CEntry* CEntry::Copy() const 62{ 63 CEntry* entry = new CEntry(); 64 for (FileNameList::const_iterator it = m_filenames.begin(); it != m_filenames.end(); ++it) { 65 entry->m_filenames.push_back(*it); 66 } 67 entry->m_uIP = m_uIP; 68 entry->m_uKeyID.SetValue(m_uKeyID); 69 entry->m_tLifeTime = m_tLifeTime; 70 entry->m_uSize = m_uSize; 71 entry->m_bSource = m_bSource; 72 entry->m_uSourceID.SetValue(m_uSourceID); 73 entry->m_uTCPport = m_uTCPport; 74 entry->m_uUDPport = m_uUDPport; 75 for (TagPtrList::const_iterator it = m_taglist.begin(); it != m_taglist.end(); ++it) { 76 entry->m_taglist.push_back((*it)->CloneTag()); 77 } 78 return entry; 79} 80 81bool CEntry::GetIntTagValue(const wxString& tagname, uint64_t& value, bool includeVirtualTags) const 82{ 83 for (TagPtrList::const_iterator it = m_taglist.begin(); it != m_taglist.end(); ++it) { 84 if ((*it)->IsInt() && ((*it)->GetName() == tagname)) { 85 value = (*it)->GetInt(); 86 return true; 87 } 88 } 89 90 if (includeVirtualTags) { 91 // SizeTag is not stored anymore, but queried in some places 92 if (tagname == TAG_FILESIZE) { 93 value = m_uSize; 94 return true; 95 } 96 } 97 value = 0; 98 return false; 99} 100 101wxString CEntry::GetStrTagValue(const wxString& tagname) const 102{ 103 for (TagPtrList::const_iterator it = m_taglist.begin(); it != m_taglist.end(); ++it) { 104 if (((*it)->GetName() == tagname) && (*it)->IsStr()) { 105 return (*it)->GetStr(); 106 } 107 } 108 return wxEmptyString; 109} 110 111void CEntry::SetFileName(const wxString& name) 112{ 113 if (!m_filenames.empty()) { 114 wxFAIL; 115 m_filenames.clear(); 116 } 117 sFileNameEntry sFN = { name, 1 }; 118 m_filenames.push_front(sFN); 119} 120 121wxString CEntry::GetCommonFileName() const 122{ 123 // return the filename on which most publishers seem to agree on 124 // due to the counting, this doesn't has to be excact, we just want to make sure to not use a filename which just 125 // a few bad publishers used and base or search matching and answering on this, instead of the most popular name 126 // Note: The Index values are not the acutal numbers of publishers, but just a relativ number to compare to other entries 127 FileNameList::const_iterator result = m_filenames.end(); 128 uint32_t highestPopularityIndex = 0; 129 for (FileNameList::const_iterator it = m_filenames.begin(); it != m_filenames.end(); ++it) { 130 if (it->m_popularityIndex > highestPopularityIndex) { 131 highestPopularityIndex = it->m_popularityIndex; 132 result = it; 133 } 134 } 135 wxString strResult(result != m_filenames.end() ? result->m_filename : wxString(wxEmptyString)); 136 wxASSERT(!strResult.IsEmpty() || m_filenames.empty()); 137 return strResult; 138} 139 140void CEntry::WriteTagListInc(CFileDataIO* data, uint32_t increaseTagNumber) 141{ 142 // write taglist and add name + size tag 143 wxCHECK_RET(data != NULL, wxT("data must not be NULL")); 144 145 uint32_t count = GetTagCount() + increaseTagNumber; // will include name and size tag in the count if needed 146 wxASSERT(count <= 0xFF); 147 data->WriteUInt8((uint8_t)count); 148 149 if (!GetCommonFileName().IsEmpty()){ 150 wxASSERT(count > m_taglist.size()); 151 data->WriteTag(CTagString(TAG_FILENAME, GetCommonFileName())); 152 } 153 if (m_uSize != 0){ 154 wxASSERT(count > m_taglist.size()); 155 data->WriteTag(CTagVarInt(TAG_FILESIZE, m_uSize)); 156 } 157 158 for (TagPtrList::const_iterator it = m_taglist.begin(); it != m_taglist.end(); ++it) { 159 data->WriteTag(**it); 160 } 161} 162 163 164///////////////////////////////////////////////////////////////////////////////////////////////////////////////// 165////// CKeyEntry 166CKeyEntry::CKeyEntry() 167{ 168 m_publishingIPs = NULL; 169 m_trustValue = 0; 170 m_lastTrustValueCalc = 0; 171} 172 173CKeyEntry::~CKeyEntry() 174{ 175 if (m_publishingIPs != NULL) { 176 for (PublishingIPList::const_iterator it = m_publishingIPs->begin(); it != m_publishingIPs->end(); ++it) { 177 AdjustGlobalPublishTracking(it->m_ip, false, wxT("instance delete")); 178 } 179 delete m_publishingIPs; 180 m_publishingIPs = NULL; 181 } 182} 183 184bool CKeyEntry::SearchTermsMatch(const SSearchTerm* searchTerm) const 185{ 186 // boolean operators 187 if (searchTerm->type == SSearchTerm::AND) { 188 return SearchTermsMatch(searchTerm->left) && SearchTermsMatch(searchTerm->right); 189 } 190 191 if (searchTerm->type == SSearchTerm::OR) { 192 return SearchTermsMatch(searchTerm->left) || SearchTermsMatch(searchTerm->right); 193 } 194 195 if (searchTerm->type == SSearchTerm::NOT) { 196 return SearchTermsMatch(searchTerm->left) && !SearchTermsMatch(searchTerm->right); 197 } 198 199 // word which is to be searched in the file name (and in additional meta data as done by some ed2k servers???) 200 if (searchTerm->type == SSearchTerm::String) { 201 int strSearchTerms = searchTerm->astr->GetCount(); 202 if (strSearchTerms == 0) { 203 return false; 204 } 205 // if there are more than one search strings specified (e.g. "aaa bbb ccc") the entire string is handled 206 // like "aaa AND bbb AND ccc". search all strings from the string search term in the tokenized list of 207 // the file name. all strings of string search term have to be found (AND) 208 wxString commonFileNameLower(GetCommonFileNameLowerCase()); 209 for (int i = 0; i < strSearchTerms; i++) { 210 // this will not give the same results as when tokenizing the filename string, but it is 20 times faster. 211 if (commonFileNameLower.Find((*(searchTerm->astr))[i]) == -1) { 212 return false; 213 } 214 } 215 return true; 216 } 217 218 if (searchTerm->type == SSearchTerm::MetaTag) { 219 if (searchTerm->tag->GetType() == 2) { // meta tags with string values 220 if (searchTerm->tag->GetName() == TAG_FILEFORMAT) { 221 // 21-Sep-2006 []: Special handling for TAG_FILEFORMAT which is already part 222 // of the filename and thus does not need to get published nor stored explicitly, 223 wxString commonFileName(GetCommonFileName()); 224 int ext = commonFileName.Find(wxT('.'), true); 225 if (ext != wxNOT_FOUND) { 226 return commonFileName.Mid(ext + 1).CmpNoCase(searchTerm->tag->GetStr()) == 0; 227 } 228 } else { 229 for (TagPtrList::const_iterator it = m_taglist.begin(); it != m_taglist.end(); ++it) { 230 if ((*it)->IsStr() && searchTerm->tag->GetName() == (*it)->GetName()) { 231 return (*it)->GetStr().CmpNoCase(searchTerm->tag->GetStr()) == 0; 232 } 233 } 234 } 235 } 236 } else if (searchTerm->type == SSearchTerm::OpGreaterEqual) { 237 if (searchTerm->tag->IsInt()) { // meta tags with integer values 238 uint64_t value; 239 if (GetIntTagValue(searchTerm->tag->GetName(), value, true)) { 240 return value >= searchTerm->tag->GetInt(); 241 } 242 } else if (searchTerm->tag->IsFloat()) { // meta tags with float values 243 for (TagPtrList::const_iterator it = m_taglist.begin(); it != m_taglist.end(); ++it) { 244 if ((*it)->IsFloat() && searchTerm->tag->GetName() == (*it)->GetName()) { 245 return (*it)->GetFloat() >= searchTerm->tag->GetFloat(); 246 } 247 } 248 } 249 } else if (searchTerm->type == SSearchTerm::OpLessEqual) { 250 if (searchTerm->tag->IsInt()) { // meta tags with integer values 251 uint64_t value; 252 if (GetIntTagValue(searchTerm->tag->GetName(), value, true)) { 253 return value <= searchTerm->tag->GetInt(); 254 } 255 } else if (searchTerm->tag->IsFloat()) { // meta tags with float values 256 for (TagPtrList::const_iterator it = m_taglist.begin(); it != m_taglist.end(); ++it) { 257 if ((*it)->IsFloat() && searchTerm->tag->GetName() == (*it)->GetName()) { 258 return (*it)->GetFloat() <= searchTerm->tag->GetFloat(); 259 } 260 } 261 } 262 } else if (searchTerm->type == SSearchTerm::OpGreater) { 263 if (searchTerm->tag->IsInt()) { // meta tags with integer values 264 uint64_t value; 265 if (GetIntTagValue(searchTerm->tag->GetName(), value, true)) { 266 return value > searchTerm->tag->GetInt(); 267 } 268 } else if (searchTerm->tag->IsFloat()) { // meta tags with float values 269 for (TagPtrList::const_iterator it = m_taglist.begin(); it != m_taglist.end(); ++it) { 270 if ((*it)->IsFloat() && searchTerm->tag->GetName() == (*it)->GetName()) { 271 return (*it)->GetFloat() > searchTerm->tag->GetFloat(); 272 } 273 } 274 } 275 } else if (searchTerm->type == SSearchTerm::OpLess) { 276 if (searchTerm->tag->IsInt()) { // meta tags with integer values 277 uint64_t value; 278 if (GetIntTagValue(searchTerm->tag->GetName(), value, true)) { 279 return value < searchTerm->tag->GetInt(); 280 } 281 } else if (searchTerm->tag->IsFloat()) { // meta tags with float values 282 for (TagPtrList::const_iterator it = m_taglist.begin(); it != m_taglist.end(); ++it) { 283 if ((*it)->IsFloat() && searchTerm->tag->GetName() == (*it)->GetName()) { 284 return (*it)->GetFloat() < searchTerm->tag->GetFloat(); 285 } 286 } 287 } 288 } else if (searchTerm->type == SSearchTerm::OpEqual) { 289 if (searchTerm->tag->IsInt()) { // meta tags with integer values 290 uint64_t value; 291 if (GetIntTagValue(searchTerm->tag->GetName(), value, true)) { 292 return value == searchTerm->tag->GetInt(); 293 } 294 } else if (searchTerm->tag->IsFloat()) { // meta tags with float values 295 for (TagPtrList::const_iterator it = m_taglist.begin(); it != m_taglist.end(); ++it) { 296 if ((*it)->IsFloat() && searchTerm->tag->GetName() == (*it)->GetName()) { 297 return (*it)->GetFloat() == searchTerm->tag->GetFloat(); 298 } 299 } 300 } 301 } else if (searchTerm->type == SSearchTerm::OpNotEqual) { 302 if (searchTerm->tag->IsInt()) { // meta tags with integer values 303 uint64_t value; 304 if (GetIntTagValue(searchTerm->tag->GetName(), value, true)) { 305 return value != searchTerm->tag->GetInt(); 306 } 307 } else if (searchTerm->tag->IsFloat()) { // meta tags with float values 308 for (TagPtrList::const_iterator it = m_taglist.begin(); it != m_taglist.end(); ++it) { 309 if ((*it)->IsFloat() && searchTerm->tag->GetName() == (*it)->GetName()) { 310 return (*it)->GetFloat() != searchTerm->tag->GetFloat(); 311 } 312 } 313 } 314 } 315 316 return false; 317} 318 319void CKeyEntry::AdjustGlobalPublishTracking(uint32_t ip, bool increase, const wxString& DEBUG_ONLY(dbgReason)) 320{ 321 uint32_t count = 0; 322 bool found = false; 323 GlobalPublishIPMap::const_iterator it = s_globalPublishIPs.find(ip & 0xFFFFFF00 /* /24 netmask, take care of endian if needed */ ); 324 if (it != s_globalPublishIPs.end()) { 325 count = it->second; 326 found = true; 327 } 328 329 if (increase) { 330 count++; 331 } else { 332 count--; 333 } 334 335 if (count > 0) { 336 s_globalPublishIPs[ip & 0xFFFFFF00] = count; 337 } else if (found) { 338 s_globalPublishIPs.erase(ip & 0xFFFFFF00); 339 } else { 340 wxFAIL; 341 } 342#ifdef __DEBUG__ 343 if (!dbgReason.IsEmpty()) { 344 AddDebugLogLineN(logKadEntryTracking, CFormat(wxT("%s %s (%s) - (%s), new count %u")) 345 % (increase ? wxT("Adding") : wxT("Removing")) % KadIPToString(ip & 0xFFFFFF00) % KadIPToString(ip) % dbgReason % count); 346 } 347#endif 348} 349 350void CKeyEntry::MergeIPsAndFilenames(CKeyEntry* fromEntry) 351{ 352 // this is called when replacing a stored entry with a refreshed one. 353 // we want to take over the tracked IPs and the different filenames from the old entry, the rest is still 354 // "overwritten" with the refreshed values. This might be not perfect for the taglist in some cases, but we can't afford 355 // to store hundreds of taglists to figure out the best one like we do for the filenames now 356 if (m_publishingIPs != NULL) { // This instance needs to be a new entry, otherwise we don't want/need to merge 357 wxASSERT(fromEntry == NULL); 358 wxASSERT(!m_publishingIPs->empty()); 359 wxASSERT(!m_filenames.empty()); 360 return; 361 } 362 363 bool refresh = false; 364 if (fromEntry == NULL || fromEntry->m_publishingIPs == NULL) { 365 wxASSERT(fromEntry == NULL); 366 // if called with NULL, this is a complete new entry and we need to initalize our lists 367 if (m_publishingIPs == NULL) { 368 m_publishingIPs = new PublishingIPList(); 369 } 370 // update the global track map below 371 } else { 372 // merge the tracked IPs, add this one if not already on the list 373 m_publishingIPs = fromEntry->m_publishingIPs; 374 fromEntry->m_publishingIPs = NULL; 375 bool fastRefresh = false; 376 for (PublishingIPList::iterator it = m_publishingIPs->begin(); it != m_publishingIPs->end(); ++it) { 377 if (it->m_ip == m_uIP) { 378 refresh = true; 379 if ((time(NULL) - it->m_lastPublish) < (KADEMLIAREPUBLISHTIMES - HR2S(1))) { 380 AddDebugLogLineN(logKadEntryTracking, wxT("FastRefresh publish, ip: ") + KadIPToString(m_uIP)); 381 fastRefresh = true; // refreshed faster than expected, will not count into filenamepopularity index 382 } 383 it->m_lastPublish = time(NULL); 384 m_publishingIPs->push_back(*it); 385 m_publishingIPs->erase(it); 386 break; 387 } 388 } 389 390 // copy over trust value, in case we don't want to recalculate 391 m_trustValue = fromEntry->m_trustValue; 392 m_lastTrustValueCalc = fromEntry->m_lastTrustValueCalc; 393 394 // copy over the different names, if they are different the one we have right now 395 wxASSERT(m_filenames.size() == 1); // we should have only one name here, since it's the entry from one single source 396 sFileNameEntry currentName = { wxEmptyString, 0 }; 397 if (m_filenames.size() != 0) { 398 currentName = m_filenames.front(); 399 m_filenames.pop_front(); 400 } 401 402 bool duplicate = false; 403 for (FileNameList::iterator it = fromEntry->m_filenames.begin(); it != fromEntry->m_filenames.end(); ++it) { 404 sFileNameEntry nameToCopy = *it; 405 if (currentName.m_filename.CmpNoCase(nameToCopy.m_filename) == 0) { 406 // the filename of our new entry matches with our old, increase the popularity index for the old one 407 duplicate = true; 408 if (!fastRefresh) { 409 nameToCopy.m_popularityIndex++; 410 } 411 } 412 m_filenames.push_back(nameToCopy); 413 } 414 if (!duplicate) { 415 m_filenames.push_back(currentName); 416 } 417 } 418 419 // if this was a refresh done, otherwise update the global track map 420 if (!refresh) { 421 wxASSERT(m_uIP != 0); 422 sPublishingIP add = { m_uIP, time(NULL) }; 423 m_publishingIPs->push_back(add); 424 425 // add the publisher to the tacking list 426 AdjustGlobalPublishTracking(m_uIP, true, wxT("new publisher")); 427 428 // we keep track of max 100 IPs, in order to avoid too much time for calculation/storing/loading. 429 if (m_publishingIPs->size() > 100) { 430 sPublishingIP curEntry = m_publishingIPs->front(); 431 m_publishingIPs->pop_front(); 432 AdjustGlobalPublishTracking(curEntry.m_ip, false, wxT("more than 100 publishers purge")); 433 } 434 435 // since we added a new publisher, we want to (re)calculate the trust value for this entry 436 ReCalculateTrustValue(); 437 } 438 AddDebugLogLineN(logKadEntryTracking, CFormat(wxT("Indexed Keyword, Refresh: %s, Current Publisher: %s, Total Publishers: %u, Total different Names: %u, TrustValue: %.2f, file: %s")) 439 % (refresh ? wxT("Yes") : wxT("No")) % KadIPToString(m_uIP) % m_publishingIPs->size() % m_filenames.size() % m_trustValue % m_uSourceID.ToHexString()); 440} 441 442void CKeyEntry::ReCalculateTrustValue() 443{ 444#define PUBLISHPOINTSSPERSUBNET 10.0 445 // The trustvalue is supposed to be an indicator how trustworthy/important (or spammy) this entry is and lies between 0 and ~10000, 446 // but mostly we say everything below 1 is bad, everything above 1 is good. It is calculated by looking at how many different 447 // IPs/24 have published this entry and how many entries each of those IPs have. 448 // Each IP/24 has x (say 3) points. This means if one IP publishes 3 different entries without any other IP publishing those entries, 449 // each of those entries will have 3 / 3 = 1 Trustvalue. Thats fine. If it publishes 6 alone, each entry has 3 / 6 = 0.5 trustvalue - not so good 450 // However if there is another publisher for entry 5, which only publishes this entry then we have 3/6 + 3/1 = 3.5 trustvalue for this entry 451 // 452 // What's the point? With this rating we try to avoid getting spammed with entries for a given keyword by a small IP range, which blends out 453 // all other entries for this keyword do to its amount as well as giving an indicator for the searcher. So if we are the node to index "Knoppix", and someone 454 // from 1 IP publishes 500 times "knoppix casino 500% bonus.txt", all those entries will have a trustvalue of 0.006 and we make sure that 455 // on search requests for knoppix, those entries are only returned after all entries with a trustvalue > 1 were sent (if there is still space). 456 // 457 // Its important to note that entry with < 1 do NOT get ignored or singled out, this only comes into play if we have 300 more results for 458 // a search request rating > 1 459 wxCHECK_RET(m_publishingIPs != NULL, wxT("No publishing IPs?")); 460 461 m_lastTrustValueCalc = ::GetTickCount(); 462 m_trustValue = 0; 463 wxASSERT(!m_publishingIPs->empty()); 464 for (PublishingIPList::iterator it = m_publishingIPs->begin(); it != m_publishingIPs->end(); ++it) { 465 sPublishingIP curEntry = *it; 466 uint32_t count = 0; 467 GlobalPublishIPMap::const_iterator itMap = s_globalPublishIPs.find(curEntry.m_ip & 0xFFFFFF00 /* /24 netmask, take care of endian if needed*/); 468 if (itMap != s_globalPublishIPs.end()) { 469 count = itMap->second; 470 } 471 if (count > 0) { 472 m_trustValue += PUBLISHPOINTSSPERSUBNET / count; 473 } else { 474 AddDebugLogLineN(logKadEntryTracking, wxT("Inconsistency in RecalcualteTrustValue()")); 475 wxFAIL; 476 } 477 } 478} 479 480double CKeyEntry::GetTrustValue() 481{ 482 // update if last calcualtion is too old, will assert if this entry is not supposed to have a trustvalue 483 if (::GetTickCount() - m_lastTrustValueCalc > MIN2MS(10)) { 484 ReCalculateTrustValue(); 485 } 486 return m_trustValue; 487} 488 489void CKeyEntry::CleanUpTrackedPublishers() 490{ 491 if (m_publishingIPs == NULL) { 492 return; 493 } 494 495 time_t now = time(NULL); 496 while (!m_publishingIPs->empty()) { 497 // entries are ordered, older ones first 498 sPublishingIP curEntry = m_publishingIPs->front(); 499 if (now - curEntry.m_lastPublish > KADEMLIAREPUBLISHTIMEK) { 500 AdjustGlobalPublishTracking(curEntry.m_ip, false, wxT("cleanup")); 501 m_publishingIPs->pop_front(); 502 } else { 503 break; 504 } 505 } 506} 507 508void CKeyEntry::WritePublishTrackingDataToFile(CFileDataIO* data) 509{ 510 // format: <Names_Count 4><{<Name string><PopularityIndex 4>} Names_Count><PublisherCount 4><{<IP 4><Time 4>} PublisherCount> 511 data->WriteUInt32((uint32_t)m_filenames.size()); 512 for (FileNameList::const_iterator it = m_filenames.begin(); it != m_filenames.end(); ++it) { 513 data->WriteString(it->m_filename, utf8strRaw, 2); 514 data->WriteUInt32(it->m_popularityIndex); 515 } 516 517 if (m_publishingIPs != NULL) { 518 data->WriteUInt32((uint32_t)m_publishingIPs->size()); 519 for (PublishingIPList::const_iterator it = m_publishingIPs->begin(); it != m_publishingIPs->end(); ++it) { 520 wxASSERT(it->m_ip != 0); 521 data->WriteUInt32(it->m_ip); 522 data->WriteUInt32((uint32_t)it->m_lastPublish); 523 } 524 } else { 525 wxFAIL; 526 data->WriteUInt32(0); 527 } 528} 529 530void CKeyEntry::ReadPublishTrackingDataFromFile(CFileDataIO* data) 531{ 532 // format: <Names_Count 4><{<Name string><PopularityIndex 4>} Names_Count><PublisherCount 4><{<IP 4><Time 4>} PublisherCount> 533 wxASSERT(m_filenames.empty()); 534 uint32_t nameCount = data->ReadUInt32(); 535 for (uint32_t i = 0; i < nameCount; i++) { 536 sFileNameEntry toAdd; 537 toAdd.m_filename = data->ReadString(true, 2); 538 toAdd.m_popularityIndex = data->ReadUInt32(); 539 m_filenames.push_back(toAdd); 540 } 541 542 wxASSERT(m_publishingIPs == NULL); 543 m_publishingIPs = new PublishingIPList(); 544 uint32_t ipCount = data->ReadUInt32(); 545#ifdef __WXDEBUG__ 546 uint32_t dbgLastTime = 0; 547#endif 548 for (uint32_t i = 0; i < ipCount; i++) { 549 sPublishingIP toAdd; 550 toAdd.m_ip = data->ReadUInt32(); 551 wxASSERT(toAdd.m_ip != 0); 552 toAdd.m_lastPublish = data->ReadUInt32(); 553#ifdef __WXDEBUG__ 554 wxASSERT(dbgLastTime <= (uint32_t)toAdd.m_lastPublish); // should always be sorted oldest first 555 dbgLastTime = toAdd.m_lastPublish; 556#endif 557 558 AdjustGlobalPublishTracking(toAdd.m_ip, true, wxEmptyString); 559 560 m_publishingIPs->push_back(toAdd); 561 } 562 ReCalculateTrustValue(); 563// #ifdef __DEBUG__ 564// if (GetTrustValue() < 1.0) { 565// AddDebugLogLineN(logKadEntryTracking,CFormat(wxT("Loaded %u different names, %u different publishIPs (trustvalue = %.2f) for file %s")) 566// % nameCount % ipCount % GetTrustValue() % m_uSourceID.ToHexString()); 567// } 568// #endif 569} 570 571void CKeyEntry::DirtyDeletePublishData() 572{ 573 // instead of deleting our publishers properly in the destructor with decreasing the count in the global map 574 // we just remove them, and trust that the caller in the end also resets the global map, so the 575 // kad shutdown is speed up a bit 576 delete m_publishingIPs; 577 m_publishingIPs = NULL; 578} 579 580void CKeyEntry::WriteTagListWithPublishInfo(CFileDataIO* data) 581{ 582 if (m_publishingIPs == NULL || m_publishingIPs->size() == 0) { 583 wxFAIL; 584 WriteTagList(data); 585 return; 586 } 587 588 // here we add a tag including how many publishers this entry has, the trustvalue and how many different names are known 589 // this is supposed to get used in later versions as an indicator for the user how valid this result is (of course this tag 590 // alone cannot be trusted 100%, because we could be a bad node, but it's a part of the puzzle) 591 592 WriteTagListInc(data, 1); // write the standard taglist but increase the tagcount by one 593 594 uint32_t trust = (uint16_t)(GetTrustValue() * 100); 595 uint32_t publishers = m_publishingIPs->size() & 0xFF /*% 256*/; 596 uint32_t names = m_filenames.size() & 0xFF /*% 256*/; 597 // 32 bit tag: <namecount uint8><publishers uint8><trustvalue*100 uint16> 598 uint32_t tagValue = (names << 24) | (publishers << 16) | trust; 599 data->WriteTag(CTagVarInt(TAG_PUBLISHINFO, tagValue)); 600} 601