2 // This file is part of the aMule Project.
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2008 Merkur ( devs@emule-project.net / http://www.emule-project.net )
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
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.
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.
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
27 #include "KnownFileList.h" // Interface declarations
29 #include <common/DataFileVersion.h>
31 #include <memory> // Do_not_auto_remove (lionel's Mac, 10.3)
32 #include "PartFile.h" // Needed for CPartFile
35 #include "ScopedPtr.h"
36 #include "SearchList.h" // Needed for UpdateSearchFileByHash
37 #include <common/Format.h>
40 // This function is inlined for performance
41 inline bool CKnownFileList::KnownFileMatches(
42 CKnownFile
*knownFile
,
43 const CPath
& filename
,
48 (knownFile
->GetLastChangeDatetime() == (time_t)in_date
) &&
49 (knownFile
->GetFileSize() == in_size
) &&
50 (knownFile
->GetFileName() == filename
);
54 CKnownFileList::CKnownFileList()
59 m_filename
= wxT("known.met");
60 m_knownSizeMap
= NULL
;
61 m_duplicateSizeMap
= NULL
;
66 CKnownFileList::~CKnownFileList()
72 bool CKnownFileList::Init()
76 CPath fullpath
= CPath(theApp
->ConfigDir
+ m_filename
);
77 if (!fullpath
.FileExists()) {
78 // This is perfectly normal. The file was probably either
79 // deleted, or this is the first time running aMule.
83 if (!file
.Open(fullpath
)) {
84 AddLogLineC(CFormat(_("WARNING: %s cannot be opened.")) % m_filename
);
89 uint8 version
= file
.ReadUInt8();
90 if ((version
!= MET_HEADER
) && (version
!= MET_HEADER_WITH_LARGEFILES
)) {
91 AddLogLineC(_("WARNING: Known file list corrupted, contains invalid header."));
95 wxMutexLocker
sLock(list_mut
);
96 uint32 RecordsNumber
= file
.ReadUInt32();
97 AddDebugLogLineN(logKnownFiles
, CFormat(wxT("Reading %i known files from file format 0x%2.2x."))
98 % RecordsNumber
% version
);
99 for (uint32 i
= 0; i
< RecordsNumber
; i
++) {
100 CScopedPtr
<CKnownFile
> record(new CKnownFile());
101 if (record
->LoadFromFile(&file
)) {
102 AddDebugLogLineN(logKnownFiles
,
103 CFormat(wxT("Known file read: %s")) % record
->GetFileName());
104 Append(record
.release());
106 AddLogLineC(_("Failed to load entry in known file list, file may be corrupt"));
109 AddDebugLogLineN(logKnownFiles
, wxT("Finished reading known files"));
112 } catch (const CInvalidPacket
& e
) {
113 AddLogLineC(_("Invalid entry in known file list, file may be corrupt: ") + e
.what());
114 } catch (const CSafeIOException
& e
) {
115 AddLogLineC(CFormat(_("IO error while reading %s file: %s")) % m_filename
% e
.what());
122 void CKnownFileList::Save()
124 CFile
file(theApp
->ConfigDir
+ m_filename
, CFile::write
);
125 if (!file
.IsOpened()) {
129 wxMutexLocker
sLock(list_mut
);
132 // Kry - This is the version, but we don't know it till
133 // we know if any largefile is saved. This allows the list
134 // to be compatible with previous versions.
135 bool bContainsAnyLargeFiles
= false;
138 file
.WriteUInt32(m_knownFileMap
.size() + m_duplicateFileList
.size());
140 // Duplicates handling. Duplicates needs to be saved first,
141 // since it is the last entry that gets used.
142 KnownFileList::iterator itDup
= m_duplicateFileList
.begin();
143 for ( ; itDup
!= m_duplicateFileList
.end(); ++itDup
) {
144 (*itDup
)->WriteToFile(&file
);
145 if ((*itDup
)->IsLargeFile()) {
146 bContainsAnyLargeFiles
= true;
150 CKnownFileMap::iterator it
= m_knownFileMap
.begin();
151 for (; it
!= m_knownFileMap
.end(); ++it
) {
152 it
->second
->WriteToFile(&file
);
153 if (it
->second
->IsLargeFile()) {
154 bContainsAnyLargeFiles
= true;
159 file
.WriteUInt8(bContainsAnyLargeFiles
? MET_HEADER_WITH_LARGEFILES
: MET_HEADER
);
160 } catch (const CIOFailureException
& e
) {
161 AddLogLineC(CFormat(_("Error while saving %s file: %s")) % m_filename
% e
.what());
166 void CKnownFileList::Clear()
168 wxMutexLocker
sLock(list_mut
);
170 DeleteContents(m_knownFileMap
);
171 DeleteContents(m_duplicateFileList
);
176 CKnownFile
* CKnownFileList::FindKnownFile(
177 const CPath
& filename
,
181 wxMutexLocker
sLock(list_mut
);
183 if (m_knownSizeMap
) {
184 std::pair
<KnownFileSizeMap::const_iterator
, KnownFileSizeMap::const_iterator
> p
;
185 p
= m_knownSizeMap
->equal_range((uint32
) in_size
);
186 for (KnownFileSizeMap::const_iterator it
= p
.first
; it
!= p
.second
; it
++) {
187 CKnownFile
*cur_file
= it
->second
;
188 if (KnownFileMatches(cur_file
, filename
, in_date
, in_size
)) {
193 for (CKnownFileMap::const_iterator it
= m_knownFileMap
.begin();
194 it
!= m_knownFileMap
.end(); ++it
) {
195 CKnownFile
*cur_file
= it
->second
;
196 if (KnownFileMatches(cur_file
, filename
, in_date
, in_size
)) {
202 return IsOnDuplicates(filename
, in_date
, in_size
);
206 CKnownFile
*CKnownFileList::IsOnDuplicates(
207 const CPath
& filename
,
209 uint64 in_size
) const
211 if (m_duplicateSizeMap
) {
212 std::pair
<KnownFileSizeMap::const_iterator
, KnownFileSizeMap::const_iterator
> p
;
213 p
= m_duplicateSizeMap
->equal_range((uint32
) in_size
);
214 for (KnownFileSizeMap::const_iterator it
= p
.first
; it
!= p
.second
; it
++) {
215 CKnownFile
*cur_file
= it
->second
;
216 if (KnownFileMatches(cur_file
, filename
, in_date
, in_size
)) {
221 for (KnownFileList::const_iterator it
= m_duplicateFileList
.begin();
222 it
!= m_duplicateFileList
.end(); ++it
) {
223 CKnownFile
*cur_file
= *it
;
224 if (KnownFileMatches(cur_file
, filename
, in_date
, in_size
)) {
233 bool CKnownFileList::IsKnownFile(const CKnownFile
*file
)
235 wxCHECK(file
, false);
237 wxMutexLocker
sLock(list_mut
);
239 // For the map, search with the key
240 const CMD4Hash
&key
= file
->GetFileHash();
241 CKnownFileMap::const_iterator itMap
= m_knownFileMap
.find(key
);
242 if (itMap
!= m_knownFileMap
.end()) {
245 // For the list, we have to iterate to search
246 for (KnownFileList::iterator it
= m_duplicateFileList
.begin();
247 it
!= m_duplicateFileList
.end(); ++it
) {
256 CKnownFile
* CKnownFileList::FindKnownFileByID(const CMD4Hash
& hash
)
258 wxMutexLocker
sLock(list_mut
);
260 if (!hash
.IsEmpty()) {
261 if (m_knownFileMap
.find(hash
) != m_knownFileMap
.end()) {
262 return m_knownFileMap
[hash
];
272 bool CKnownFileList::SafeAddKFile(CKnownFile
* toadd
)
276 wxMutexLocker
sLock(list_mut
);
280 theApp
->searchlist
->UpdateSearchFileByHash(toadd
->GetFileHash());
286 bool CKnownFileList::Append(CKnownFile
*Record
)
288 if (Record
->GetFileSize() > 0) {
289 const CMD4Hash
& tkey
= Record
->GetFileHash();
290 CKnownFileMap::iterator it
= m_knownFileMap
.find(tkey
);
291 if (it
== m_knownFileMap
.end()) {
292 m_knownFileMap
[tkey
] = Record
;
296 time_t in_date
= it
->second
->GetLastChangeDatetime();
297 uint64 in_size
= it
->second
->GetFileSize();
298 CPath filename
= it
->second
->GetFileName();
299 if (KnownFileMatches(Record
, filename
, in_date
, in_size
) ||
300 IsOnDuplicates(filename
, in_date
, in_size
)) {
301 // The file is already on the list, ignore it.
304 // The file is a duplicated hash. Add THE OLD ONE to the duplicates list.
305 m_duplicateFileList
.push_back(m_knownFileMap
[tkey
]);
306 // Is this thread-safe? If John is not sure and I'm not sure either...
307 if (theApp
->sharedfiles
) {
308 // Removing the old kad keywords created with the old filename
309 theApp
->sharedfiles
->RemoveKeywords(it
->second
);
311 m_knownFileMap
[tkey
] = Record
;
316 AddDebugLogLineN(logGeneral
,
317 CFormat(wxT("%s is 0-size, not added")) %
318 Record
->GetFileName());
324 // Make an index by size to speed up FindKnownFile
325 // Size modulo 2^32 is enough here
326 void CKnownFileList::PrepareIndex()
329 m_knownSizeMap
= new KnownFileSizeMap
;
330 for (CKnownFileMap::const_iterator it
= m_knownFileMap
.begin(); it
!= m_knownFileMap
.end(); it
++) {
331 m_knownSizeMap
->insert(std::pair
<uint32
, CKnownFile
*>((uint32
) it
->second
->GetFileSize(), it
->second
));
333 m_duplicateSizeMap
= new KnownFileSizeMap
;
334 for (KnownFileList::const_iterator it
= m_duplicateFileList
.begin(); it
!= m_duplicateFileList
.end(); it
++) {
335 m_duplicateSizeMap
->insert(std::pair
<uint32
, CKnownFile
*>((uint32
) (*it
)->GetFileSize(), *it
));
340 void CKnownFileList::ReleaseIndex()
342 delete m_knownSizeMap
;
343 delete m_duplicateSizeMap
;
344 m_knownSizeMap
= NULL
;
345 m_duplicateSizeMap
= NULL
;
348 // File_checked_for_headers