Upstream tarball 20080304
[amule.git] / src / SafeFile.cpp
blob83b020470aabcc8e179861cb49f9eeeb4bc99841
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 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
33 #define CHECK_BOM(size, x) ((size >= 3) && (x[0] == (char)0xEF) && (x[1] == (char)0xBB) && (x[2] == (char)0xBF))
35 const char BOMHeader[3] = {0xEF, 0xBB, 0xBF};
38 CSafeIOException::CSafeIOException(const wxString& type, const wxString& desc)
39 : CMuleException(wxT("SafeIO::") + type, desc) {}
42 CEOFException::CEOFException(const wxString& desc)
43 : CSafeIOException(wxT("EOF"), desc) {}
46 CIOFailureException::CIOFailureException(const wxString& desc)
47 : CSafeIOException(wxT("IOFailure"), desc) {}
49 CIOFailureException::CIOFailureException(const wxString& type, const wxString& desc)
50 : CSafeIOException(wxT("IOFailure::") + type, desc) {}
53 ///////////////////////////////////////////////////////////////////////////////
54 // CFileDataIO
57 CFileDataIO::~CFileDataIO()
62 bool CFileDataIO::Eof() const
64 return GetPosition() >= GetLength();
68 void CFileDataIO::Read(void *buffer, size_t count) const
70 MULE_VALIDATE_PARAMS(buffer, wxT("Attempting to write to NULL buffer."));
72 // Check that we read everything we wanted.
73 if (doRead(buffer, count) == (signed)count) {
74 return;
77 // To reduce potential system calls, we only do EOF checks when reads fail.
78 if (Eof()) {
79 throw CEOFException(wxT("Attempt to read past end of file."));
80 } else {
81 throw CIOFailureException(wxT("Read error, failed to read from file."));
86 void CFileDataIO::Write(const void* buffer, size_t count)
88 MULE_VALIDATE_PARAMS(buffer, wxT("Attempting to read from NULL buffer."));
90 if (doWrite(buffer, count) != (signed)count) {
91 throw CIOFailureException(wxT("Write error, failed to write to file."));
96 uint64 CFileDataIO::Seek(sint64 offset, wxSeekMode from) const
98 sint64 newpos = 0;
99 switch (from) {
100 case wxFromStart:
101 newpos = offset;
102 break;
104 case wxFromCurrent:
105 newpos = GetPosition() + offset;
106 break;
108 case wxFromEnd:
109 newpos = GetLength() + offset;
110 break;
112 default:
113 MULE_VALIDATE_PARAMS(false, wxT("Invalid seek-mode specified."));
116 MULE_VALIDATE_PARAMS(newpos >= 0, wxT("Position after seeking would be less than zero!"));
118 sint64 result = doSeek(newpos);
119 MULE_VALIDATE_STATE(result >= 0, wxT("Seeking resulted in invalid offset."));
120 MULE_VALIDATE_STATE(result == newpos, wxT("Target position and actual position disagree."));
122 return result;
126 uint8 CFileDataIO::ReadUInt8() const
128 uint8 value = 0;
129 Read(&value, sizeof(uint8));
131 return value;
135 uint16 CFileDataIO::ReadUInt16() const
137 uint16 value = 0;
138 Read(&value, sizeof(uint16));
140 return ENDIAN_SWAP_16(value);
144 uint32 CFileDataIO::ReadUInt32() const
146 uint32 value = 0;
147 Read(&value, sizeof(uint32));
149 return ENDIAN_SWAP_32(value);
153 uint64 CFileDataIO::ReadUInt64() const
155 uint64 value = 0;
156 Read(&value, sizeof(uint64));
158 return ENDIAN_SWAP_64(value);
162 CUInt128 CFileDataIO::ReadUInt128() const
164 CUInt128 value;
165 uint32* data = (uint32*)value.GetDataPtr();
166 for (int i = 0; i < (128/32); i++) {
167 // Four 32bits chunks
168 data[i] = ReadUInt32();
171 return value;
175 CMD4Hash CFileDataIO::ReadHash() const
177 CMD4Hash value;
178 Read(value.GetHash(), MD4HASH_LENGTH);
180 return value;
184 float CFileDataIO::ReadFloat() const
186 float retVal;
187 Read(&retVal, sizeof(float));
188 return retVal;
192 unsigned char* CFileDataIO::ReadBsob(uint8* puSize)
194 MULE_VALIDATE_PARAMS(puSize, wxT("NULL pointer argument in ReadBsob"));
196 *puSize = ReadUInt8();
198 CScopedArray<unsigned char> bsob(new unsigned char[*puSize]);
199 Read(bsob.get(), *puSize);
201 return bsob.release();
205 wxString CFileDataIO::ReadString(bool bOptUTF8, uint8 SizeLen, bool SafeRead) const
207 uint32 readLen;
208 switch (SizeLen) {
209 case sizeof(uint16): readLen = ReadUInt16(); break;
210 case sizeof(uint32): readLen = ReadUInt32(); break;
212 default:
213 MULE_VALIDATE_PARAMS(false, wxT("Invalid SizeLen value in ReadString"));
216 if (SafeRead) {
217 readLen = std::min<uint64>(readLen, GetLength() - GetPosition());
220 return ReadOnlyString(bOptUTF8, readLen);
224 wxString CFileDataIO::ReadOnlyString(bool bOptUTF8, uint16 raw_len) const
226 // We only need to set the the NULL terminator, since we know that
227 // reads will either succeed or throw an exception, in which case
228 // we wont be returning anything
229 std::vector<char> val_array(raw_len + 1);
230 val_array[raw_len] = 0;
232 char* val = &(val_array[0]);
234 Read(val, raw_len);
235 wxString str;
237 if (CHECK_BOM(raw_len, val)) {
238 // This is a UTF8 string with a BOM header, skip header.
239 str = UTF82unicode(val + 3);
240 } else if (bOptUTF8) {
241 str = UTF82unicode(val);
242 if (str.IsEmpty()) {
243 // Fallback to Latin-1
244 str = wxString(val, wxConvISO8859_1, raw_len);
246 } else {
247 // Raw strings are written as Latin-1 (see CFileDataIO::WriteStringCore)
248 str = wxString(val, wxConvISO8859_1, raw_len);
251 return str;
255 void CFileDataIO::WriteUInt8(uint8 value)
257 Write(&value, sizeof(uint8));
261 void CFileDataIO::WriteUInt16(uint16 value)
263 ENDIAN_SWAP_I_16(value);
265 Write(&value, sizeof(uint16));
269 void CFileDataIO::WriteUInt32(uint32 value)
271 ENDIAN_SWAP_I_32(value);
273 Write(&value, sizeof(uint32));
277 void CFileDataIO::WriteUInt64(uint64 value)
279 ENDIAN_SWAP_I_64(value);
281 Write(&value, sizeof(uint64));
285 void CFileDataIO::WriteUInt128(const Kademlia::CUInt128& value)
287 for (int i = 0; i < (128/32); i++) {
288 // Four 32bits chunks
289 WriteUInt32(value.Get32BitChunk(i));
294 void CFileDataIO::WriteHash(const CMD4Hash& value)
296 Write(value.GetHash(), MD4HASH_LENGTH);
300 void CFileDataIO::WriteFloat(float value)
302 Write(&value, sizeof(float));
306 void CFileDataIO::WriteBsob(const unsigned char* value, uint8 size)
308 WriteUInt8(size);
309 Write(value, size);
313 void CFileDataIO::WriteString(const wxString& str, EUtf8Str eEncode, uint8 SizeLen)
315 switch (eEncode) {
316 case utf8strRaw:
317 case utf8strOptBOM: {
318 Unicode2CharBuf s(unicode2UTF8(str));
319 if (s) {
320 WriteStringCore(s, eEncode, SizeLen);
321 break;
324 default: {
325 // Non UTF-8 strings are saved as Latin-1
326 wxCharBuffer s1 = wxConvISO8859_1.cWC2MB(str);
327 WriteStringCore(s1, utf8strNone, SizeLen);
333 void CFileDataIO::WriteStringCore(const char *s, EUtf8Str eEncode, uint8 SizeLen)
335 uint32 sLength = s ? strlen(s) : 0;
336 uint32 real_length = 0;
337 if (eEncode == utf8strOptBOM) {
338 real_length = sLength + 3; // For BOM header.
339 } else {
340 real_length = sLength;
343 switch (SizeLen) {
344 case 0:
345 // Don't write size.
346 break;
348 case sizeof(uint16):
349 // We must not allow too long strings to be written,
350 // as this would allow for a buggy clients to "poison"
351 // us, by sending ISO8859-1 strings that expand to a
352 // greater than 16b length when converted as UTF-8.
353 if (real_length > 0xFFFF) {
354 wxFAIL_MSG(wxT("String is too long to be saved"));
356 real_length = std::min<uint32>(real_length, 0xFFFF);
357 if (eEncode == utf8strOptBOM) {
358 sLength = real_length - 3;
359 } else {
360 sLength = real_length;
364 WriteUInt16(real_length);
365 break;
367 case sizeof(uint32):
368 WriteUInt32(real_length);
369 break;
371 default:
372 MULE_VALIDATE_PARAMS(false, wxT("Invalid length for string-length field."));
375 // The BOM header must be written even if the string is empty.
376 if (eEncode == utf8strOptBOM) {
377 Write(BOMHeader, 3);
380 // Only attempt to write non-NULL strings.
381 if (sLength) {
382 // No NULL terminator is written since we explicitly specify the length
383 Write(s, sLength);
388 CTag *CFileDataIO::ReadTag(bool bOptACP)
390 CTag *retVal = NULL;
391 wxString name;
392 byte type = 0;
393 try {
394 type = ReadUInt8();
395 name = ReadString(false);
397 switch (type)
399 // NOTE: This tag data type is accepted and stored only to give us the possibility to upgrade
400 // the net in some months.
402 // And still.. it doesnt't work this way without breaking backward compatibility. To properly
403 // do this without messing up the network the following would have to be done:
404 // - those tag types have to be ignored by any client, otherwise those tags would also be sent (and
405 // that's really the problem)
407 // - ignoring means, each client has to read and right throw away those tags, so those tags get
408 // get never stored in any tag list which might be sent by that client to some other client.
410 // - all calling functions have to be changed to deal with the 'nr. of tags' attribute (which was
411 // already parsed) correctly.. just ignoring those tags here is not enough, any taglists have to
412 // be built with the knowledge that the 'nr. of tags' attribute may get decreased during the tag
413 // reading..
415 // If those new tags would just be stored and sent to remote clients, any malicious or just bugged
416 // client could let send a lot of nodes "corrupted" packets...
418 case TAGTYPE_HASH16:
420 retVal = new CTagHash(name, ReadHash());
421 break;
424 case TAGTYPE_STRING:
425 retVal = new CTagString(name, ReadString(bOptACP));
426 break;
428 case TAGTYPE_UINT64:
429 retVal = new CTagInt64(name, ReadUInt64());
430 break;
432 case TAGTYPE_UINT32:
433 retVal = new CTagInt32(name, ReadUInt32());
434 break;
436 case TAGTYPE_UINT16:
437 retVal = new CTagInt16(name, ReadUInt16());
438 break;
440 case TAGTYPE_UINT8:
441 retVal = new CTagInt8(name, ReadUInt8());
442 break;
444 case TAGTYPE_FLOAT32:
445 retVal = new CTagFloat(name, ReadFloat());
446 break;
448 // NOTE: This tag data type is accepted and stored only to give us the possibility to upgrade
449 // the net in some months.
451 // And still.. it doesnt't work this way without breaking backward compatibility
452 case TAGTYPE_BSOB:
454 uint8 size = 0;
455 CScopedArray<unsigned char> value(ReadBsob(&size));
457 retVal = new CTagBsob(name, value.get(), size);
458 break;
461 default:
462 throw wxString(wxT("Invalid Kad tag type on packet"));
464 } catch (...) {
465 printf("Invalid Kad tag; type=0x%02x name=%s\n",
466 type, (const char *)unicode2char(name));
467 delete retVal;
468 throw;
471 return retVal;
475 void CFileDataIO::ReadTagPtrList(TagPtrList* taglist, bool bOptACP)
477 MULE_VALIDATE_PARAMS(taglist, wxT("NULL pointer argument in ReadTagPtrList"));
479 uint32 count = ReadUInt8();
480 for (uint32 i = 0; i < count; i++)
482 CTag* tag = ReadTag(bOptACP);
483 taglist->push_back(tag);
488 void CFileDataIO::WriteTag(const CTag& tag)
492 WriteUInt8(tag.GetType());
494 if (!tag.GetName().IsEmpty()) {
495 WriteString(tag.GetName(),utf8strNone);
496 } else {
497 WriteUInt16(1);
498 WriteUInt8(tag.GetNameID());
501 switch (tag.GetType())
503 case TAGTYPE_HASH16:
504 // Do NOT use this to transfer any tags for at least half a year!!
505 WriteHash(CMD4Hash(tag.GetHash()));
506 break;
507 case TAGTYPE_STRING:
508 WriteString(tag.GetStr(), utf8strRaw); // Always UTF8
509 break;
510 case TAGTYPE_UINT64:
511 WriteUInt64(tag.GetInt());
512 break;
513 case TAGTYPE_UINT32:
514 WriteUInt32(tag.GetInt());
515 break;
516 case TAGTYPE_FLOAT32:
517 WriteFloat(tag.GetFloat());
518 break;
519 case TAGTYPE_BSOB:
520 // Used for uint128 on Kad now
521 WriteBsob(tag.GetBsob(), tag.GetBsobSize());
522 break;
523 case TAGTYPE_UINT16:
524 WriteUInt16(tag.GetInt());
525 break;
526 case TAGTYPE_UINT8:
527 WriteUInt8(tag.GetInt());
528 break;
529 case TAGTYPE_BLOB:
530 // NOTE: This will break backward compatibility with met files for eMule versions prior to 0.44a
531 // and any aMule prior to SVN 26/02/2005
532 WriteUInt32(tag.GetBlobSize());
533 Write(tag.GetBlob(), tag.GetBlobSize());
534 break;
535 default:
536 //TODO: Support more tag types
537 // With the if above, this should NEVER happen.
538 printf("%s; Unknown tag: type=0x%02X\n", __FUNCTION__, tag.GetType());
539 wxASSERT(0);
540 break;
542 } catch (...) {
543 //AddDebugLogLine(false, wxT("Exception in CDataIO:WriteTag"));
544 printf("Exception in CDataIO:WriteTag");
545 throw;
550 void CFileDataIO::WriteTagPtrList(const TagPtrList& tagList)
552 uint32 count = tagList.size();
553 wxASSERT( count <= 0xFF );
555 WriteUInt8(count);
556 TagPtrList::const_iterator it;
557 for (it = tagList.begin(); it != tagList.end(); it++) {
558 WriteTag(**it);
562 uint64 CFileDataIO::GetIntTagValue() const {
564 uint8 type = ReadUInt8();
566 ReadString(false);
568 switch (type) {
570 case TAGTYPE_UINT64:
571 return ReadUInt64();
572 break;
574 case TAGTYPE_UINT32:
575 return ReadUInt32();
576 break;
578 case TAGTYPE_UINT16:
579 return ReadUInt16();
580 break;
582 case TAGTYPE_UINT8:
583 return ReadUInt8();
584 break;
586 default:
587 throw wxString(wxT("Wrong tag type reading int tag"));
590 // File_checked_for_headers