Upstream tarball 10184
[amule.git] / src / KnownFileList.cpp
blob97af05fc345ed0e401565317ec43e0a1bef49e8d
1 //
2 // This file is part of the aMule Project.
3 //
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 )
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.
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.
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
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
33 #include "amule.h"
34 #include "Logger.h"
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,
44 uint32 in_date,
45 uint64 in_size) const
47 return
48 (knownFile->GetLastChangeDatetime() == (time_t)in_date) &&
49 (knownFile->GetFileSize() == in_size) &&
50 (knownFile->GetFileName() == filename);
54 CKnownFileList::CKnownFileList()
56 accepted = 0;
57 requested = 0;
58 transferred = 0;
59 m_filename = wxT("known.met");
60 m_knownSizeMap = NULL;
61 m_duplicateSizeMap = NULL;
62 Init();
66 CKnownFileList::~CKnownFileList()
68 Clear();
72 bool CKnownFileList::Init()
74 CFile file;
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.
80 return false;
83 if (!file.Open(fullpath)) {
84 AddLogLineM(true, CFormat(_("WARNING: %s cannot be opened.")) % m_filename);
85 return false;
88 try {
89 uint8 version = file.ReadUInt8();
90 if ((version != MET_HEADER) && (version != MET_HEADER_WITH_LARGEFILES)) {
91 AddLogLineM(true, _("WARNING: Known file list corrupted, contains invalid header."));
92 return false;
95 wxMutexLocker sLock(list_mut);
96 uint32 RecordsNumber = file.ReadUInt32();
97 AddDebugLogLineM(false, logKnownFiles,
98 wxString::Format(wxT("Reading %i known files from file format 0x%2.2x."),
99 RecordsNumber, version));
100 for (uint32 i = 0; i < RecordsNumber; i++) {
101 CScopedPtr<CKnownFile> record(new CKnownFile());
102 if (record->LoadFromFile(&file)) {
103 AddDebugLogLineM(false, logKnownFiles,
104 CFormat(wxT("Known file read: %s")) % record->GetFileName());
105 Append(record.release());
106 } else {
107 AddLogLineM(true,
108 wxT("Failed to load entry in knownfilelist, file may be corrupt"));
111 AddDebugLogLineM(false, logKnownFiles, wxT("Finished reading known files"));
113 return true;
114 } catch (const CInvalidPacket& e) {
115 AddLogLineM(true, wxT("Invalid entry in knownfilelist, file may be corrupt: ") + e.what());
116 } catch (const CSafeIOException& e) {
117 AddLogLineM(true, CFormat(_("IO error while reading %s file: %s")) % m_filename % e.what());
120 return false;
124 void CKnownFileList::Save()
126 CFile file(theApp->ConfigDir + m_filename, CFile::write);
127 if (!file.IsOpened()) {
128 return;
131 wxMutexLocker sLock(list_mut);
133 try {
134 // Kry - This is the version, but we don't know it till
135 // we know if any largefile is saved. This allows the list
136 // to be compatible with previous versions.
137 bool bContainsAnyLargeFiles = false;
138 file.WriteUInt8(0);
140 file.WriteUInt32(m_knownFileMap.size() + m_duplicateFileList.size());
142 // Duplicates handling. Duplicates needs to be saved first,
143 // since it is the last entry that gets used.
144 KnownFileList::iterator itDup = m_duplicateFileList.begin();
145 for ( ; itDup != m_duplicateFileList.end(); ++itDup ) {
146 (*itDup)->WriteToFile(&file);
147 if ((*itDup)->IsLargeFile()) {
148 bContainsAnyLargeFiles = true;
152 CKnownFileMap::iterator it = m_knownFileMap.begin();
153 for (; it != m_knownFileMap.end(); ++it) {
154 it->second->WriteToFile(&file);
155 if (it->second->IsLargeFile()) {
156 bContainsAnyLargeFiles = true;
160 file.Seek(0);
161 file.WriteUInt8(bContainsAnyLargeFiles ? MET_HEADER_WITH_LARGEFILES : MET_HEADER);
162 } catch (const CIOFailureException& e) {
163 AddLogLineM(true, CFormat(_("Error while saving %s file: %s")) % m_filename % e.what());
168 void CKnownFileList::Clear()
170 wxMutexLocker sLock(list_mut);
172 DeleteContents(m_knownFileMap);
173 DeleteContents(m_duplicateFileList);
174 ReleaseIndex();
178 CKnownFile* CKnownFileList::FindKnownFile(
179 const CPath& filename,
180 time_t in_date,
181 uint64 in_size)
183 wxMutexLocker sLock(list_mut);
185 if (m_knownSizeMap) {
186 std::pair<KnownFileSizeMap::const_iterator, KnownFileSizeMap::const_iterator> p;
187 p = m_knownSizeMap->equal_range((uint32) in_size);
188 for (KnownFileSizeMap::const_iterator it = p.first; it != p.second; it++) {
189 CKnownFile *cur_file = it->second;
190 if (KnownFileMatches(cur_file, filename, in_date, in_size)) {
191 return cur_file;
194 } else {
195 for (CKnownFileMap::const_iterator it = m_knownFileMap.begin();
196 it != m_knownFileMap.end(); ++it) {
197 CKnownFile *cur_file = it->second;
198 if (KnownFileMatches(cur_file, filename, in_date, in_size)) {
199 return cur_file;
204 return IsOnDuplicates(filename, in_date, in_size);
208 CKnownFile *CKnownFileList::IsOnDuplicates(
209 const CPath& filename,
210 uint32 in_date,
211 uint64 in_size) const
213 if (m_duplicateSizeMap) {
214 std::pair<KnownFileSizeMap::const_iterator, KnownFileSizeMap::const_iterator> p;
215 p = m_duplicateSizeMap->equal_range((uint32) in_size);
216 for (KnownFileSizeMap::const_iterator it = p.first; it != p.second; it++) {
217 CKnownFile *cur_file = it->second;
218 if (KnownFileMatches(cur_file, filename, in_date, in_size)) {
219 return cur_file;
222 } else {
223 for (KnownFileList::const_iterator it = m_duplicateFileList.begin();
224 it != m_duplicateFileList.end(); ++it) {
225 CKnownFile *cur_file = *it;
226 if (KnownFileMatches(cur_file, filename, in_date, in_size)) {
227 return cur_file;
231 return NULL;
235 bool CKnownFileList::IsKnownFile(const CKnownFile *file)
237 wxCHECK(file, false);
239 wxMutexLocker sLock(list_mut);
241 // For the map, search with the key
242 const CMD4Hash &key = file->GetFileHash();
243 CKnownFileMap::const_iterator itMap = m_knownFileMap.find(key);
244 if (itMap != m_knownFileMap.end()) {
245 return true;
247 // For the list, we have to iterate to search
248 for (KnownFileList::iterator it = m_duplicateFileList.begin();
249 it != m_duplicateFileList.end(); ++it) {
250 if (*it == file) {
251 return true;
254 return false;
258 CKnownFile* CKnownFileList::FindKnownFileByID(const CMD4Hash& hash)
260 wxMutexLocker sLock(list_mut);
262 if (!hash.IsEmpty()) {
263 if (m_knownFileMap.find(hash) != m_knownFileMap.end()) {
264 return m_knownFileMap[hash];
265 } else {
266 return NULL;
269 return NULL;
274 bool CKnownFileList::SafeAddKFile(CKnownFile* toadd)
276 bool ret;
278 wxMutexLocker sLock(list_mut);
279 ret = Append(toadd);
281 if (ret) {
282 theApp->searchlist->UpdateSearchFileByHash(toadd->GetFileHash());
284 return ret;
288 bool CKnownFileList::Append(CKnownFile *Record)
290 if (Record->GetFileSize() > 0) {
291 const CMD4Hash& tkey = Record->GetFileHash();
292 CKnownFileMap::iterator it = m_knownFileMap.find(tkey);
293 if (it == m_knownFileMap.end()) {
294 m_knownFileMap[tkey] = Record;
295 return true;
296 } else {
297 it->second;
298 time_t in_date = it->second->GetLastChangeDatetime();
299 uint64 in_size = it->second->GetFileSize();
300 CPath filename = it->second->GetFileName();
301 if (KnownFileMatches(Record, filename, in_date, in_size) ||
302 IsOnDuplicates(filename, in_date, in_size)) {
303 // The file is already on the list, ignore it.
304 return false;
305 } else {
306 // The file is a duplicated hash. Add THE OLD ONE to the duplicates list.
307 m_duplicateFileList.push_back(m_knownFileMap[tkey]);
308 // Is this thread-safe? If John is not sure and I'm not sure either...
309 if (theApp->sharedfiles) {
310 // Removing the old kad keywords created with the old filename
311 theApp->sharedfiles->RemoveKeywords(it->second);
313 m_knownFileMap[tkey] = Record;
314 return true;
317 } else {
318 AddDebugLogLineM(false, logGeneral,
319 CFormat(wxT("%s is 0-size, not added")) %
320 Record->GetFileName());
322 return false;
326 // Make an index by size to speed up FindKnownFile
327 // Size modulo 2^32 is enough here
328 void CKnownFileList::PrepareIndex()
330 ReleaseIndex();
331 m_knownSizeMap = new KnownFileSizeMap;
332 for (CKnownFileMap::const_iterator it = m_knownFileMap.begin(); it != m_knownFileMap.end(); it++) {
333 m_knownSizeMap->insert(std::pair<uint32, CKnownFile*>((uint32) it->second->GetFileSize(), it->second));
335 m_duplicateSizeMap = new KnownFileSizeMap;
336 for (KnownFileList::const_iterator it = m_duplicateFileList.begin(); it != m_duplicateFileList.end(); it++) {
337 m_duplicateSizeMap->insert(std::pair<uint32, CKnownFile*>((uint32) (*it)->GetFileSize(), *it));
342 void CKnownFileList::ReleaseIndex()
344 delete m_knownSizeMap;
345 delete m_duplicateSizeMap;
346 m_knownSizeMap = NULL;
347 m_duplicateSizeMap = NULL;
350 // File_checked_for_headers