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.
10 //
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.
15 //
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
24 //
25 
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__
33 
34 
35 #define CHECK_BOM(size, x) ((size >= 3)  && (x[0] == (char)0xEF) && (x[1] == (char)0xBB) && (x[2] == (char)0xBF))
36 
37 const char BOMHeader[3] = { '\xEF', '\xBB', '\xBF'};
38 
39 
CSafeIOException(const wxString & type,const wxString & desc)40 CSafeIOException::CSafeIOException(const wxString& type, const wxString& desc)
41 	: CMuleException(wxT("SafeIO::") + type, desc) {}
42 
43 
CEOFException(const wxString & desc)44 CEOFException::CEOFException(const wxString& desc)
45 	: CSafeIOException(wxT("EOF"), desc) {}
46 
47 
CIOFailureException(const wxString & desc)48 CIOFailureException::CIOFailureException(const wxString& desc)
49 	: CSafeIOException(wxT("IOFailure"), desc) {}
50 
CIOFailureException(const wxString & type,const wxString & desc)51 CIOFailureException::CIOFailureException(const wxString& type, const wxString& desc)
52 	: CSafeIOException(wxT("IOFailure::") + type, desc) {}
53 
54 
55 ///////////////////////////////////////////////////////////////////////////////
56 // CFileDataIO
57 
58 
~CFileDataIO()59 CFileDataIO::~CFileDataIO()
60 {
61 }
62 
63 
Eof() const64 bool CFileDataIO::Eof() const
65 {
66 	return GetPosition() >= GetLength();
67 }
68 
69 
Read(void * buffer,size_t count) const70 void CFileDataIO::Read(void *buffer, size_t count) const
71 {
72 	MULE_VALIDATE_PARAMS(buffer, wxT("Attempting to write to NULL buffer."));
73 
74 	// Check that we read everything we wanted.
75 	if (doRead(buffer, count) == (signed)count) {
76 		return;
77 	}
78 
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."));
84 	}
85 }
86 
87 
Write(const void * buffer,size_t count)88 void CFileDataIO::Write(const void* buffer, size_t count)
89 {
90 	MULE_VALIDATE_PARAMS(buffer, wxT("Attempting to read from NULL buffer."));
91 
92 	if (doWrite(buffer, count) != (signed)count) {
93 		throw CIOFailureException(wxT("Write error, failed to write to file."));
94 	}
95 }
96 
97 
Seek(sint64 offset,wxSeekMode from) const98 uint64 CFileDataIO::Seek(sint64 offset, wxSeekMode from) const
99 {
100 	sint64 newpos = 0;
101 	switch (from) {
102 		case wxFromStart:
103 			newpos = offset;
104 			break;
105 
106 		case wxFromCurrent:
107 			newpos = GetPosition() + offset;
108 			break;
109 
110 		case wxFromEnd:
111 			newpos = GetLength() + offset;
112 			break;
113 
114 		default:
115 			MULE_VALIDATE_PARAMS(false, wxT("Invalid seek-mode specified."));
116 	}
117 
118 	MULE_VALIDATE_PARAMS(newpos >= 0, wxT("Position after seeking would be less than zero!"));
119 
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."));
123 
124 	return result;
125 }
126 
127 
ReadUInt8() const128 uint8 CFileDataIO::ReadUInt8() const
129 {
130 	uint8 value = 0;
131 	Read(&value, sizeof(uint8));
132 
133 	return value;
134 }
135 
136 
ReadUInt16() const137 uint16 CFileDataIO::ReadUInt16() const
138 {
139 	uint16 value = 0;
140 	Read(&value, sizeof(uint16));
141 
142 	return ENDIAN_SWAP_16(value);
143 }
144 
145 
ReadUInt32() const146 uint32 CFileDataIO::ReadUInt32() const
147 {
148 	uint32 value = 0;
149 	Read(&value, sizeof(uint32));
150 
151 	return ENDIAN_SWAP_32(value);
152 }
153 
154 
ReadUInt64() const155 uint64 CFileDataIO::ReadUInt64() const
156 {
157 	uint64 value = 0;
158 	Read(&value, sizeof(uint64));
159 
160 	return ENDIAN_SWAP_64(value);
161 }
162 
163 
164 // UInt128 values are stored a little weird way...
165 // Four little-endian 32-bit numbers, stored in
166 // big-endian order
ReadUInt128() const167 CUInt128 CFileDataIO::ReadUInt128() const
168 {
169 	CUInt128 value;
170 	for (int i = 0; i < 4; i++) {
171 		// Four 32bits chunks
172 		value.Set32BitChunk(i, ReadUInt32());
173 	}
174 
175 	return value;
176 }
177 
178 
ReadHash() const179 CMD4Hash CFileDataIO::ReadHash() const
180 {
181 	CMD4Hash value;
182 	Read(value.GetHash(), MD4HASH_LENGTH);
183 
184 	return value;
185 }
186 
187 
ReadFloat() const188 float CFileDataIO::ReadFloat() const
189 {
190 	float retVal;
191 	Read(&retVal, sizeof(float));
192 	return retVal;
193 }
194 
195 
ReadBsob(uint8 * puSize) const196 unsigned char* CFileDataIO::ReadBsob(uint8* puSize) const
197 {
198 	MULE_VALIDATE_PARAMS(puSize, wxT("NULL pointer argument in ReadBsob"));
199 
200 	*puSize = ReadUInt8();
201 
202 	CScopedArray<unsigned char> bsob(*puSize);
203 	Read(bsob.get(), *puSize);
204 
205 	return bsob.release();
206 }
207 
208 
ReadString(bool bOptUTF8,uint8 SizeLen,bool SafeRead) const209 wxString CFileDataIO::ReadString(bool bOptUTF8, uint8 SizeLen, bool SafeRead) const
210 {
211 	uint32 readLen;
212 	switch (SizeLen) {
213 		case sizeof(uint16):	readLen = ReadUInt16();	break;
214 		case sizeof(uint32):	readLen = ReadUInt32();	break;
215 
216 		default:
217 			MULE_VALIDATE_PARAMS(false, wxT("Invalid SizeLen value in ReadString"));
218 	}
219 
220 	if (SafeRead) {
221 		readLen = std::min<uint64>(readLen, GetLength() - GetPosition());
222 	}
223 
224 	return ReadOnlyString(bOptUTF8, readLen);
225 }
226 
227 
ReadOnlyString(bool bOptUTF8,uint16 raw_len) const228 wxString CFileDataIO::ReadOnlyString(bool bOptUTF8, uint16 raw_len) const
229 {
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;
235 
236 	char* val = &(val_array[0]);
237 
238 	Read(val, raw_len);
239 	wxString str;
240 
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);
249 		}
250 	} else {
251 		// Raw strings are written as Latin-1 (see CFileDataIO::WriteStringCore)
252 		str = wxString(val, wxConvISO8859_1, raw_len);
253 	}
254 
255 	return str;
256 }
257 
258 
WriteUInt8(uint8 value)259 void CFileDataIO::WriteUInt8(uint8 value)
260 {
261 	Write(&value, sizeof(uint8));
262 }
263 
264 
WriteUInt16(uint16 value)265 void CFileDataIO::WriteUInt16(uint16 value)
266 {
267 	ENDIAN_SWAP_I_16(value);
268 
269 	Write(&value, sizeof(uint16));
270 }
271 
272 
WriteUInt32(uint32 value)273 void CFileDataIO::WriteUInt32(uint32 value)
274 {
275 	ENDIAN_SWAP_I_32(value);
276 
277 	Write(&value, sizeof(uint32));
278 }
279 
280 
WriteUInt64(uint64 value)281 void CFileDataIO::WriteUInt64(uint64 value)
282 {
283 	ENDIAN_SWAP_I_64(value);
284 
285 	Write(&value, sizeof(uint64));
286 }
287 
288 
289 // UInt128 values are stored a little weird way...
290 // Four little-endian 32-bit numbers, stored in
291 // big-endian order
WriteUInt128(const Kademlia::CUInt128 & value)292 void CFileDataIO::WriteUInt128(const Kademlia::CUInt128& value)
293 {
294 	for (int i = 0; i < 4; i++) {
295 		// Four 32bits chunks
296 		WriteUInt32(value.Get32BitChunk(i));
297 	}
298 }
299 
300 
WriteHash(const CMD4Hash & value)301 void CFileDataIO::WriteHash(const CMD4Hash& value)
302 {
303 	Write(value.GetHash(), MD4HASH_LENGTH);
304 }
305 
306 
WriteFloat(float value)307 void CFileDataIO::WriteFloat(float value)
308 {
309 	Write(&value, sizeof(float));
310 }
311 
312 
WriteBsob(const unsigned char * value,uint8 size)313 void CFileDataIO::WriteBsob(const unsigned char* value, uint8 size)
314 {
315 	WriteUInt8(size);
316 	Write(value, size);
317 }
318 
319 
WriteString(const wxString & str,EUtf8Str eEncode,uint8 SizeLen)320 void CFileDataIO::WriteString(const wxString& str, EUtf8Str eEncode, uint8 SizeLen)
321 {
322 	switch (eEncode) {
323 		case utf8strRaw:
324 		case utf8strOptBOM: {
325 			Unicode2CharBuf s(unicode2UTF8(str));
326 			if (s.data()) {
327 				WriteStringCore(s, eEncode, SizeLen);
328 				break;
329 			}
330 		}
331 		/* fall through */
332 		default: {
333 			// Non UTF-8 strings are saved as Latin-1
334 			wxCharBuffer s1 = str.mb_str(wxConvISO8859_1);
335 			WriteStringCore(s1, utf8strNone, SizeLen);
336 		}
337 	}
338 }
339 
340 
WriteStringCore(const char * s,EUtf8Str eEncode,uint8 SizeLen)341 void CFileDataIO::WriteStringCore(const char *s, EUtf8Str eEncode, uint8 SizeLen)
342 {
343 	uint32 sLength = s ? strlen(s) : 0;
344 	uint32 real_length = 0;
345 	if (eEncode == utf8strOptBOM) {
346 		real_length = sLength + 3; // For BOM header.
347 	} else {
348 		real_length = sLength;
349 	}
350 
351 	switch (SizeLen) {
352 		case 0:
353 			// Don't write size.
354 			break;
355 
356 		case sizeof(uint16):
357 			// We must not allow too long strings to be written,
358 			// as this would allow for a buggy clients to "poison"
359 			// us, by sending ISO8859-1 strings that expand to a
360 			// greater than 16b length when converted as UTF-8.
361 			if (real_length > 0xFFFF) {
362 				wxFAIL_MSG(wxT("String is too long to be saved"));
363 
364 				real_length = std::min<uint32>(real_length, 0xFFFF);
365 				if (eEncode == utf8strOptBOM) {
366 					sLength = real_length - 3;
367 				} else {
368 					sLength = real_length;
369 				}
370 			}
371 
372 			WriteUInt16(real_length);
373 			break;
374 
375 		case sizeof(uint32):
376 			WriteUInt32(real_length);
377 			break;
378 
379 		default:
380 			MULE_VALIDATE_PARAMS(false, wxT("Invalid length for string-length field."));
381 	}
382 
383 	// The BOM header must be written even if the string is empty.
384 	if (eEncode == utf8strOptBOM) {
385 		Write(BOMHeader, 3);
386 	}
387 
388 	// Only attempt to write non-NULL strings.
389 	if (sLength) {
390 		// No NULL terminator is written since we explicitly specify the length
391 		Write(s, sLength);
392 	}
393 }
394 
395 
ReadTag(bool bOptACP) const396 CTag *CFileDataIO::ReadTag(bool bOptACP) const
397 {
398 	CTag *retVal = NULL;
399 	wxString name;
400 	byte type = 0;
401 	try {
402 		type = ReadUInt8();
403 		name = ReadString(false);
404 
405 		switch (type)
406 		{
407 			// NOTE: This tag data type is accepted and stored only to give us the possibility to upgrade
408 			// the net in some months.
409 			//
410 			// And still.. it doesnt't work this way without breaking backward compatibility. To properly
411 			// do this without messing up the network the following would have to be done:
412 			//	 -	those tag types have to be ignored by any client, otherwise those tags would also be sent (and
413 			//		that's really the problem)
414 			//
415 			//	 -	ignoring means, each client has to read and right throw away those tags, so those tags get
416 			//		get never stored in any tag list which might be sent by that client to some other client.
417 			//
418 			//	 -	all calling functions have to be changed to deal with the 'nr. of tags' attribute (which was
419 			//		already parsed) correctly.. just ignoring those tags here is not enough, any taglists have to
420 			//		be built with the knowledge that the 'nr. of tags' attribute may get decreased during the tag
421 			//		reading..
422 			//
423 			// If those new tags would just be stored and sent to remote clients, any malicious or just bugged
424 			// client could let send a lot of nodes "corrupted" packets...
425 			//
426 			case TAGTYPE_HASH16:
427 			{
428 				retVal = new CTagHash(name, ReadHash());
429 				break;
430 			}
431 
432 			case TAGTYPE_STRING:
433 				retVal = new CTagString(name, ReadString(bOptACP));
434 				break;
435 
436 			case TAGTYPE_UINT64:
437 				retVal = new CTagInt64(name, ReadUInt64());
438 				break;
439 
440 			case TAGTYPE_UINT32:
441 				retVal = new CTagInt32(name, ReadUInt32());
442 				break;
443 
444 			case TAGTYPE_UINT16:
445 				retVal = new CTagInt16(name, ReadUInt16());
446 				break;
447 
448 			case TAGTYPE_UINT8:
449 				retVal = new CTagInt8(name, ReadUInt8());
450 				break;
451 
452 			case TAGTYPE_FLOAT32:
453 				retVal = new CTagFloat(name, ReadFloat());
454 				break;
455 
456 			// NOTE: This tag data type is accepted and stored only to give us the possibility to upgrade
457 			// the net in some months.
458 			//
459 			// And still.. it doesnt't work this way without breaking backward compatibility
460 			case TAGTYPE_BSOB:
461 			{
462 				uint8 size = 0;
463 				CScopedArray<unsigned char> value(ReadBsob(&size));
464 
465 				retVal = new CTagBsob(name, value.get(), size);
466 				break;
467 			}
468 
469 			default:
470 				throw wxString(CFormat(wxT("Invalid Kad tag type; type=0x%02x name=%s\n")) % type % name);
471 		}
472 	} catch(const CMuleException& e) {
473 		AddLogLineN(e.what());
474 		delete retVal;
475 		throw;
476 	} catch(const wxString& e) {
477 		AddLogLineN(e);
478 		throw;
479 	}
480 
481 	return retVal;
482 }
483 
484 
ReadTagPtrList(TagPtrList * taglist,bool bOptACP) const485 void CFileDataIO::ReadTagPtrList(TagPtrList* taglist, bool bOptACP) const
486 {
487 	MULE_VALIDATE_PARAMS(taglist, wxT("NULL pointer argument in ReadTagPtrList"));
488 
489 	uint32 count = ReadUInt8();
490 	for (uint32 i = 0; i < count; i++)
491 	{
492 		CTag* tag = ReadTag(bOptACP);
493 		taglist->push_back(tag);
494 	}
495 }
496 
497 
WriteTag(const CTag & tag)498 void CFileDataIO::WriteTag(const CTag& tag)
499 {
500 	try
501 	{
502 		WriteUInt8(tag.GetType());
503 
504 		if (!tag.GetName().IsEmpty()) {
505 			WriteString(tag.GetName(),utf8strNone);
506 		} else {
507 			WriteUInt16(1);
508 			WriteUInt8(tag.GetNameID());
509 		}
510 
511 		switch (tag.GetType())
512 		{
513 			case TAGTYPE_HASH16:
514 				// Do NOT use this to transfer any tags for at least half a year!!
515 				WriteHash(CMD4Hash(tag.GetHash()));
516 				break;
517 			case TAGTYPE_STRING:
518 				WriteString(tag.GetStr(), utf8strRaw); // Always UTF8
519 				break;
520 			case TAGTYPE_UINT64:
521 				WriteUInt64(tag.GetInt());
522 				break;
523 			case TAGTYPE_UINT32:
524 				WriteUInt32(tag.GetInt());
525 				break;
526 			case TAGTYPE_FLOAT32:
527 				WriteFloat(tag.GetFloat());
528 				break;
529 			case TAGTYPE_BSOB:
530 				WriteBsob(tag.GetBsob(), tag.GetBsobSize());
531 				break;
532 			case TAGTYPE_UINT16:
533 				WriteUInt16(tag.GetInt());
534 				break;
535 			case TAGTYPE_UINT8:
536 				WriteUInt8(tag.GetInt());
537 				break;
538 			case TAGTYPE_BLOB:
539 				// NOTE: This will break backward compatibility with met files for eMule versions prior to 0.44a
540 				// and any aMule prior to SVN 26/02/2005
541 				WriteUInt32(tag.GetBlobSize());
542 				Write(tag.GetBlob(), tag.GetBlobSize());
543 				break;
544 			default:
545 				//TODO: Support more tag types
546 				// With the if above, this should NEVER happen.
547 				AddLogLineNS(CFormat(wxT("CFileDataIO::WriteTag: Unknown tag: type=0x%02X")) % tag.GetType());
548 				wxFAIL;
549 				break;
550 		}
551 	} catch (...) {
552 		AddLogLineNS(wxT("Exception in CDataIO:WriteTag"));
553 		throw;
554 	}
555 }
556 
557 
WriteTagPtrList(const TagPtrList & tagList)558 void CFileDataIO::WriteTagPtrList(const TagPtrList& tagList)
559 {
560 	uint32 count = tagList.size();
561 	wxASSERT( count <= 0xFF );
562 
563 	WriteUInt8(count);
564 	TagPtrList::const_iterator it;
565 	for (it = tagList.begin(); it != tagList.end(); ++it) {
566 		WriteTag(**it);
567 	}
568 }
569 
GetIntTagValue() const570 uint64 CFileDataIO::GetIntTagValue() const {
571 
572 	uint8 type = ReadUInt8();
573 
574 	ReadString(false);
575 
576 	switch (type) {
577 
578 		case TAGTYPE_UINT64:
579 			return ReadUInt64();
580 			break;
581 
582 		case TAGTYPE_UINT32:
583 			return ReadUInt32();
584 			break;
585 
586 		case TAGTYPE_UINT16:
587 			return ReadUInt16();
588 			break;
589 
590 		case TAGTYPE_UINT8:
591 			return ReadUInt8();
592 			break;
593 
594 		default:
595 			throw wxString(wxT("Wrong tag type reading int tag"));
596 	}
597 }
598 // File_checked_for_headers
599