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 
27 #include "Tag.h"			// Interface declarations
28 
29 #include <common/Format.h>		// Needed for WXLONGLONGFMTSPEC
30 
31 #include "SafeFile.h"			// Needed for CFileDataIO
32 #include "MD4Hash.h"			// Needed for CMD4Hash
33 #include "CompilerSpecific.h"		// Needed for __FUNCTION__
34 
35 
36 ///////////////////////////////////////////////////////////////////////////////
37 // CTag
38 
CTag(const wxString & Name)39 CTag::CTag(const wxString& Name)
40 {
41 	m_uType = 0;
42 	m_uName = 0;
43 	m_Name = Name;
44 	m_uVal = 0;
45 	m_nSize = 0;
46 }
47 
CTag(uint8 uName)48 CTag::CTag(uint8 uName)
49 {
50 	m_uType = 0;
51 	m_uName = uName;
52 	m_uVal = 0;
53 	m_nSize = 0;
54 }
55 
CTag(const CTag & rTag)56 CTag::CTag(const CTag& rTag)
57 {
58 	m_uType = rTag.m_uType;
59 	m_uName = rTag.m_uName;
60 	m_Name = rTag.m_Name;
61 	m_nSize = 0;
62 	if (rTag.IsStr()) {
63 		m_pstrVal = new wxString(rTag.GetStr());
64 	} else if (rTag.IsInt()) {
65 		m_uVal = rTag.GetInt();
66 	} else if (rTag.IsFloat()) {
67 		m_fVal = rTag.GetFloat();
68 	} else if (rTag.IsHash()) {
69 		m_hashVal = new CMD4Hash(rTag.GetHash());
70 	} else if (rTag.IsBlob()) {
71 		m_nSize = rTag.GetBlobSize();
72 		m_pData = new unsigned char[rTag.GetBlobSize()];
73 		memcpy(m_pData, rTag.GetBlob(), rTag.GetBlobSize());
74 	} else if (rTag.IsBsob()) {
75 		m_nSize = rTag.GetBsobSize();
76 		m_pData = new unsigned char[rTag.GetBsobSize()];
77 		memcpy(m_pData, rTag.GetBsob(), rTag.GetBsobSize());
78 	} else {
79 		wxFAIL;
80 		m_uVal = 0;
81 	}
82 }
83 
84 
CTag(const CFileDataIO & data,bool bOptUTF8)85 CTag::CTag(const CFileDataIO& data, bool bOptUTF8)
86 {
87 	// Zero variables to allow for safe deletion
88 	m_uType = m_uName = m_nSize = m_uVal = 0;
89 	m_pData = NULL;
90 
91 	try {
92 		m_uType = data.ReadUInt8();
93 		if (m_uType & 0x80) {
94 			m_uType &= 0x7F;
95 			m_uName = data.ReadUInt8();
96 		} else {
97 			uint16 length = data.ReadUInt16();
98 			if (length == 1) {
99 				m_uName = data.ReadUInt8();
100 			} else {
101 				m_uName = 0;
102 				m_Name = data.ReadOnlyString(utf8strNone,length);
103 			}
104 		}
105 
106 		// NOTE: It's very important that we read the *entire* packet data,
107 		// even if we do not use each tag. Otherwise we will get in trouble
108 		// when the packets are returned in a list - like the search results
109 		// from a server. If we cannot do this, then we throw an exception.
110 		switch (m_uType) {
111 			case TAGTYPE_STRING:
112 				m_pstrVal = new wxString(data.ReadString(bOptUTF8));
113 				break;
114 
115 			case TAGTYPE_UINT32:
116 				m_uVal = data.ReadUInt32();
117 				break;
118 
119 			case TAGTYPE_UINT64:
120 				m_uVal = data.ReadUInt64();
121 				break;
122 
123 			case TAGTYPE_UINT16:
124 				m_uVal = data.ReadUInt16();
125 				m_uType = TAGTYPE_UINT32;
126 				break;
127 
128 			case TAGTYPE_UINT8:
129 				m_uVal = data.ReadUInt8();
130 				m_uType = TAGTYPE_UINT32;
131 				break;
132 
133 			case TAGTYPE_FLOAT32:
134 				//#warning Endianess problem?
135 				data.Read(&m_fVal, 4);
136 				break;
137 
138 			case TAGTYPE_HASH16:
139 				m_hashVal = new CMD4Hash(data.ReadHash());
140 				break;
141 
142 			case TAGTYPE_BOOL:
143 				printf("***NOTE: %s; Reading BOOL tag\n", __FUNCTION__);
144 				data.ReadUInt8();
145 				break;
146 
147 			case TAGTYPE_BOOLARRAY: {
148 				printf("***NOTE: %s; Reading BOOL Array tag\n", __FUNCTION__);
149 				uint16 len = data.ReadUInt16();
150 
151 				// 07-Apr-2004: eMule versions prior to 0.42e.29 used the formula "(len+7)/8"!
152 				//#warning This seems to be off by one! 8 / 8 + 1 == 2, etc.
153 				data.Seek((len / 8) + 1, wxFromCurrent);
154 				break;
155 			}
156 
157 			case TAGTYPE_BLOB:
158 				// 07-Apr-2004: eMule versions prior to 0.42e.29 handled the "len" as int16!
159 				m_nSize = data.ReadUInt32();
160 
161 				// Since the length is 32b, this check is needed to avoid
162 				// huge allocations in case of bad tags.
163 				if (m_nSize > data.GetLength() - data.GetPosition()) {
164 					throw CInvalidPacket(wxT("Malformed tag"));
165 				}
166 
167 				m_pData = new unsigned char[m_nSize];
168 				data.Read(m_pData, m_nSize);
169 				break;
170 
171 			default:
172 				if (m_uType >= TAGTYPE_STR1 && m_uType <= TAGTYPE_STR16) {
173 					uint8 length = m_uType - TAGTYPE_STR1 + 1;
174 					m_pstrVal = new wxString(data.ReadOnlyString(bOptUTF8, length));
175 					m_uType = TAGTYPE_STRING;
176 				} else {
177 					// Since we cannot determine the length of this tag, we
178 					// simply have to abort reading the file.
179 					throw CInvalidPacket(CFormat(wxT("Unknown tag type encounted %x, cannot proceed!")) % m_uType);
180 				}
181 		}
182 	} catch (...) {
183 		if (m_uType == TAGTYPE_BLOB) {
184 			delete[] m_pData;
185 		}
186 
187 		throw;
188 	}
189 }
190 
191 
~CTag()192 CTag::~CTag()
193 {
194 	if (IsStr()) {
195 		delete m_pstrVal;
196 	} else if (IsHash()) {
197 		delete m_hashVal;
198 	} else if (IsBlob() || IsBsob()) {
199 		delete[] m_pData;
200 	}
201 }
202 
203 
operator =(const CTag & rhs)204 CTag &CTag::operator=(const CTag &rhs)
205 {
206 	if (&rhs != this) {
207 		m_uType = rhs.m_uType;
208 		m_uName = rhs.m_uName;
209 		m_Name = rhs.m_Name;
210 		m_nSize = 0;
211 		if (rhs.IsStr()) {
212 			wxString *p = new wxString(rhs.GetStr());
213 			delete m_pstrVal;
214 			m_pstrVal = p;
215 		} else if (rhs.IsInt()) {
216 			m_uVal = rhs.GetInt();
217 		} else if (rhs.IsFloat()) {
218 			m_fVal = rhs.GetFloat();
219 		} else if (rhs.IsHash()) {
220 			CMD4Hash *p = new CMD4Hash(rhs.GetHash());
221 			delete m_hashVal;
222 			m_hashVal = p;
223 		} else if (rhs.IsBlob()) {
224 			m_nSize = rhs.GetBlobSize();
225 			unsigned char *p = new unsigned char[rhs.GetBlobSize()];
226 			delete [] m_pData;
227 			m_pData = p;
228 			memcpy(m_pData, rhs.GetBlob(), rhs.GetBlobSize());
229 		} else if (rhs.IsBsob()) {
230 			m_nSize = rhs.GetBsobSize();
231 			unsigned char *p = new unsigned char[rhs.GetBsobSize()];
232 			delete [] m_pData;
233 			m_pData = p;
234 			memcpy(m_pData, rhs.GetBsob(), rhs.GetBsobSize());
235 		} else {
236 			wxFAIL;
237 			m_uVal = 0;
238 		}
239 	}
240 	return *this;
241 }
242 
243 
244 #define CHECK_TAG_TYPE(check, expected) \
245 	if (!(check)) { \
246 		throw CInvalidPacket(wxT(#expected) wxT(" tag expected, but found ") + GetFullInfo()); \
247 	}
248 
GetInt() const249 uint64 CTag::GetInt() const
250 {
251 	CHECK_TAG_TYPE(IsInt(), Integer);
252 
253 	return m_uVal;
254 }
255 
256 
GetStr() const257 const wxString& CTag::GetStr() const
258 {
259 	CHECK_TAG_TYPE(IsStr(), String);
260 
261 	return *m_pstrVal;
262 }
263 
264 
GetFloat() const265 float CTag::GetFloat() const
266 {
267 	CHECK_TAG_TYPE(IsFloat(), Float);
268 
269 	return m_fVal;
270 }
271 
272 
GetHash() const273 const CMD4Hash& CTag::GetHash() const
274 {
275 	CHECK_TAG_TYPE(IsHash(), Hash);
276 
277 	return *m_hashVal;
278 }
279 
280 
GetBlobSize() const281 uint32 CTag::GetBlobSize() const
282 {
283 	CHECK_TAG_TYPE(IsBlob(), Blob);
284 
285 	return m_nSize;
286 }
287 
288 
GetBlob() const289 const byte* CTag::GetBlob() const
290 {
291 	CHECK_TAG_TYPE(IsBlob(), Blob);
292 
293 	return m_pData;
294 }
295 
296 
GetBsobSize() const297 uint32 CTag::GetBsobSize() const
298 {
299 	CHECK_TAG_TYPE(IsBsob(), Bsob);
300 
301 	return m_nSize;
302 }
303 
304 
GetBsob() const305 const byte* CTag::GetBsob() const
306 {
307 	CHECK_TAG_TYPE(IsBsob(), Bsob);
308 
309 	return m_pData;
310 }
311 
WriteNewEd2kTag(CFileDataIO * data,EUtf8Str eStrEncode) const312 bool CTag::WriteNewEd2kTag(CFileDataIO* data, EUtf8Str eStrEncode) const
313 {
314 
315 	// Write tag type
316 	uint8 uType;
317 
318 	if (IsInt()) {
319 		if (m_uVal <= 0xFF) {
320 			uType = TAGTYPE_UINT8;
321 		} else if (m_uVal <= 0xFFFF) {
322 			uType = TAGTYPE_UINT16;
323 		} else if (m_uVal <= 0xFFFFFFFF) {
324 			uType = TAGTYPE_UINT32;
325 		} else  {
326 			uType = TAGTYPE_UINT64;
327 		}
328 	} else if (IsStr()) {
329 		uint16 uStrValLen = GetRawSize(*m_pstrVal, eStrEncode);
330 		if (uStrValLen >= 1 && uStrValLen <= 16) {
331 			uType = TAGTYPE_STR1 + uStrValLen - 1;
332 		} else {
333 			uType = TAGTYPE_STRING;
334 		}
335 	} else {
336 		uType = m_uType;
337 	}
338 
339 	// Write tag name
340 	if (!m_Name.IsEmpty()) {
341 		data->WriteUInt8(uType);
342 		data->WriteString(m_Name,utf8strNone);
343 	} else {
344 		wxASSERT( m_uName != 0 );
345 		data->WriteUInt8(uType | 0x80);
346 		data->WriteUInt8(m_uName);
347 	}
348 
349 	// Write tag data
350 	switch (uType) {
351 		case TAGTYPE_STRING:
352 			data->WriteString(*m_pstrVal,eStrEncode);
353 			break;
354 		case TAGTYPE_UINT64:
355 			data->WriteUInt64(m_uVal);
356 			break;
357 		case TAGTYPE_UINT32:
358 			data->WriteUInt32(m_uVal);
359 			break;
360 		case TAGTYPE_UINT16:
361 			data->WriteUInt16(m_uVal);
362 			break;
363 		case TAGTYPE_UINT8:
364 			data->WriteUInt8(m_uVal);
365 			break;
366 		case TAGTYPE_FLOAT32:
367 			//#warning Endianess problem?
368 			data->Write(&m_fVal, 4);
369 			break;
370 		case TAGTYPE_HASH16:
371 			data->WriteHash(*m_hashVal);
372 			break;
373 		case TAGTYPE_BLOB:
374 			data->WriteUInt32(m_nSize);
375 			data->Write(m_pData, m_nSize);
376 			break;
377 		default:
378 			// See comment on the default: of CTag::CTag(const CFileDataIO& data, bool bOptUTF8)
379 			if (uType >= TAGTYPE_STR1 && uType <= TAGTYPE_STR16) {
380 				// Sending '0' as len size makes it not write the len on the IO file.
381 				// This is because this tag types send the len as their type.
382 				data->WriteString(*m_pstrVal,eStrEncode,0);
383 			} else {
384 				printf("%s; Unknown tag: type=0x%02X\n", __FUNCTION__, uType);
385 				wxFAIL;
386 				return false;
387 			}
388 			break;
389 	}
390 
391 	return true;
392 }
393 
WriteTagToFile(CFileDataIO * file,EUtf8Str WXUNUSED (eStrEncode),bool restrictive) const394 bool CTag::WriteTagToFile(CFileDataIO* file, EUtf8Str WXUNUSED(eStrEncode), bool restrictive) const
395 {
396 
397 	// Don't write tags of unknown types, we wouldn't be able to read them in again
398 	// and the met file would be corrupted
399 	if (!restrictive || (IsStr() || IsInt() || IsFloat() || IsBlob())) {
400 
401 		// If this fails, it'll throw.
402 		file->WriteTag(*this);
403 		return true;
404 
405 	} else {
406 		printf("%s; Ignored tag with unknown type=0x%02X\n", __FUNCTION__, m_uType);
407 		return false;
408 	}
409 }
410 
411 
GetFullInfo() const412 wxString CTag::GetFullInfo() const
413 {
414 	wxString strTag;
415 	if (!m_Name.IsEmpty()) {
416 		// Special case: Kad tags, and some ED2k tags ...
417 		if (m_Name.Length() == 1) {
418 			strTag = CFormat(wxT("0x%02X")) % (unsigned)m_Name[0];
419 		} else {
420 			strTag = wxT('\"');
421 			strTag += m_Name;
422 			strTag += wxT('\"');
423 		}
424 	} else {
425 		strTag = CFormat(wxT("0x%02X")) % m_uName;
426 	}
427 	strTag += wxT("=");
428 	if (m_uType == TAGTYPE_STRING) {
429 		strTag += wxT("\"");
430 		strTag += *m_pstrVal;
431 		strTag += wxT("\"");
432 	} else if (m_uType >= TAGTYPE_STR1 && m_uType <= TAGTYPE_STR16) {
433 		strTag += CFormat(wxT("(Str%u)\"")) % (m_uType - TAGTYPE_STR1 + 1)
434 					+  *m_pstrVal + wxT("\"");
435 	} else if (m_uType == TAGTYPE_UINT64) {
436 		strTag += CFormat(wxT("(Int64)%u")) % m_uVal;
437 	} else if (m_uType == TAGTYPE_UINT32) {
438 		strTag += CFormat(wxT("(Int32)%u")) % m_uVal;
439 	} else if (m_uType == TAGTYPE_UINT16) {
440 		strTag += CFormat(wxT("(Int16)%u")) % m_uVal;
441 	} else if (m_uType == TAGTYPE_UINT8) {
442 		strTag += CFormat(wxT("(Int8)%u")) % m_uVal;
443 	} else if (m_uType == TAGTYPE_FLOAT32) {
444 		strTag += CFormat(wxT("(Float32)%f")) % m_fVal;
445 	} else if (m_uType == TAGTYPE_BLOB) {
446 		strTag += CFormat(wxT("(Blob)%u")) % m_nSize;
447 	} else if (m_uType == TAGTYPE_BSOB) {
448 		strTag += CFormat(wxT("(Bsob)%u")) % m_nSize;
449 	} else {
450 		strTag += CFormat(wxT("Type=%u")) % m_uType;
451 	}
452 	return strTag;
453 }
454 
CTagHash(const wxString & name,const CMD4Hash & value)455 CTagHash::CTagHash(const wxString& name, const CMD4Hash& value)
456 	: CTag(name) {
457 		m_hashVal = new CMD4Hash(value);
458 		m_uType = TAGTYPE_HASH16;
459 	}
460 
CTagHash(uint8 name,const CMD4Hash & value)461 CTagHash::CTagHash(uint8 name, const CMD4Hash& value)
462 	: CTag(name) {
463 		m_hashVal = new CMD4Hash(value);
464 		m_uType = TAGTYPE_HASH16;
465 	}
466 
deleteTagPtrListEntries(TagPtrList * taglist)467 void deleteTagPtrListEntries(TagPtrList* taglist)
468 {
469 	DeleteContents(*taglist);
470 }
471 // File_checked_for_headers
472