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
26 #include "SafeFile.h" // Interface declarations.
27 #include "MD4Hash.h" // Needed for CMD4Hash
28 #include "kademlia/utils/UInt128.h" // Needed for CUInt128
29 #include "ScopedPtr.h" // Needed for CScopedPtr and CScopedArray
31 #include <common/Format.h> // Needed for CFormat
33 #if defined(__SUNPRO_CC)
34 #define __FUNCTION__ __FILE__+__LINE__
37 #define CHECK_BOM(size, x) ((size >= 3) && (x[0] == (char)0xEF) && (x[1] == (char)0xBB) && (x[2] == (char)0xBF))
39 const char BOMHeader
[3] = { '\xEF', '\xBB', '\xBF'};
42 CSafeIOException::CSafeIOException(const wxString
& type
, const wxString
& desc
)
43 : CMuleException(wxT("SafeIO::") + type
, desc
) {}
46 CEOFException::CEOFException(const wxString
& desc
)
47 : CSafeIOException(wxT("EOF"), desc
) {}
50 CIOFailureException::CIOFailureException(const wxString
& desc
)
51 : CSafeIOException(wxT("IOFailure"), desc
) {}
53 CIOFailureException::CIOFailureException(const wxString
& type
, const wxString
& desc
)
54 : CSafeIOException(wxT("IOFailure::") + type
, desc
) {}
57 ///////////////////////////////////////////////////////////////////////////////
61 CFileDataIO::~CFileDataIO()
66 bool CFileDataIO::Eof() const
68 return GetPosition() >= GetLength();
72 void CFileDataIO::Read(void *buffer
, size_t count
) const
74 MULE_VALIDATE_PARAMS(buffer
, wxT("Attempting to write to NULL buffer."));
76 // Check that we read everything we wanted.
77 if (doRead(buffer
, count
) == (signed)count
) {
81 // To reduce potential system calls, we only do EOF checks when reads fail.
83 throw CEOFException(wxT("Attempt to read past end of file."));
85 throw CIOFailureException(wxT("Read error, failed to read from file."));
90 void CFileDataIO::Write(const void* buffer
, size_t count
)
92 MULE_VALIDATE_PARAMS(buffer
, wxT("Attempting to read from NULL buffer."));
94 if (doWrite(buffer
, count
) != (signed)count
) {
95 throw CIOFailureException(wxT("Write error, failed to write to file."));
100 uint64
CFileDataIO::Seek(sint64 offset
, wxSeekMode from
) const
109 newpos
= GetPosition() + offset
;
113 newpos
= GetLength() + offset
;
117 MULE_VALIDATE_PARAMS(false, wxT("Invalid seek-mode specified."));
120 MULE_VALIDATE_PARAMS(newpos
>= 0, wxT("Position after seeking would be less than zero!"));
122 sint64 result
= doSeek(newpos
);
123 MULE_VALIDATE_STATE(result
>= 0, wxT("Seeking resulted in invalid offset."));
124 MULE_VALIDATE_STATE(result
== newpos
, wxT("Target position and actual position disagree."));
130 uint8
CFileDataIO::ReadUInt8() const
133 Read(&value
, sizeof(uint8
));
139 uint16
CFileDataIO::ReadUInt16() const
142 Read(&value
, sizeof(uint16
));
144 return ENDIAN_SWAP_16(value
);
148 uint32
CFileDataIO::ReadUInt32() const
151 Read(&value
, sizeof(uint32
));
153 return ENDIAN_SWAP_32(value
);
157 uint64
CFileDataIO::ReadUInt64() const
160 Read(&value
, sizeof(uint64
));
162 return ENDIAN_SWAP_64(value
);
166 // UInt128 values are stored a little weird way...
167 // Four little-endian 32-bit numbers, stored in
169 CUInt128
CFileDataIO::ReadUInt128() const
172 for (int i
= 0; i
< 4; i
++) {
173 // Four 32bits chunks
174 value
.Set32BitChunk(i
, ReadUInt32());
181 CMD4Hash
CFileDataIO::ReadHash() const
184 Read(value
.GetHash(), MD4HASH_LENGTH
);
190 float CFileDataIO::ReadFloat() const
193 Read(&retVal
, sizeof(float));
198 unsigned char* CFileDataIO::ReadBsob(uint8
* puSize
) const
200 MULE_VALIDATE_PARAMS(puSize
, wxT("NULL pointer argument in ReadBsob"));
202 *puSize
= ReadUInt8();
204 CScopedArray
<unsigned char> bsob(*puSize
);
205 Read(bsob
.get(), *puSize
);
207 return bsob
.release();
211 wxString
CFileDataIO::ReadString(bool bOptUTF8
, uint8 SizeLen
, bool SafeRead
) const
215 case sizeof(uint16
): readLen
= ReadUInt16(); break;
216 case sizeof(uint32
): readLen
= ReadUInt32(); break;
219 MULE_VALIDATE_PARAMS(false, wxT("Invalid SizeLen value in ReadString"));
223 readLen
= std::min
<uint64
>(readLen
, GetLength() - GetPosition());
226 return ReadOnlyString(bOptUTF8
, readLen
);
230 wxString
CFileDataIO::ReadOnlyString(bool bOptUTF8
, uint16 raw_len
) const
232 // We only need to set the the NULL terminator, since we know that
233 // reads will either succeed or throw an exception, in which case
234 // we wont be returning anything
235 std::vector
<char> val_array(raw_len
+ 1);
236 val_array
[raw_len
] = 0;
238 char* val
= &(val_array
[0]);
243 if (CHECK_BOM(raw_len
, val
)) {
244 // This is a UTF8 string with a BOM header, skip header.
245 str
= UTF82unicode(val
+ 3);
246 } else if (bOptUTF8
) {
247 str
= UTF82unicode(val
);
249 // Fallback to Latin-1
250 str
= wxString(val
, wxConvISO8859_1
, raw_len
);
253 // Raw strings are written as Latin-1 (see CFileDataIO::WriteStringCore)
254 str
= wxString(val
, wxConvISO8859_1
, raw_len
);
261 void CFileDataIO::WriteUInt8(uint8 value
)
263 Write(&value
, sizeof(uint8
));
267 void CFileDataIO::WriteUInt16(uint16 value
)
269 ENDIAN_SWAP_I_16(value
);
271 Write(&value
, sizeof(uint16
));
275 void CFileDataIO::WriteUInt32(uint32 value
)
277 ENDIAN_SWAP_I_32(value
);
279 Write(&value
, sizeof(uint32
));
283 void CFileDataIO::WriteUInt64(uint64 value
)
285 ENDIAN_SWAP_I_64(value
);
287 Write(&value
, sizeof(uint64
));
291 // UInt128 values are stored a little weird way...
292 // Four little-endian 32-bit numbers, stored in
294 void CFileDataIO::WriteUInt128(const Kademlia::CUInt128
& value
)
296 for (int i
= 0; i
< 4; i
++) {
297 // Four 32bits chunks
298 WriteUInt32(value
.Get32BitChunk(i
));
303 void CFileDataIO::WriteHash(const CMD4Hash
& value
)
305 Write(value
.GetHash(), MD4HASH_LENGTH
);
309 void CFileDataIO::WriteFloat(float value
)
311 Write(&value
, sizeof(float));
315 void CFileDataIO::WriteBsob(const unsigned char* value
, uint8 size
)
322 void CFileDataIO::WriteString(const wxString
& str
, EUtf8Str eEncode
, uint8 SizeLen
)
326 case utf8strOptBOM
: {
327 Unicode2CharBuf
s(unicode2UTF8(str
));
329 WriteStringCore(s
, eEncode
, SizeLen
);
334 // Non UTF-8 strings are saved as Latin-1
335 wxCharBuffer s1
= wxConvISO8859_1
.cWC2MB(str
);
336 WriteStringCore(s1
, utf8strNone
, SizeLen
);
342 void CFileDataIO::WriteStringCore(const char *s
, EUtf8Str eEncode
, uint8 SizeLen
)
344 uint32 sLength
= s
? strlen(s
) : 0;
345 uint32 real_length
= 0;
346 if (eEncode
== utf8strOptBOM
) {
347 real_length
= sLength
+ 3; // For BOM header.
349 real_length
= sLength
;
358 // We must not allow too long strings to be written,
359 // as this would allow for a buggy clients to "poison"
360 // us, by sending ISO8859-1 strings that expand to a
361 // greater than 16b length when converted as UTF-8.
362 if (real_length
> 0xFFFF) {
363 wxFAIL_MSG(wxT("String is too long to be saved"));
365 real_length
= std::min
<uint32
>(real_length
, 0xFFFF);
366 if (eEncode
== utf8strOptBOM
) {
367 sLength
= real_length
- 3;
369 sLength
= real_length
;
373 WriteUInt16(real_length
);
377 WriteUInt32(real_length
);
381 MULE_VALIDATE_PARAMS(false, wxT("Invalid length for string-length field."));
384 // The BOM header must be written even if the string is empty.
385 if (eEncode
== utf8strOptBOM
) {
389 // Only attempt to write non-NULL strings.
391 // No NULL terminator is written since we explicitly specify the length
397 CTag
*CFileDataIO::ReadTag(bool bOptACP
) const
404 name
= ReadString(false);
408 // NOTE: This tag data type is accepted and stored only to give us the possibility to upgrade
409 // the net in some months.
411 // And still.. it doesnt't work this way without breaking backward compatibility. To properly
412 // do this without messing up the network the following would have to be done:
413 // - those tag types have to be ignored by any client, otherwise those tags would also be sent (and
414 // that's really the problem)
416 // - ignoring means, each client has to read and right throw away those tags, so those tags get
417 // get never stored in any tag list which might be sent by that client to some other client.
419 // - all calling functions have to be changed to deal with the 'nr. of tags' attribute (which was
420 // already parsed) correctly.. just ignoring those tags here is not enough, any taglists have to
421 // be built with the knowledge that the 'nr. of tags' attribute may get decreased during the tag
424 // If those new tags would just be stored and sent to remote clients, any malicious or just bugged
425 // client could let send a lot of nodes "corrupted" packets...
429 retVal
= new CTagHash(name
, ReadHash());
434 retVal
= new CTagString(name
, ReadString(bOptACP
));
438 retVal
= new CTagInt64(name
, ReadUInt64());
442 retVal
= new CTagInt32(name
, ReadUInt32());
446 retVal
= new CTagInt16(name
, ReadUInt16());
450 retVal
= new CTagInt8(name
, ReadUInt8());
453 case TAGTYPE_FLOAT32
:
454 retVal
= new CTagFloat(name
, ReadFloat());
457 // NOTE: This tag data type is accepted and stored only to give us the possibility to upgrade
458 // the net in some months.
460 // And still.. it doesnt't work this way without breaking backward compatibility
464 CScopedArray
<unsigned char> value(ReadBsob(&size
));
466 retVal
= new CTagBsob(name
, value
.get(), size
);
471 throw wxString(CFormat(wxT("Invalid Kad tag type; type=0x%02x name=%s\n")) % type
% name
);
473 } catch(const CMuleException
& e
) {
474 AddLogLineN(e
.what());
477 } catch(const wxString
& e
) {
486 void CFileDataIO::ReadTagPtrList(TagPtrList
* taglist
, bool bOptACP
) const
488 MULE_VALIDATE_PARAMS(taglist
, wxT("NULL pointer argument in ReadTagPtrList"));
490 uint32 count
= ReadUInt8();
491 for (uint32 i
= 0; i
< count
; i
++)
493 CTag
* tag
= ReadTag(bOptACP
);
494 taglist
->push_back(tag
);
499 void CFileDataIO::WriteTag(const CTag
& tag
)
503 WriteUInt8(tag
.GetType());
505 if (!tag
.GetName().IsEmpty()) {
506 WriteString(tag
.GetName(),utf8strNone
);
509 WriteUInt8(tag
.GetNameID());
512 switch (tag
.GetType())
515 // Do NOT use this to transfer any tags for at least half a year!!
516 WriteHash(CMD4Hash(tag
.GetHash()));
519 WriteString(tag
.GetStr(), utf8strRaw
); // Always UTF8
522 WriteUInt64(tag
.GetInt());
525 WriteUInt32(tag
.GetInt());
527 case TAGTYPE_FLOAT32
:
528 WriteFloat(tag
.GetFloat());
531 WriteBsob(tag
.GetBsob(), tag
.GetBsobSize());
534 WriteUInt16(tag
.GetInt());
537 WriteUInt8(tag
.GetInt());
540 // NOTE: This will break backward compatibility with met files for eMule versions prior to 0.44a
541 // and any aMule prior to SVN 26/02/2005
542 WriteUInt32(tag
.GetBlobSize());
543 Write(tag
.GetBlob(), tag
.GetBlobSize());
546 //TODO: Support more tag types
547 // With the if above, this should NEVER happen.
548 AddLogLineNS(CFormat(wxT("CFileDataIO::WriteTag: Unknown tag: type=0x%02X")) % tag
.GetType());
553 AddLogLineNS(wxT("Exception in CDataIO:WriteTag"));
559 void CFileDataIO::WriteTagPtrList(const TagPtrList
& tagList
)
561 uint32 count
= tagList
.size();
562 wxASSERT( count
<= 0xFF );
565 TagPtrList::const_iterator it
;
566 for (it
= tagList
.begin(); it
!= tagList
.end(); it
++) {
571 uint64
CFileDataIO::GetIntTagValue() const {
573 uint8 type
= ReadUInt8();
596 throw wxString(wxT("Wrong tag type reading int tag"));
599 // File_checked_for_headers