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