Upstream tarball 20080901
[amule.git] / src / SafeFile.cpp
blobb4ebdd7b58fb5fefc444f1a1f1a4bcd9f326d13c
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
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
32 #define CHECK_BOM(size, x) ((size >= 3) && (x[0] == (char)0xEF) && (x[1] == (char)0xBB) && (x[2] == (char)0xBF))
34 const char BOMHeader[3] = { '\xEF', '\xBB', '\xBF'};
37 CSafeIOException::CSafeIOException(const wxString& type, const wxString& desc)
38 : CMuleException(wxT("SafeIO::") + type, desc) {}
41 CEOFException::CEOFException(const wxString& desc)
42 : CSafeIOException(wxT("EOF"), desc) {}
45 CIOFailureException::CIOFailureException(const wxString& desc)
46 : CSafeIOException(wxT("IOFailure"), desc) {}
48 CIOFailureException::CIOFailureException(const wxString& type, const wxString& desc)
49 : CSafeIOException(wxT("IOFailure::") + type, desc) {}
52 ///////////////////////////////////////////////////////////////////////////////
53 // CFileDataIO
56 CFileDataIO::~CFileDataIO()
61 bool CFileDataIO::Eof() const
63 return GetPosition() >= GetLength();
67 void CFileDataIO::Read(void *buffer, size_t count) const
69 MULE_VALIDATE_PARAMS(buffer, wxT("Attempting to write to NULL buffer."));
71 // Check that we read everything we wanted.
72 if (doRead(buffer, count) == (signed)count) {
73 return;
76 // To reduce potential system calls, we only do EOF checks when reads fail.
77 if (Eof()) {
78 throw CEOFException(wxT("Attempt to read past end of file."));
79 } else {
80 throw CIOFailureException(wxT("Read error, failed to read from file."));
85 void CFileDataIO::Write(const void* buffer, size_t count)
87 MULE_VALIDATE_PARAMS(buffer, wxT("Attempting to read from NULL buffer."));
89 if (doWrite(buffer, count) != (signed)count) {
90 throw CIOFailureException(wxT("Write error, failed to write to file."));
95 uint64 CFileDataIO::Seek(sint64 offset, wxSeekMode from) const
97 sint64 newpos = 0;
98 switch (from) {
99 case wxFromStart:
100 newpos = offset;
101 break;
103 case wxFromCurrent:
104 newpos = GetPosition() + offset;
105 break;
107 case wxFromEnd:
108 newpos = GetLength() + offset;
109 break;
111 default:
112 MULE_VALIDATE_PARAMS(false, wxT("Invalid seek-mode specified."));
115 MULE_VALIDATE_PARAMS(newpos >= 0, wxT("Position after seeking would be less than zero!"));
117 sint64 result = doSeek(newpos);
118 MULE_VALIDATE_STATE(result >= 0, wxT("Seeking resulted in invalid offset."));
119 MULE_VALIDATE_STATE(result == newpos, wxT("Target position and actual position disagree."));
121 return result;
125 uint8 CFileDataIO::ReadUInt8() const
127 uint8 value = 0;
128 Read(&value, sizeof(uint8));
130 return value;
134 uint16 CFileDataIO::ReadUInt16() const
136 uint16 value = 0;
137 Read(&value, sizeof(uint16));
139 return ENDIAN_SWAP_16(value);
143 uint32 CFileDataIO::ReadUInt32() const
145 uint32 value = 0;
146 Read(&value, sizeof(uint32));
148 return ENDIAN_SWAP_32(value);
152 uint64 CFileDataIO::ReadUInt64() const
154 uint64 value = 0;
155 Read(&value, sizeof(uint64));
157 return ENDIAN_SWAP_64(value);
161 // UInt128 values are stored a little weird way...
162 // Four little-endian 32-bit numbers, stored in
163 // big-endian order
164 CUInt128 CFileDataIO::ReadUInt128() const
166 CUInt128 value;
167 for (int i = 0; i < 4; i++) {
168 // Four 32bits chunks
169 value.Set32BitChunk(i, ReadUInt32());
172 return value;
176 CMD4Hash CFileDataIO::ReadHash() const
178 CMD4Hash value;
179 Read(value.GetHash(), MD4HASH_LENGTH);
181 return value;
185 float CFileDataIO::ReadFloat() const
187 float retVal;
188 Read(&retVal, sizeof(float));
189 return retVal;
193 unsigned char* CFileDataIO::ReadBsob(uint8* puSize) const
195 MULE_VALIDATE_PARAMS(puSize, wxT("NULL pointer argument in ReadBsob"));
197 *puSize = ReadUInt8();
199 CScopedArray<unsigned char> bsob(new unsigned char[*puSize]);
200 Read(bsob.get(), *puSize);
202 return bsob.release();
206 wxString CFileDataIO::ReadString(bool bOptUTF8, uint8 SizeLen, bool SafeRead) const
208 uint32 readLen;
209 switch (SizeLen) {
210 case sizeof(uint16): readLen = ReadUInt16(); break;
211 case sizeof(uint32): readLen = ReadUInt32(); break;
213 default:
214 MULE_VALIDATE_PARAMS(false, wxT("Invalid SizeLen value in ReadString"));
217 if (SafeRead) {
218 readLen = std::min<uint64>(readLen, GetLength() - GetPosition());
221 return ReadOnlyString(bOptUTF8, readLen);
225 wxString CFileDataIO::ReadOnlyString(bool bOptUTF8, uint16 raw_len) const
227 // We only need to set the the NULL terminator, since we know that
228 // reads will either succeed or throw an exception, in which case
229 // we wont be returning anything
230 std::vector<char> val_array(raw_len + 1);
231 val_array[raw_len] = 0;
233 char* val = &(val_array[0]);
235 Read(val, raw_len);
236 wxString str;
238 if (CHECK_BOM(raw_len, val)) {
239 // This is a UTF8 string with a BOM header, skip header.
240 str = UTF82unicode(val + 3);
241 } else if (bOptUTF8) {
242 str = UTF82unicode(val);
243 if (str.IsEmpty()) {
244 // Fallback to Latin-1
245 str = wxString(val, wxConvISO8859_1, raw_len);
247 } else {
248 // Raw strings are written as Latin-1 (see CFileDataIO::WriteStringCore)
249 str = wxString(val, wxConvISO8859_1, raw_len);
252 return str;
256 void CFileDataIO::WriteUInt8(uint8 value)
258 Write(&value, sizeof(uint8));
262 void CFileDataIO::WriteUInt16(uint16 value)
264 ENDIAN_SWAP_I_16(value);
266 Write(&value, sizeof(uint16));
270 void CFileDataIO::WriteUInt32(uint32 value)
272 ENDIAN_SWAP_I_32(value);
274 Write(&value, sizeof(uint32));
278 void CFileDataIO::WriteUInt64(uint64 value)
280 ENDIAN_SWAP_I_64(value);
282 Write(&value, sizeof(uint64));
286 // UInt128 values are stored a little weird way...
287 // Four little-endian 32-bit numbers, stored in
288 // big-endian order
289 void CFileDataIO::WriteUInt128(const Kademlia::CUInt128& value)
291 for (int i = 0; i < 4; i++) {
292 // Four 32bits chunks
293 WriteUInt32(value.Get32BitChunk(i));
298 void CFileDataIO::WriteHash(const CMD4Hash& value)
300 Write(value.GetHash(), MD4HASH_LENGTH);
304 void CFileDataIO::WriteFloat(float value)
306 Write(&value, sizeof(float));
310 void CFileDataIO::WriteBsob(const unsigned char* value, uint8 size)
312 WriteUInt8(size);
313 Write(value, size);
317 void CFileDataIO::WriteString(const wxString& str, EUtf8Str eEncode, uint8 SizeLen)
319 switch (eEncode) {
320 case utf8strRaw:
321 case utf8strOptBOM: {
322 Unicode2CharBuf s(unicode2UTF8(str));
323 if (s) {
324 WriteStringCore(s, eEncode, SizeLen);
325 break;
328 default: {
329 // Non UTF-8 strings are saved as Latin-1
330 wxCharBuffer s1 = wxConvISO8859_1.cWC2MB(str);
331 WriteStringCore(s1, utf8strNone, SizeLen);
337 void CFileDataIO::WriteStringCore(const char *s, EUtf8Str eEncode, uint8 SizeLen)
339 uint32 sLength = s ? strlen(s) : 0;
340 uint32 real_length = 0;
341 if (eEncode == utf8strOptBOM) {
342 real_length = sLength + 3; // For BOM header.
343 } else {
344 real_length = sLength;
347 switch (SizeLen) {
348 case 0:
349 // Don't write size.
350 break;
352 case sizeof(uint16):
353 // We must not allow too long strings to be written,
354 // as this would allow for a buggy clients to "poison"
355 // us, by sending ISO8859-1 strings that expand to a
356 // greater than 16b length when converted as UTF-8.
357 if (real_length > 0xFFFF) {
358 wxFAIL_MSG(wxT("String is too long to be saved"));
360 real_length = std::min<uint32>(real_length, 0xFFFF);
361 if (eEncode == utf8strOptBOM) {
362 sLength = real_length - 3;
363 } else {
364 sLength = real_length;
368 WriteUInt16(real_length);
369 break;
371 case sizeof(uint32):
372 WriteUInt32(real_length);
373 break;
375 default:
376 MULE_VALIDATE_PARAMS(false, wxT("Invalid length for string-length field."));
379 // The BOM header must be written even if the string is empty.
380 if (eEncode == utf8strOptBOM) {
381 Write(BOMHeader, 3);
384 // Only attempt to write non-NULL strings.
385 if (sLength) {
386 // No NULL terminator is written since we explicitly specify the length
387 Write(s, sLength);
392 CTag *CFileDataIO::ReadTag(bool bOptACP) const
394 CTag *retVal = NULL;
395 wxString name;
396 byte type = 0;
397 try {
398 type = ReadUInt8();
399 name = ReadString(false);
401 switch (type)
403 // NOTE: This tag data type is accepted and stored only to give us the possibility to upgrade
404 // the net in some months.
406 // And still.. it doesnt't work this way without breaking backward compatibility. To properly
407 // do this without messing up the network the following would have to be done:
408 // - those tag types have to be ignored by any client, otherwise those tags would also be sent (and
409 // that's really the problem)
411 // - ignoring means, each client has to read and right throw away those tags, so those tags get
412 // get never stored in any tag list which might be sent by that client to some other client.
414 // - all calling functions have to be changed to deal with the 'nr. of tags' attribute (which was
415 // already parsed) correctly.. just ignoring those tags here is not enough, any taglists have to
416 // be built with the knowledge that the 'nr. of tags' attribute may get decreased during the tag
417 // reading..
419 // If those new tags would just be stored and sent to remote clients, any malicious or just bugged
420 // client could let send a lot of nodes "corrupted" packets...
422 case TAGTYPE_HASH16:
424 retVal = new CTagHash(name, ReadHash());
425 break;
428 case TAGTYPE_STRING:
429 retVal = new CTagString(name, ReadString(bOptACP));
430 break;
432 case TAGTYPE_UINT64:
433 retVal = new CTagInt64(name, ReadUInt64());
434 break;
436 case TAGTYPE_UINT32:
437 retVal = new CTagInt32(name, ReadUInt32());
438 break;
440 case TAGTYPE_UINT16:
441 retVal = new CTagInt16(name, ReadUInt16());
442 break;
444 case TAGTYPE_UINT8:
445 retVal = new CTagInt8(name, ReadUInt8());
446 break;
448 case TAGTYPE_FLOAT32:
449 retVal = new CTagFloat(name, ReadFloat());
450 break;
452 // NOTE: This tag data type is accepted and stored only to give us the possibility to upgrade
453 // the net in some months.
455 // And still.. it doesnt't work this way without breaking backward compatibility
456 case TAGTYPE_BSOB:
458 uint8 size = 0;
459 CScopedArray<unsigned char> value(ReadBsob(&size));
461 retVal = new CTagBsob(name, value.get(), size);
462 break;
465 default:
466 throw wxString(wxT("Invalid Kad tag type on packet"));
468 } catch (...) {
469 printf("Invalid Kad tag; type=0x%02x name=%s\n",
470 type, (const char *)unicode2char(name));
471 delete retVal;
472 throw;
475 return retVal;
479 void CFileDataIO::ReadTagPtrList(TagPtrList* taglist, bool bOptACP) const
481 MULE_VALIDATE_PARAMS(taglist, wxT("NULL pointer argument in ReadTagPtrList"));
483 uint32 count = ReadUInt8();
484 for (uint32 i = 0; i < count; i++)
486 CTag* tag = ReadTag(bOptACP);
487 taglist->push_back(tag);
492 void CFileDataIO::WriteTag(const CTag& tag)
496 WriteUInt8(tag.GetType());
498 if (!tag.GetName().IsEmpty()) {
499 WriteString(tag.GetName(),utf8strNone);
500 } else {
501 WriteUInt16(1);
502 WriteUInt8(tag.GetNameID());
505 switch (tag.GetType())
507 case TAGTYPE_HASH16:
508 // Do NOT use this to transfer any tags for at least half a year!!
509 WriteHash(CMD4Hash(tag.GetHash()));
510 break;
511 case TAGTYPE_STRING:
512 WriteString(tag.GetStr(), utf8strRaw); // Always UTF8
513 break;
514 case TAGTYPE_UINT64:
515 WriteUInt64(tag.GetInt());
516 break;
517 case TAGTYPE_UINT32:
518 WriteUInt32(tag.GetInt());
519 break;
520 case TAGTYPE_FLOAT32:
521 WriteFloat(tag.GetFloat());
522 break;
523 case TAGTYPE_BSOB:
524 WriteBsob(tag.GetBsob(), tag.GetBsobSize());
525 break;
526 case TAGTYPE_UINT16:
527 WriteUInt16(tag.GetInt());
528 break;
529 case TAGTYPE_UINT8:
530 WriteUInt8(tag.GetInt());
531 break;
532 case TAGTYPE_BLOB:
533 // NOTE: This will break backward compatibility with met files for eMule versions prior to 0.44a
534 // and any aMule prior to SVN 26/02/2005
535 WriteUInt32(tag.GetBlobSize());
536 Write(tag.GetBlob(), tag.GetBlobSize());
537 break;
538 default:
539 //TODO: Support more tag types
540 // With the if above, this should NEVER happen.
541 printf("%s; Unknown tag: type=0x%02X\n", __FUNCTION__, tag.GetType());
542 wxASSERT(0);
543 break;
545 } catch (...) {
546 //AddDebugLogLine(false, wxT("Exception in CDataIO:WriteTag"));
547 printf("Exception in CDataIO:WriteTag");
548 throw;
553 void CFileDataIO::WriteTagPtrList(const TagPtrList& tagList)
555 uint32 count = tagList.size();
556 wxASSERT( count <= 0xFF );
558 WriteUInt8(count);
559 TagPtrList::const_iterator it;
560 for (it = tagList.begin(); it != tagList.end(); it++) {
561 WriteTag(**it);
565 uint64 CFileDataIO::GetIntTagValue() const {
567 uint8 type = ReadUInt8();
569 ReadString(false);
571 switch (type) {
573 case TAGTYPE_UINT64:
574 return ReadUInt64();
575 break;
577 case TAGTYPE_UINT32:
578 return ReadUInt32();
579 break;
581 case TAGTYPE_UINT16:
582 return ReadUInt16();
583 break;
585 case TAGTYPE_UINT8:
586 return ReadUInt8();
587 break;
589 default:
590 throw wxString(wxT("Wrong tag type reading int tag"));
593 // File_checked_for_headers