// // This file is part of the aMule Project. // // Parts of this file are based on work from pan One (http://home-3.tiscali.nl/~meost/pms/) // // Copyright (c) 2003-2006 aMule Team ( admin@amule.org / http://www.amule.org ) // Copyright (c) 2002 Merkur ( devs@emule-project.net / http://www.emule-project.net ) // // Any parts of this program derived from the xMule, lMule or eMule project, // or contributed by third-party developers are copyrighted by their // respective authors. // // 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA // #include #include #include #include #include #include #include #include #include #include "KnownFile.h" // Interface declarations. #include "OtherFunctions.h" // Needed for nstrdup #include "UploadQueue.h" // Needed for CUploadQueue #include "MemFile.h" // Needed for CMemFile #include "updownclient.h" // Needed for CUpDownClient #include "Tag.h" // Needed for CTag #include "Packet.h" // Needed for CPacket #include "Preferences.h" // Needed for CPreferences #include "SharedFileList.h" // Needed for CSharedFileList #include "KnownFileList.h" // Needed for CKnownFileList #include "amule.h" // Needed for theApp #include "PartFile.h" // Needed for SavePartFile #include "ClientList.h" // Needed for clientlist (buddy support) #include "ArchSpecific.h" #include "Logger.h" #include "kademlia/kademlia/Entry.h" #include // this is a magic incantation which must be done! #include "CryptoPP_Inc.h" // Needed for MD4 WX_DEFINE_OBJARRAY(ArrayOfCMD4Hash) WX_DEFINE_OBJARRAY(ArrayOfCTag) CFileStatistic::CFileStatistic() : requested(0), transfered(0), accepted(0), alltimerequested(0), alltimetransferred(0), alltimeaccepted(0) { } #ifndef CLIENT_GUI void CFileStatistic::AddRequest(){ requested++; alltimerequested++; theApp.knownfiles->requested++; theApp.sharedfiles->UpdateItem(fileParent); } void CFileStatistic::AddAccepted(){ accepted++; alltimeaccepted++; theApp.knownfiles->accepted++; theApp.sharedfiles->UpdateItem(fileParent); } void CFileStatistic::AddTransferred(uint64 bytes){ transfered += bytes; alltimetransferred += bytes; theApp.knownfiles->transfered += bytes; theApp.sharedfiles->UpdateItem(fileParent); } #endif // CLIENT_GUI /* Abstract File (base class)*/ CAbstractFile::CAbstractFile() : m_nFileSize(0), m_iRating(0), m_hasComment(false), m_iUserRating(0) { } void CAbstractFile::SetFileName(const wxString& strFileName) { m_strFileName = strFileName; } uint32 CAbstractFile::GetIntTagValue(uint8 tagname) const { for (unsigned int i = 0; i < taglist.Count(); i++){ const CTag* pTag = taglist[i]; if ((pTag->GetNameID() == tagname) && pTag->IsInt()) { return pTag->GetInt(); } } return 0; } bool CAbstractFile::GetIntTagValue(uint8 tagname, uint32& ruValue) const { for (unsigned int i = 0; i < taglist.Count(); i++){ const CTag* pTag = taglist[i]; if ((pTag->GetNameID() == tagname) && pTag->IsInt()){ ruValue = pTag->GetInt(); return true; } } return false; } uint32 CAbstractFile::GetIntTagValue(const char* tagname) const { for (unsigned int i = 0; i < taglist.Count(); i++){ const CTag* pTag = taglist[i]; if (pTag->IsInt() && pTag->GetName() && (CmpED2KTagName(pTag->GetName(), tagname)==0)) { return pTag->GetInt(); } } return 0; } const wxString& CAbstractFile::GetStrTagValue(uint8 tagname) const { for (unsigned int i = 0; i < taglist.Count(); i++){ const CTag* pTag = taglist[i]; if (pTag->GetNameID()==tagname && pTag->IsStr()) { return pTag->GetStr(); } } return EmptyString; } const wxString& CAbstractFile::GetStrTagValue(const char* tagname) const { for (unsigned int i = 0; i < taglist.Count(); i++){ const CTag* pTag = taglist[i]; if (pTag->IsStr() && pTag->GetName() && CmpED2KTagName(pTag->GetName(), tagname)==0) { return pTag->GetStr(); } } return EmptyString; } CTag* CAbstractFile::GetTag(uint8 tagname, uint8 tagtype) const { for (unsigned int i = 0; i < taglist.Count(); i++){ CTag* pTag = taglist[i]; if ((pTag->GetNameID()==tagname) && pTag->GetType()==tagtype) { return pTag; } } return NULL; } CTag* CAbstractFile::GetTag(const char* tagname, uint8 tagtype) const { for (unsigned int i = 0; i < taglist.Count(); i++){ CTag* pTag = taglist[i]; if (pTag->GetType()==tagtype && pTag->GetName() && (CmpED2KTagName(pTag->GetName(), tagname)==0)) { return pTag; } } return NULL; } CTag* CAbstractFile::GetTag(uint8 tagname) const { for (unsigned int i = 0; i < taglist.Count(); i++){ CTag* pTag = taglist[i]; if (pTag->GetNameID()==tagname) { return pTag; } } return NULL; } CTag* CAbstractFile::GetTag(const char* tagname) const { for (unsigned int i = 0; i < taglist.Count(); i++){ CTag* pTag = taglist[i]; if (pTag->GetName() && CmpED2KTagName(pTag->GetName(), tagname)==0) { return pTag; } } return NULL; } void CAbstractFile::AddTagUnique(CTag* pTag) { for (unsigned int i = 0; i < taglist.Count(); i++){ const CTag* pCurTag = taglist[i]; if ( ( (pCurTag->GetNameID()!=0 && pCurTag->GetNameID()==pTag->GetNameID()) || (pCurTag->GetName()!=NULL && pTag->GetName()!=NULL && CmpED2KTagName(pCurTag->GetName(), pTag->GetName())==0) ) && pCurTag->GetType() == pTag->GetType()){ taglist.RemoveAt(i); // When an item is removed from a ObjArray, it gets deleted, acording to docs. // delete pCurTag; taglist.Insert(pTag,i); return; } } taglist.Add(pTag); } #ifndef CLIENT_GUI void CAbstractFile::AddNote(Kademlia::CEntry *pEntry) { CKadEntryPtrList::iterator it = m_kadNotes.begin(); for (; it != m_kadNotes.end(); ++it) { Kademlia::CEntry* entry = *it; if(entry->ip == pEntry->ip || !entry->sourceID.compareTo(pEntry->sourceID)) { delete pEntry; return; } } m_kadNotes.push_front(pEntry); } #else void CAbstractFile::AddNote(Kademlia::CEntry *) { } #endif /* Known File */ CKnownFile::CKnownFile() { Init(); m_bAutoUpPriority = thePrefs::GetNewAutoUp(); m_iUpPriority = ( m_bAutoUpPriority ) ? PR_HIGH : PR_NORMAL; } void CKnownFile::Init() { date = 0; m_nCompleteSourcesTime = time(NULL); m_nCompleteSourcesCount = 0; m_nCompleteSourcesCountLo = 0; m_nCompleteSourcesCountHi = 0; m_bCommentLoaded = false; m_iPartCount = 0; m_iED2KPartCount = 0; m_iED2KPartHashCount = 0; m_PublishedED2K = false; kadFileSearchID = 0; m_lastPublishTimeKadSrc = 0; m_lastPublishTimeKadNotes = 0; m_lastBuddyIP = 0; statistic.fileParent = this; m_pAICHHashSet = new CAICHHashSet(this); } #ifdef CLIENT_GUI CKnownFile::CKnownFile(CEC_SharedFile_Tag *tag) { Init(); m_strFileName = tag->FileName(); m_abyFileHash = tag->ID(); m_nFileSize = tag->SizeFull(); m_iPartCount = (m_nFileSize + (PARTSIZE - 1)) / PARTSIZE; m_AvailPartFrequency.SetCount(m_iPartCount); m_iUpPriority = tag->Prio(); if ( m_iUpPriority >= 10 ) { m_iUpPriority-= 10; m_bAutoUpPriority = true; } else { m_bAutoUpPriority = false; } } CKnownFile::~CKnownFile() { hashlist.Clear(); delete m_pAICHHashSet; } #else // ! CLIENT_GUI CKnownFile::~CKnownFile(){ hashlist.Clear(); for (size_t i = 0; i != taglist.GetCount(); i++) { delete taglist[i]; } SourceSet::iterator it = m_ClientUploadList.begin(); for ( ; it != m_ClientUploadList.end(); ++it ) { (*it)->ClearUploadFileID(); } delete m_pAICHHashSet; } void CKnownFile::AddUploadingClient(CUpDownClient* client) { m_ClientUploadList.insert(client); UpdateAutoUpPriority(); } void CKnownFile::RemoveUploadingClient(CUpDownClient* client) { if (m_ClientUploadList.erase(client)) { UpdateAutoUpPriority(); } } void CKnownFile::SetFilePath(const wxString& strFilePath) { m_strFilePath = strFilePath; } bool CKnownFile::CreateAICHHashSetOnly() { wxASSERT( !IsPartFile() ); m_pAICHHashSet->FreeHashSet(); CFile file(JoinPaths(GetFilePath(), GetFileName()), CFile::read); if ( !file.IsOpened() ){ AddDebugLogLineM( false, logAICHThread, wxT("CreateAICHHashSetOnly(): Failed to open file: ") + file.GetFilePath() ); return false; } // create aichhashset uint32 togo = m_nFileSize; uint16 hashcount; for (hashcount = 0; togo >= PARTSIZE; ) { CAICHHashTree* pBlockAICHHashTree = m_pAICHHashSet->m_pHashTree.FindHash(hashcount*PARTSIZE, PARTSIZE); wxASSERT( pBlockAICHHashTree != NULL ); try { CreateHashFromFile(&file, PARTSIZE, NULL, pBlockAICHHashTree); } catch (const CIOFailureException& e) { AddDebugLogLineM(true, logAICHThread, wxT("CreateAICHHashSetOnly(): IO failure while hashing file: ") + e.what()); return false; } // SLUGFILLER: SafeHash - quick fallback if ( !theApp.IsRunning()){ // in case of shutdown while still hashing file.Close(); return false; } togo -= PARTSIZE; hashcount++; } if (togo != 0){ CAICHHashTree* pBlockAICHHashTree = m_pAICHHashSet->m_pHashTree.FindHash(hashcount*PARTSIZE, togo); wxASSERT( pBlockAICHHashTree != NULL ); try { CreateHashFromFile(&file, togo, NULL, pBlockAICHHashTree); } catch (const CIOFailureException& e) { AddDebugLogLineM(true, logAICHThread, wxT("CreateAICHHashSetOnly(): IO failure while hashing file: ") + e.what()); return false; } } m_pAICHHashSet->ReCalculateHash(false); if ( m_pAICHHashSet->VerifyHashTree(true) ){ m_pAICHHashSet->SetStatus(AICH_HASHSETCOMPLETE); if (!m_pAICHHashSet->SaveHashSet()){ AddDebugLogLineM( true, logAICHThread, wxT("Failed to save AICH Hashset!") ); } } else{ // now something went pretty wrong AddDebugLogLineM(true, logAICHThread, wxT("Failed to calculate AICH Hashset from file ") + GetFileName() ); } file.Close(); return true; } void CKnownFile::SetFileSize(uint32 nFileSize) { CAbstractFile::SetFileSize(nFileSize); m_pAICHHashSet->SetFileSize(nFileSize); // Examples of parthashs, hashsets and filehashs for different filesizes // according the ed2k protocol //---------------------------------------------------------------------- // //File size: 3 bytes //File hash: 2D55E87D0E21F49B9AD25F98531F3724 //Nr. hashs: 0 // // //File size: 1*PARTSIZE //File hash: A72CA8DF7F07154E217C236C89C17619 //Nr. hashs: 2 //Hash[ 0]: 4891ED2E5C9C49F442145A3A5F608299 //Hash[ 1]: 31D6CFE0D16AE931B73C59D7E0C089C0 *special part hash* // // //File size: 1*PARTSIZE + 1 byte //File hash: 2F620AE9D462CBB6A59FE8401D2B3D23 //Nr. hashs: 2 //Hash[ 0]: 121795F0BEDE02DDC7C5426D0995F53F //Hash[ 1]: C329E527945B8FE75B3C5E8826755747 // // //File size: 2*PARTSIZE //File hash: A54C5E562D5E03CA7D77961EB9A745A4 //Nr. hashs: 3 //Hash[ 0]: B3F5CE2A06BF403BFB9BFFF68BDDC4D9 //Hash[ 1]: 509AA30C9EA8FC136B1159DF2F35B8A9 //Hash[ 2]: 31D6CFE0D16AE931B73C59D7E0C089C0 *special part hash* // // //File size: 3*PARTSIZE //File hash: 5E249B96F9A46A18FC2489B005BF2667 //Nr. hashs: 4 //Hash[ 0]: 5319896A2ECAD43BF17E2E3575278E72 //Hash[ 1]: D86EF157D5E49C5ED502EDC15BB5F82B //Hash[ 2]: 10F2D5B1FCB95C0840519C58D708480F //Hash[ 3]: 31D6CFE0D16AE931B73C59D7E0C089C0 *special part hash* // // //File size: 3*PARTSIZE + 1 byte //File hash: 797ED552F34380CAFF8C958207E40355 //Nr. hashs: 4 //Hash[ 0]: FC7FD02CCD6987DCF1421F4C0AF94FB8 //Hash[ 1]: 2FE466AF8A7C06DA3365317B75A5ACFE //Hash[ 2]: 873D3BF52629F7C1527C6E8E473C1C30 //Hash[ 3]: BCE50BEE7877BB07BB6FDA56BFE142FB // // File size Data parts ED2K parts ED2K part hashs // --------------------------------------------------------------- // 1..PARTSIZE-1 1 1 0(!) // PARTSIZE 1 2(!) 2(!) // PARTSIZE+1 2 2 2 // PARTSIZE*2 2 3(!) 3(!) // PARTSIZE*2+1 3 3 3 if (nFileSize == 0){ //wxASSERT(0); // Kry - Why commented out by lemonfan? it can never be 0 m_iPartCount = 0; m_iED2KPartCount = 0; m_iED2KPartHashCount = 0; return; } // nr. of data parts m_iPartCount = ((uint64)nFileSize + (PARTSIZE - 1)) / PARTSIZE; // nr. of parts to be used with OP_FILESTATUS m_iED2KPartCount = nFileSize / PARTSIZE + 1; wxASSERT(m_iED2KPartCount <= 441); // nr. of parts to be used with OP_HASHSETANSWER m_iED2KPartHashCount = nFileSize / PARTSIZE; if (m_iED2KPartHashCount != 0) m_iED2KPartHashCount += 1; } // needed for memfiles. its probably better to switch everything to CFile... bool CKnownFile::LoadHashsetFromFile(const CFileDataIO* file, bool checkhash) { CMD4Hash checkid = file->ReadHash(); uint16 parts = file->ReadUInt16(); for (uint16 i = 0; i < parts; i++){ CMD4Hash cur_hash = file->ReadHash(); hashlist.Add(cur_hash); } // SLUGFILLER: SafeHash - always check for valid hashlist if (!checkhash){ m_abyFileHash = checkid; if (parts <= 1) { // nothing to check return true; } } else { if ( m_abyFileHash != checkid ) { return false; // wrong file? } else { if (parts != GetED2KPartHashCount()) { return false; } } } // SLUGFILLER: SafeHash // trust noone ;-) // lol, useless comment but made me lmao if (!hashlist.IsEmpty()){ byte buffer[hashlist.GetCount() * 16]; for (size_t i = 0;i != hashlist.GetCount();i++) { md4cpy(buffer+(i*16),hashlist[i].GetHash()); } CreateHashFromString(buffer,hashlist.GetCount()*16,checkid.GetHash()); } if ( m_abyFileHash == checkid ) { return true; } else { hashlist.Clear(); return false; } } bool CKnownFile::LoadTagsFromFile(const CFileDataIO* file) { uint32 tagcount = file->ReadUInt32(); for (uint32 j = 0; j != tagcount;j++) { CTag newtag(*file, true); switch(newtag.GetNameID()){ case FT_FILENAME: #if wxUSE_UNICODE if (GetFileName().IsEmpty()) #endif SetFileName(newtag.GetStr()); break; case FT_FILESIZE: SetFileSize(newtag.GetInt()); m_AvailPartFrequency.Clear(); m_AvailPartFrequency.Add(0, GetPartCount()); break; case FT_ATTRANSFERED: statistic.alltimetransferred += newtag.GetInt(); break; case FT_ATTRANSFEREDHI: statistic.alltimetransferred = (((uint64)newtag.GetInt()) << 32) + ((uint64)statistic.alltimetransferred); break; case FT_ATREQUESTED: statistic.alltimerequested = newtag.GetInt(); break; case FT_ATACCEPTED: statistic.alltimeaccepted = newtag.GetInt(); break; case FT_ULPRIORITY: m_iUpPriority = newtag.GetInt(); if( m_iUpPriority == PR_AUTO ){ m_iUpPriority = PR_HIGH; m_bAutoUpPriority = true; } else { if ( m_iUpPriority != PR_VERYLOW && m_iUpPriority != PR_LOW && m_iUpPriority != PR_NORMAL && m_iUpPriority != PR_HIGH && m_iUpPriority != PR_VERYHIGH && m_iUpPriority != PR_POWERSHARE) { m_iUpPriority = PR_NORMAL; } m_bAutoUpPriority = false; } break; case FT_PERMISSIONS: // Ignore it, it's not used anymore. break; case FT_AICH_HASH: { CAICHHash hash; bool hashSizeOk = hash.DecodeBase32(newtag.GetStr()) == CAICHHash::GetHashSize(); wxASSERT(hashSizeOk); if (hashSizeOk) { m_pAICHHashSet->SetMasterHash(hash, AICH_HASHSETCOMPLETE); } break; } case FT_KADLASTPUBLISHSRC: SetLastPublishTimeKadSrc( newtag.GetInt(), 0 ); if(GetLastPublishTimeKadSrc() > (uint32)time(NULL)+KADEMLIAREPUBLISHTIMES) { //There may be a posibility of an older client that saved a random number here.. This will check for that.. SetLastPublishTimeKadSrc(0, 0); } break; case FT_KADLASTPUBLISHNOTES: SetLastPublishTimeKadNotes( newtag.GetInt() ); break; case FT_KADLASTPUBLISHKEY: // Just purge it wxASSERT( newtag.IsInt() ); break; default: // Store them here and write them back on saving. taglist.Add(new CTag(newtag)); } } return true; } bool CKnownFile::LoadDateFromFile(const CFileDataIO* file) { date = file->ReadUInt32(); return true; } bool CKnownFile::LoadFromFile(const CFileDataIO* file) { // SLUGFILLER: SafeHash - load first, verify later bool ret1 = LoadDateFromFile(file); bool ret2 = LoadHashsetFromFile(file,false); bool ret3 = LoadTagsFromFile(file); UpdatePartsInfo(); // Final hash-count verification, needs to be done after the tags are loaded. return ret1 && ret2 && ret3 && GetED2KPartHashCount()==GetHashCount(); // SLUGFILLER: SafeHash } bool CKnownFile::WriteToFile(CFileDataIO* file) { wxCHECK(!IsPartFile(), false); // date file->WriteUInt32(date); // hashset file->WriteHash(m_abyFileHash); uint16 parts = hashlist.GetCount(); file->WriteUInt16(parts); for (int i = 0; i < parts; i++) file->WriteHash(hashlist[i]); //tags const int iFixedTags = 7; uint32 tagcount = iFixedTags; if ( m_pAICHHashSet->HasValidMasterHash() && ( m_pAICHHashSet->GetStatus() == AICH_HASHSETCOMPLETE || m_pAICHHashSet->GetStatus() == AICH_VERIFIED)) { tagcount++; } // Float meta tags are currently not written. All older eMule versions < 0.28a have // a bug in the meta tag reading+writing code. To achive maximum backward // compatibility for met files with older eMule versions we just don't write float // tags. This is OK, because we (eMule) do not use float tags. The only float tags // we may have to handle is the '# Sent' tag from the Hybrid, which is pretty // useless but may be received from us via the servers. // // The code for writing the float tags SHOULD BE ENABLED in SOME MONTHS (after most // people are using the newer eMule versions which do not write broken float tags). for (size_t j = 0; j < taglist.GetCount(); j++){ if (taglist[j]->IsInt() || taglist[j]->IsStr()) { tagcount++; } } #if wxUSE_UNICODE ++tagcount; #endif if (m_lastPublishTimeKadSrc) { ++tagcount; } if (m_lastPublishTimeKadNotes){ ++tagcount; } // standard tags file->WriteUInt32(tagcount); #if wxUSE_UNICODE CTag nametag_unicode(FT_FILENAME, GetFileName()); // We write it with BOM to kep eMule compatibility nametag_unicode.WriteTagToFile(file,utf8strOptBOM); #endif CTag nametag(FT_FILENAME, GetFileName()); nametag.WriteTagToFile(file); CTag sizetag(FT_FILESIZE, m_nFileSize); sizetag.WriteTagToFile(file); // statistic uint32 tran; tran=statistic.alltimetransferred & 0xFFFFFFFF; CTag attag1(FT_ATTRANSFERED, tran); attag1.WriteTagToFile(file); tran=statistic.alltimetransferred>>32; CTag attag4(FT_ATTRANSFEREDHI, tran); attag4.WriteTagToFile(file); CTag attag2(FT_ATREQUESTED, statistic.GetAllTimeRequests()); attag2.WriteTagToFile(file); CTag attag3(FT_ATACCEPTED, statistic.GetAllTimeAccepts()); attag3.WriteTagToFile(file); // priority N permission CTag priotag(FT_ULPRIORITY, IsAutoUpPriority() ? PR_AUTO : m_iUpPriority); priotag.WriteTagToFile(file); //AICH Filehash if ( m_pAICHHashSet->HasValidMasterHash() && ( m_pAICHHashSet->GetStatus() == AICH_HASHSETCOMPLETE || m_pAICHHashSet->GetStatus() == AICH_VERIFIED)) { CTag aichtag(FT_AICH_HASH, m_pAICHHashSet->GetMasterHash().GetString()); aichtag.WriteTagToFile(file); } // Kad sources if (m_lastPublishTimeKadSrc){ CTag kadLastPubSrc(FT_KADLASTPUBLISHSRC, m_lastPublishTimeKadSrc); kadLastPubSrc.WriteTagToFile(file); } // Kad notes if (m_lastPublishTimeKadNotes){ CTag kadLastPubNotes(FT_KADLASTPUBLISHNOTES, m_lastPublishTimeKadNotes); kadLastPubNotes.WriteTagToFile(file); } //other tags for (size_t j = 0; j < taglist.GetCount(); j++){ if (taglist[j]->IsInt() || taglist[j]->IsStr()) { taglist[j]->WriteTagToFile(file); } } return true; } void CKnownFile::CreateHashFromInput(CFileDataIO* file, uint32 Length, byte* Output, byte* in_string, CAICHHashTree* pShaHashOut) const { wxASSERT( Output != NULL || pShaHashOut != NULL); wxASSERT( Length <= PARTSIZE); // We never hash more than one PARTSIZE CAICHHashAlgo* pHashAlg = NULL; bool delete_in_string = false; try { if (file) { wxASSERT(!in_string); in_string = new unsigned char[Length]; delete_in_string = true; file->Read(in_string,Length); } CMemFile data(in_string, Length); uint32 Required = Length; byte X[64*128]; uint32 posCurrentEMBlock = 0; uint32 nIACHPos = 0; pHashAlg = m_pAICHHashSet->GetNewHashAlgo(); // This is all AICH. while (Required >= 64){ uint32 len = Required / 64; if (len > sizeof(X)/(64 * sizeof(X[0]))) { len = sizeof(X)/(64 * sizeof(X[0])); } data.Read(&X,len*64); // SHA hash needs 180KB blocks if (pShaHashOut != NULL){ if (nIACHPos + len*64 >= EMBLOCKSIZE){ uint32 nToComplete = EMBLOCKSIZE - nIACHPos; pHashAlg->Add(X, nToComplete); wxASSERT( nIACHPos + nToComplete == EMBLOCKSIZE ); pShaHashOut->SetBlockHash(EMBLOCKSIZE, posCurrentEMBlock, pHashAlg); posCurrentEMBlock += EMBLOCKSIZE; pHashAlg->Reset(); pHashAlg->Add(X+nToComplete,(len*64) - nToComplete); nIACHPos = (len*64) - nToComplete; } else{ pHashAlg->Add(X, len*64); nIACHPos += len*64; } } Required -= len*64; } // bytes to read Required = Length % 64; if (Required != 0){ data.Read(&X,Required); if (pShaHashOut != NULL){ if (nIACHPos + Required >= EMBLOCKSIZE){ uint32 nToComplete = EMBLOCKSIZE - nIACHPos; pHashAlg->Add(X, nToComplete); wxASSERT( nIACHPos + nToComplete == EMBLOCKSIZE ); pShaHashOut->SetBlockHash(EMBLOCKSIZE, posCurrentEMBlock, pHashAlg); posCurrentEMBlock += EMBLOCKSIZE; pHashAlg->Reset(); pHashAlg->Add(X+nToComplete, Required - nToComplete); nIACHPos = Required - nToComplete; } else{ pHashAlg->Add(X, Required); nIACHPos += Required; } } } if (pShaHashOut != NULL){ if(nIACHPos > 0){ pShaHashOut->SetBlockHash(nIACHPos, posCurrentEMBlock, pHashAlg); posCurrentEMBlock += nIACHPos; } wxASSERT( posCurrentEMBlock == Length ); wxCHECK2( pShaHashOut->ReCalculateHash(pHashAlg, false), ); } if (Output != NULL){ CryptoPP::MD4 md4_hasher; md4_hasher.CalculateDigest(Output, in_string, Length); } delete pHashAlg; if (delete_in_string) { delete[] in_string; } } catch (...) { delete pHashAlg; if (delete_in_string) { delete[] in_string; } throw; } } const CMD4Hash& CKnownFile::GetPartHash(uint16 part) const { wxASSERT( part < hashlist.GetCount() ); return hashlist[part]; } CPacket* CKnownFile::CreateSrcInfoPacket(const CUpDownClient* forClient) { // Kad reviewed if (((CKnownFile*)forClient->GetRequestFile() != this) && ((CKnownFile*)forClient->GetUploadFile() != this)) { wxString file1 = _("Unknown"); if (forClient->GetRequestFile() && !forClient->GetRequestFile()->GetFileName().IsEmpty()) { file1 = forClient->GetRequestFile()->GetFileName(); } else if (forClient->GetUploadFile() && !forClient->GetUploadFile()->GetFileName().IsEmpty()) { file1 = forClient->GetUploadFile()->GetFileName(); } wxString file2 = _("Unknown"); if (!GetFileName().IsEmpty()) { file2 = GetFileName(); } AddDebugLogLineM(false, logKnownFiles, wxT("File missmatch on source packet (K) Sending: ") + file1 + wxT(" From: ") + file2); return NULL; } if (m_ClientUploadList.empty() ) { return NULL; } const BitVector& rcvstatus = forClient->GetUpPartStatus(); bool SupportsUploadChunksState = !rcvstatus.empty(); //wxASSERT(rcvstatus.size() == GetPartCount()); // Obviously! if (rcvstatus.size() != GetPartCount()) { // Yuck. Same file but different part count? Seriously fucked up. AddDebugLogLineM(false, logKnownFiles, wxString::Format(wxT("Impossible situation: different partcounts for the same known file: %i (client) and %i (file)"),rcvstatus.size(),GetPartCount())); return NULL; } CMemFile data(1024); uint16 nCount = 0; data.WriteHash(forClient->GetUploadFileID()); data.WriteUInt16(nCount); uint32 cDbgNoSrc = 0; SourceSet::iterator it = m_ClientUploadList.begin(); for ( ; it != m_ClientUploadList.end(); it++ ) { const CUpDownClient *cur_src = *it; if ( cur_src->HasLowID() || cur_src == forClient || !( cur_src->GetUploadState() == US_UPLOADING || cur_src->GetUploadState() == US_ONUPLOADQUEUE)) { continue; } bool bNeeded = false; if ( SupportsUploadChunksState ) { const BitVector& srcstatus = cur_src->GetUpPartStatus(); if ( !srcstatus.empty() ) { //wxASSERT(srcstatus.size() == GetPartCount()); // Obviously! if (srcstatus.size() != GetPartCount()) { continue; } if ( cur_src->GetUpPartCount() == forClient->GetUpPartCount() ) { for (int x = 0; x < GetPartCount(); x++ ) { if ( srcstatus[x] && !rcvstatus[x] ) { // We know the receiving client needs // a chunk from this client. bNeeded = true; break; } } } } else { cDbgNoSrc++; // This client doesn't support upload chunk status. // So just send it and hope for the best. bNeeded = true; } } else { // remote client does not support upload chunk status, // search sources which have at least one complete part // we could even sort the list of sources by available // chunks to return as much sources as possible which // have the most available chunks. but this could be // a noticeable performance problem. const BitVector& srcstatus = cur_src->GetUpPartStatus(); if ( !srcstatus.empty() ) { //wxASSERT(srcstatus.size() == GetPartCount()); if (srcstatus.size() != GetPartCount()) { continue; } for (int x = 0; x < GetPartCount(); x++ ) { if ( srcstatus[x] ) { // this client has at least one chunk bNeeded = true; break; } } } else { // This client doesn't support upload chunk status. // So just send it and hope for the best. bNeeded = true; } } if ( bNeeded ) { nCount++; uint32 dwID; if(forClient->GetSourceExchangeVersion() > 2) { dwID = cur_src->GetUserIDHybrid(); } else { dwID = cur_src->GetIP(); } data.WriteUInt32(dwID); data.WriteUInt16(cur_src->GetUserPort()); data.WriteUInt32(cur_src->GetServerIP()); data.WriteUInt16(cur_src->GetServerPort()); if (forClient->GetSourceExchangeVersion() > 1) { data.WriteHash(cur_src->GetUserHash()); } if (nCount > 500) { break; } } } if (!nCount) { return 0; } data.Seek(16, wxFromStart); data.WriteUInt16(nCount); CPacket* result = new CPacket(&data, OP_EMULEPROT); result->SetOpCode( OP_ANSWERSOURCES ); if ( result->GetPacketSize() > 354 ) { result->PackPacket(); } return result; } // Updates priority of file if autopriority is activated void CKnownFile::UpdateAutoUpPriority() { if (IsAutoUpPriority()) { uint32 queued = GetQueuedCount(); uint8 priority = PR_NORMAL; if (queued > 20) { priority = PR_LOW; } else if (queued > 1) { priority = PR_NORMAL; } else { priority = PR_HIGH; } if (GetUpPriority() != priority) { SetUpPriority(priority, false); Notify_SharedFilesUpdateItem(this); } } } void CKnownFile::SetFileComment(const wxString& strNewComment) { if (m_strComment != strNewComment) { SetLastPublishTimeKadNotes(0); wxString strCfgPath = wxT("/") + m_abyFileHash.Encode() + wxT("/"); wxConfigBase* cfg = wxConfigBase::Get(); cfg->Write( strCfgPath + wxT("Comment"), strNewComment); m_strComment = strNewComment; SourceSet::iterator it = m_ClientUploadList.begin(); for ( ; it != m_ClientUploadList.end(); it++ ) { (*it)->SetCommentDirty(); } } } // For File rate void CKnownFile::SetFileRating(int8 iNewRating) { if (m_iRating != iNewRating) { SetLastPublishTimeKadNotes(0); wxString strCfgPath = wxT("/") + m_abyFileHash.Encode() + wxT("/"); wxConfigBase* cfg = wxConfigBase::Get(); cfg->Write( strCfgPath + wxT("Rate"), iNewRating); m_iRating = iNewRating; SourceSet::iterator it = m_ClientUploadList.begin(); for ( ; it != m_ClientUploadList.end(); it++ ) { (*it)->SetCommentDirty(); } } } void CKnownFile::SetUpPriority(uint8 iNewUpPriority, bool m_bsave){ m_iUpPriority = iNewUpPriority; if( IsPartFile() && m_bsave ) { ((CPartFile*)this)->SavePartFile(); } } void CKnownFile::SetPublishedED2K(bool val){ m_PublishedED2K = val; Notify_SharedFilesUpdateItem(this); } bool CKnownFile::PublishNotes() { if(m_lastPublishTimeKadNotes > (uint32)time(NULL)) { return false; } if(!GetFileComment().IsEmpty()) { m_lastPublishTimeKadNotes = (uint32)time(NULL)+KADEMLIAREPUBLISHTIMEN; return true; } if(GetFileRating() != 0) { m_lastPublishTimeKadNotes = (uint32)time(NULL)+KADEMLIAREPUBLISHTIMEN; return true; } return false; } bool CKnownFile::PublishSrc() { uint32 lastBuddyIP = 0; if( theApp.IsFirewalled() ) { CUpDownClient* buddy = theApp.clientlist->GetBuddy(); if( buddy ) { lastBuddyIP = theApp.clientlist->GetBuddy()->GetIP(); if( lastBuddyIP != m_lastBuddyIP ) { SetLastPublishTimeKadSrc( (uint32)time(NULL)+KADEMLIAREPUBLISHTIMES, lastBuddyIP ); return true; } } else { return false; } } if(m_lastPublishTimeKadSrc > (uint32)time(NULL)) { return false; } SetLastPublishTimeKadSrc((uint32)time(NULL)+KADEMLIAREPUBLISHTIMES,lastBuddyIP); return true; } void CKnownFile::UpdatePartsInfo() { // Cache part count uint16 partcount = GetPartCount(); bool flag = (time(NULL) - m_nCompleteSourcesTime > 0); // Ensure the frequency-list is ready if ( m_AvailPartFrequency.GetCount() != GetPartCount() ) { m_AvailPartFrequency.Clear(); m_AvailPartFrequency.Add( 0, GetPartCount() ); } if (flag) { ArrayOfUInts16 count; count.Alloc(m_ClientUploadList.size()); SourceSet::iterator it = m_ClientUploadList.begin(); for ( ; it != m_ClientUploadList.end(); it++ ) { if ( !(*it)->GetUpPartStatus().empty() && (*it)->GetUpPartCount() == partcount ) { count.Add( (*it)->GetUpCompleteSourcesCount() ); } } m_nCompleteSourcesCount = m_nCompleteSourcesCountLo = m_nCompleteSourcesCountHi = 0; if( partcount > 0) { m_nCompleteSourcesCount = m_AvailPartFrequency[0]; } for (uint16 i = 1; i < partcount; i++) { if( m_nCompleteSourcesCount > m_AvailPartFrequency[i]) { m_nCompleteSourcesCount = m_AvailPartFrequency[i]; } } count.Add(m_nCompleteSourcesCount); count.Shrink(); int32 n = count.GetCount(); if (n > 0) { // Kry - Native wx functions instead count.Sort(Uint16CompareValues); // calculate range int i = n >> 1; // (n / 2) int j = (n * 3) >> 2; // (n * 3) / 4 int k = (n * 7) >> 3; // (n * 7) / 8 // For complete files, trust the people your uploading to more... // For low guess and normal guess count // - If we see more sources then the guessed low and // normal, use what we see. // - If we see less sources then the guessed low, // adjust network accounts for 100%, we account for // 0% with what we see and make sure we are still // above the normal. // For high guess // Adjust 100% network and 0% what we see. if (n < 20) { if ( count[i] < m_nCompleteSourcesCount ) { m_nCompleteSourcesCountLo = m_nCompleteSourcesCount; } else { m_nCompleteSourcesCountLo = count[i]; } m_nCompleteSourcesCount= m_nCompleteSourcesCountLo; m_nCompleteSourcesCountHi = count[j]; if( m_nCompleteSourcesCountHi < m_nCompleteSourcesCount ) { m_nCompleteSourcesCountHi = m_nCompleteSourcesCount; } } else { // Many sources.. // For low guess // Use what we see. // For normal guess // Adjust network accounts for 100%, we account for // 0% with what we see and make sure we are still above the low. // For high guess // Adjust network accounts for 100%, we account for 0% // with what we see and make sure we are still above the normal. m_nCompleteSourcesCountLo = m_nCompleteSourcesCount; m_nCompleteSourcesCount = count[j]; if( m_nCompleteSourcesCount < m_nCompleteSourcesCountLo ) { m_nCompleteSourcesCount = m_nCompleteSourcesCountLo; } m_nCompleteSourcesCountHi= count[k]; if( m_nCompleteSourcesCountHi < m_nCompleteSourcesCount ) { m_nCompleteSourcesCountHi = m_nCompleteSourcesCount; } } } m_nCompleteSourcesTime = time(NULL) + (60); } Notify_SharedFilesUpdateItem(this); } void CKnownFile::UpdateUpPartsFrequency( CUpDownClient* client, bool increment ) { const BitVector& freq = client->GetUpPartStatus(); if ( m_AvailPartFrequency.GetCount() != GetPartCount() ) { m_AvailPartFrequency.Clear(); m_AvailPartFrequency.Add( 0, GetPartCount() ); if ( !increment ) { return; } } unsigned int size = freq.size(); if ( size != m_AvailPartFrequency.GetCount() ) { return; } if ( increment ) { for ( unsigned int i = 0; i < size; i++ ) { if ( freq[i] ) { m_AvailPartFrequency[i]++; } } } else { for ( unsigned int i = 0; i < size; i++ ) { if ( freq[i] ) { m_AvailPartFrequency[i]--; } } } } void CKnownFile::ClearPriority() { if ( !m_bAutoUpPriority ) return; m_iUpPriority = ( m_bAutoUpPriority ) ? PR_HIGH : PR_NORMAL; UpdateAutoUpPriority(); } void CKnownFile::SetFileName(const wxString& strmakeFilename) { CAbstractFile::SetFileName(strmakeFilename); #ifndef CLIENT_GUI wordlist.clear(); Kademlia::CSearchManager::getWords(GetFileName(), &wordlist); #endif } #endif // CLIENT_GUI //For File Comment // void CKnownFile::LoadComment() { #ifndef CLIENT_GUI wxString strCfgPath = wxT("/") + m_abyFileHash.Encode() + wxT("/"); wxConfigBase* cfg = wxConfigBase::Get(); m_strComment = cfg->Read( strCfgPath + wxT("Comment"), wxEmptyString); m_iRating = cfg->Read( strCfgPath + wxT("Rate"), 0l); m_bCommentLoaded = true; #else m_strComment = wxT("Comments are not allowed on remote gui yet"); #endif }