Fixed compilation with wx 3.0 STL build
[amule.git] / src / SafeFile.cpp
blob064f2fe6a2f28c335163600b0810966281547d74
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2011 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.
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
30 #include "Logger.h"
31 #include <common/Format.h> // Needed for CFormat
32 #include "CompilerSpecific.h" // Needed for __FUNCTION__
35 #define CHECK_BOM(size, x) ((size >= 3) && (x[0] == (char)0xEF) && (x[1] == (char)0xBB) && (x[2] == (char)0xBF))
37 const char BOMHeader[3] = { '\xEF', '\xBB', '\xBF'};
40 CSafeIOException::CSafeIOException(const wxString& type, const wxString& desc)
41 : CMuleException(wxT("SafeIO::") + type, desc) {}
44 CEOFException::CEOFException(const wxString& desc)
45 : CSafeIOException(wxT("EOF"), desc) {}
48 CIOFailureException::CIOFailureException(const wxString& desc)
49 : CSafeIOException(wxT("IOFailure"), desc) {}
51 CIOFailureException::CIOFailureException(const wxString& type, const wxString& desc)
52 : CSafeIOException(wxT("IOFailure::") + type, desc) {}
55 ///////////////////////////////////////////////////////////////////////////////
56 // CFileDataIO
59 CFileDataIO::~CFileDataIO()
64 bool CFileDataIO::Eof() const
66 return GetPosition() >= GetLength();
70 void CFileDataIO::Read(void *buffer, size_t count) const
72 MULE_VALIDATE_PARAMS(buffer, wxT("Attempting to write to NULL buffer."));
74 // Check that we read everything we wanted.
75 if (doRead(buffer, count) == (signed)count) {
76 return;
79 // To reduce potential system calls, we only do EOF checks when reads fail.
80 if (Eof()) {
81 throw CEOFException(wxT("Attempt to read past end of file."));
82 } else {
83 throw CIOFailureException(wxT("Read error, failed to read from file."));
88 void CFileDataIO::Write(const void* buffer, size_t count)
90 MULE_VALIDATE_PARAMS(buffer, wxT("Attempting to read from NULL buffer."));
92 if (doWrite(buffer, count) != (signed)count) {
93 throw CIOFailureException(wxT("Write error, failed to write to file."));
98 uint64 CFileDataIO::Seek(sint64 offset, wxSeekMode from) const
100 sint64 newpos = 0;
101 switch (from) {
102 case wxFromStart:
103 newpos = offset;
104 break;
106 case wxFromCurrent:
107 newpos = GetPosition() + offset;
108 break;
110 case wxFromEnd:
111 newpos = GetLength() + offset;
112 break;
114 default:
115 MULE_VALIDATE_PARAMS(false, wxT("Invalid seek-mode specified."));
118 MULE_VALIDATE_PARAMS(newpos >= 0, wxT("Position after seeking would be less than zero!"));
120 sint64 result = doSeek(newpos);
121 MULE_VALIDATE_STATE(result >= 0, wxT("Seeking resulted in invalid offset."));
122 MULE_VALIDATE_STATE(result == newpos, wxT("Target position and actual position disagree."));
124 return result;
128 uint8 CFileDataIO::ReadUInt8() const
130 uint8 value = 0;
131 Read(&value, sizeof(uint8));
133 return value;
137 uint16 CFileDataIO::ReadUInt16() const
139 uint16 value = 0;
140 Read(&value, sizeof(uint16));
142 return ENDIAN_SWAP_16(value);
146 uint32 CFileDataIO::ReadUInt32() const
148 uint32 value = 0;
149 Read(&value, sizeof(uint32));
151 return ENDIAN_SWAP_32(value);
155 uint64 CFileDataIO::ReadUInt64() const
157 uint64 value = 0;
158 Read(&value, sizeof(uint64));
160 return ENDIAN_SWAP_64(value);
164 // UInt128 values are stored a little weird way...
165 // Four little-endian 32-bit numbers, stored in
166 // big-endian order
167 CUInt128 CFileDataIO::ReadUInt128() const
169 CUInt128 value;
170 for (int i = 0; i < 4; i++) {
171 // Four 32bits chunks
172 value.Set32BitChunk(i, ReadUInt32());
175 return value;
179 CMD4Hash CFileDataIO::ReadHash() const
181 CMD4Hash value;
182 Read(value.GetHash(), MD4HASH_LENGTH);
184 return value;
188 float CFileDataIO::ReadFloat() const
190 float retVal;
191 Read(&retVal, sizeof(float));
192 return retVal;
196 unsigned char* CFileDataIO::ReadBsob(uint8* puSize) const
198 MULE_VALIDATE_PARAMS(puSize, wxT("NULL pointer argument in ReadBsob"));
200 *puSize = ReadUInt8();
202 CScopedArray<unsigned char> bsob(*puSize);
203 Read(bsob.get(), *puSize);
205 return bsob.release();
209 wxString CFileDataIO::ReadString(bool bOptUTF8, uint8 SizeLen, bool SafeRead) const
211 uint32 readLen;
212 switch (SizeLen) {
213 case sizeof(uint16): readLen = ReadUInt16(); break;
214 case sizeof(uint32): readLen = ReadUInt32(); break;
216 default:
217 MULE_VALIDATE_PARAMS(false, wxT("Invalid SizeLen value in ReadString"));
220 if (SafeRead) {
221 readLen = std::min<uint64>(readLen, GetLength() - GetPosition());
224 return ReadOnlyString(bOptUTF8, readLen);
228 wxString CFileDataIO::ReadOnlyString(bool bOptUTF8, uint16 raw_len) const
230 // We only need to set the the NULL terminator, since we know that
231 // reads will either succeed or throw an exception, in which case
232 // we wont be returning anything
233 std::vector<char> val_array(raw_len + 1);
234 val_array[raw_len] = 0;
236 char* val = &(val_array[0]);
238 Read(val, raw_len);
239 wxString str;
241 if (CHECK_BOM(raw_len, val)) {
242 // This is a UTF8 string with a BOM header, skip header.
243 str = UTF82unicode(val + 3);
244 } else if (bOptUTF8) {
245 str = UTF82unicode(val);
246 if (str.IsEmpty()) {
247 // Fallback to Latin-1
248 str = wxString(val, wxConvISO8859_1, raw_len);
250 } else {
251 // Raw strings are written as Latin-1 (see CFileDataIO::WriteStringCore)
252 str = wxString(val, wxConvISO8859_1, raw_len);
255 return str;
259 void CFileDataIO::WriteUInt8(uint8 value)
261 Write(&value, sizeof(uint8));
265 void CFileDataIO::WriteUInt16(uint16 value)
267 ENDIAN_SWAP_I_16(value);
269 Write(&value, sizeof(uint16));
273 void CFileDataIO::WriteUInt32(uint32 value)
275 ENDIAN_SWAP_I_32(value);
277 Write(&value, sizeof(uint32));
281 void CFileDataIO::WriteUInt64(uint64 value)
283 ENDIAN_SWAP_I_64(value);
285 Write(&value, sizeof(uint64));
289 // UInt128 values are stored a little weird way...
290 // Four little-endian 32-bit numbers, stored in
291 // big-endian order
292 void CFileDataIO::WriteUInt128(const Kademlia::CUInt128& value)
294 for (int i = 0; i < 4; i++) {
295 // Four 32bits chunks
296 WriteUInt32(value.Get32BitChunk(i));
301 void CFileDataIO::WriteHash(const CMD4Hash& value)
303 Write(value.GetHash(), MD4HASH_LENGTH);
307 void CFileDataIO::WriteFloat(float value)
309 Write(&value, sizeof(float));
313 void CFileDataIO::WriteBsob(const unsigned char* value, uint8 size)
315 WriteUInt8(size);
316 Write(value, size);
320 void CFileDataIO::WriteString(const wxString& str, EUtf8Str eEncode, uint8 SizeLen)
322 switch (eEncode) {
323 case utf8strRaw:
324 case utf8strOptBOM: {
325 Unicode2CharBuf s(unicode2UTF8(str));
326 if (s) {
327 WriteStringCore(s, eEncode, SizeLen);
328 break;
331 default: {
332 // Non UTF-8 strings are saved as Latin-1
333 wxCharBuffer s1 = str.mb_str(wxConvISO8859_1);
334 WriteStringCore(s1, utf8strNone, SizeLen);
340 void CFileDataIO::WriteStringCore(const char *s, EUtf8Str eEncode, uint8 SizeLen)
342 uint32 sLength = s ? strlen(s) : 0;
343 uint32 real_length = 0;
344 if (eEncode == utf8strOptBOM) {
345 real_length = sLength + 3; // For BOM header.
346 } else {
347 real_length = sLength;
350 switch (SizeLen) {
351 case 0:
352 // Don't write size.
353 break;
355 case sizeof(uint16):
356 // We must not allow too long strings to be written,
357 // as this would allow for a buggy clients to "poison"
358 // us, by sending ISO8859-1 strings that expand to a
359 // greater than 16b length when converted as UTF-8.
360 if (real_length > 0xFFFF) {
361 wxFAIL_MSG(wxT("String is too long to be saved"));
363 real_length = std::min<uint32>(real_length, 0xFFFF);
364 if (eEncode == utf8strOptBOM) {
365 sLength = real_length - 3;
366 } else {
367 sLength = real_length;
371 WriteUInt16(real_length);
372 break;
374 case sizeof(uint32):
375 WriteUInt32(real_length);
376 break;
378 default:
379 MULE_VALIDATE_PARAMS(false, wxT("Invalid length for string-length field."));
382 // The BOM header must be written even if the string is empty.
383 if (eEncode == utf8strOptBOM) {
384 Write(BOMHeader, 3);
387 // Only attempt to write non-NULL strings.
388 if (sLength) {
389 // No NULL terminator is written since we explicitly specify the length
390 Write(s, sLength);
395 CTag *CFileDataIO::ReadTag(bool bOptACP) const
397 CTag *retVal = NULL;
398 wxString name;
399 byte type = 0;
400 try {
401 type = ReadUInt8();
402 name = ReadString(false);
404 switch (type)
406 // NOTE: This tag data type is accepted and stored only to give us the possibility to upgrade
407 // the net in some months.
409 // And still.. it doesnt't work this way without breaking backward compatibility. To properly
410 // do this without messing up the network the following would have to be done:
411 // - those tag types have to be ignored by any client, otherwise those tags would also be sent (and
412 // that's really the problem)
414 // - ignoring means, each client has to read and right throw away those tags, so those tags get
415 // get never stored in any tag list which might be sent by that client to some other client.
417 // - all calling functions have to be changed to deal with the 'nr. of tags' attribute (which was
418 // already parsed) correctly.. just ignoring those tags here is not enough, any taglists have to
419 // be built with the knowledge that the 'nr. of tags' attribute may get decreased during the tag
420 // reading..
422 // If those new tags would just be stored and sent to remote clients, any malicious or just bugged
423 // client could let send a lot of nodes "corrupted" packets...
425 case TAGTYPE_HASH16:
427 retVal = new CTagHash(name, ReadHash());
428 break;
431 case TAGTYPE_STRING:
432 retVal = new CTagString(name, ReadString(bOptACP));
433 break;
435 case TAGTYPE_UINT64:
436 retVal = new CTagInt64(name, ReadUInt64());
437 break;
439 case TAGTYPE_UINT32:
440 retVal = new CTagInt32(name, ReadUInt32());
441 break;
443 case TAGTYPE_UINT16:
444 retVal = new CTagInt16(name, ReadUInt16());
445 break;
447 case TAGTYPE_UINT8:
448 retVal = new CTagInt8(name, ReadUInt8());
449 break;
451 case TAGTYPE_FLOAT32:
452 retVal = new CTagFloat(name, ReadFloat());
453 break;
455 // NOTE: This tag data type is accepted and stored only to give us the possibility to upgrade
456 // the net in some months.
458 // And still.. it doesnt't work this way without breaking backward compatibility
459 case TAGTYPE_BSOB:
461 uint8 size = 0;
462 CScopedArray<unsigned char> value(ReadBsob(&size));
464 retVal = new CTagBsob(name, value.get(), size);
465 break;
468 default:
469 throw wxString(CFormat(wxT("Invalid Kad tag type; type=0x%02x name=%s\n")) % type % name);
471 } catch(const CMuleException& e) {
472 AddLogLineN(e.what());
473 delete retVal;
474 throw;
475 } catch(const wxString& e) {
476 AddLogLineN(e);
477 throw;
480 return retVal;
484 void CFileDataIO::ReadTagPtrList(TagPtrList* taglist, bool bOptACP) const
486 MULE_VALIDATE_PARAMS(taglist, wxT("NULL pointer argument in ReadTagPtrList"));
488 uint32 count = ReadUInt8();
489 for (uint32 i = 0; i < count; i++)
491 CTag* tag = ReadTag(bOptACP);
492 taglist->push_back(tag);
497 void CFileDataIO::WriteTag(const CTag& tag)
501 WriteUInt8(tag.GetType());
503 if (!tag.GetName().IsEmpty()) {
504 WriteString(tag.GetName(),utf8strNone);
505 } else {
506 WriteUInt16(1);
507 WriteUInt8(tag.GetNameID());
510 switch (tag.GetType())
512 case TAGTYPE_HASH16:
513 // Do NOT use this to transfer any tags for at least half a year!!
514 WriteHash(CMD4Hash(tag.GetHash()));
515 break;
516 case TAGTYPE_STRING:
517 WriteString(tag.GetStr(), utf8strRaw); // Always UTF8
518 break;
519 case TAGTYPE_UINT64:
520 WriteUInt64(tag.GetInt());
521 break;
522 case TAGTYPE_UINT32:
523 WriteUInt32(tag.GetInt());
524 break;
525 case TAGTYPE_FLOAT32:
526 WriteFloat(tag.GetFloat());
527 break;
528 case TAGTYPE_BSOB:
529 WriteBsob(tag.GetBsob(), tag.GetBsobSize());
530 break;
531 case TAGTYPE_UINT16:
532 WriteUInt16(tag.GetInt());
533 break;
534 case TAGTYPE_UINT8:
535 WriteUInt8(tag.GetInt());
536 break;
537 case TAGTYPE_BLOB:
538 // NOTE: This will break backward compatibility with met files for eMule versions prior to 0.44a
539 // and any aMule prior to SVN 26/02/2005
540 WriteUInt32(tag.GetBlobSize());
541 Write(tag.GetBlob(), tag.GetBlobSize());
542 break;
543 default:
544 //TODO: Support more tag types
545 // With the if above, this should NEVER happen.
546 AddLogLineNS(CFormat(wxT("CFileDataIO::WriteTag: Unknown tag: type=0x%02X")) % tag.GetType());
547 wxFAIL;
548 break;
550 } catch (...) {
551 AddLogLineNS(wxT("Exception in CDataIO:WriteTag"));
552 throw;
557 void CFileDataIO::WriteTagPtrList(const TagPtrList& tagList)
559 uint32 count = tagList.size();
560 wxASSERT( count <= 0xFF );
562 WriteUInt8(count);
563 TagPtrList::const_iterator it;
564 for (it = tagList.begin(); it != tagList.end(); ++it) {
565 WriteTag(**it);
569 uint64 CFileDataIO::GetIntTagValue() const {
571 uint8 type = ReadUInt8();
573 ReadString(false);
575 switch (type) {
577 case TAGTYPE_UINT64:
578 return ReadUInt64();
579 break;
581 case TAGTYPE_UINT32:
582 return ReadUInt32();
583 break;
585 case TAGTYPE_UINT16:
586 return ReadUInt16();
587 break;
589 case TAGTYPE_UINT8:
590 return ReadUInt8();
591 break;
593 default:
594 throw wxString(wxT("Wrong tag type reading int tag"));
597 // File_checked_for_headers