Updated changelog
[amule.git] / src / Tag.cpp
blob2e7e0c803c0727cff6ff393182680c6085466eca
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.
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.
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
27 #include "Tag.h" // Interface declarations
29 #include <common/Format.h> // Needed for WXLONGLONGFMTSPEC
31 #include "SafeFile.h" // Needed for CFileDataIO
32 #include "MD4Hash.h" // Needed for CMD4Hash
33 #include "CompilerSpecific.h" // Needed for __FUNCTION__
36 ///////////////////////////////////////////////////////////////////////////////
37 // CTag
39 CTag::CTag(const wxString& Name)
41 m_uType = 0;
42 m_uName = 0;
43 m_Name = Name;
44 m_uVal = 0;
45 m_nSize = 0;
48 CTag::CTag(uint8 uName)
50 m_uType = 0;
51 m_uName = uName;
52 m_uVal = 0;
53 m_nSize = 0;
56 CTag::CTag(const CTag& rTag)
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;
85 CTag::CTag(const CFileDataIO& data, bool bOptUTF8)
87 // Zero variables to allow for safe deletion
88 m_uType = m_uName = m_nSize = m_uVal = 0;
89 m_pData = NULL;
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);
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;
115 case TAGTYPE_UINT32:
116 m_uVal = data.ReadUInt32();
117 break;
119 case TAGTYPE_UINT64:
120 m_uVal = data.ReadUInt64();
121 break;
123 case TAGTYPE_UINT16:
124 m_uVal = data.ReadUInt16();
125 m_uType = TAGTYPE_UINT32;
126 break;
128 case TAGTYPE_UINT8:
129 m_uVal = data.ReadUInt8();
130 m_uType = TAGTYPE_UINT32;
131 break;
133 case TAGTYPE_FLOAT32:
134 //#warning Endianess problem?
135 data.Read(&m_fVal, 4);
136 break;
138 case TAGTYPE_HASH16:
139 m_hashVal = new CMD4Hash(data.ReadHash());
140 break;
142 case TAGTYPE_BOOL:
143 printf("***NOTE: %s; Reading BOOL tag\n", __FUNCTION__);
144 data.ReadUInt8();
145 break;
147 case TAGTYPE_BOOLARRAY: {
148 printf("***NOTE: %s; Reading BOOL Array tag\n", __FUNCTION__);
149 uint16 len = data.ReadUInt16();
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;
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();
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"));
167 m_pData = new unsigned char[m_nSize];
168 data.Read(m_pData, m_nSize);
169 break;
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);
182 } catch (...) {
183 if (m_uType == TAGTYPE_BLOB) {
184 delete[] m_pData;
187 throw;
192 CTag::~CTag()
194 if (IsStr()) {
195 delete m_pstrVal;
196 } else if (IsHash()) {
197 delete m_hashVal;
198 } else if (IsBlob() || IsBsob()) {
199 delete[] m_pData;
204 CTag &CTag::operator=(const CTag &rhs)
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;
240 return *this;
244 #define CHECK_TAG_TYPE(check, expected) \
245 if (!(check)) { \
246 throw CInvalidPacket(wxT(#expected) wxT(" tag expected, but found ") + GetFullInfo()); \
249 uint64 CTag::GetInt() const
251 CHECK_TAG_TYPE(IsInt(), Integer);
253 return m_uVal;
257 const wxString& CTag::GetStr() const
259 CHECK_TAG_TYPE(IsStr(), String);
261 return *m_pstrVal;
265 float CTag::GetFloat() const
267 CHECK_TAG_TYPE(IsFloat(), Float);
269 return m_fVal;
273 const CMD4Hash& CTag::GetHash() const
275 CHECK_TAG_TYPE(IsHash(), Hash);
277 return *m_hashVal;
281 uint32 CTag::GetBlobSize() const
283 CHECK_TAG_TYPE(IsBlob(), Blob);
285 return m_nSize;
289 const byte* CTag::GetBlob() const
291 CHECK_TAG_TYPE(IsBlob(), Blob);
293 return m_pData;
297 uint32 CTag::GetBsobSize() const
299 CHECK_TAG_TYPE(IsBsob(), Bsob);
301 return m_nSize;
305 const byte* CTag::GetBsob() const
307 CHECK_TAG_TYPE(IsBsob(), Bsob);
309 return m_pData;
312 bool CTag::WriteNewEd2kTag(CFileDataIO* data, EUtf8Str eStrEncode) const
315 // Write tag type
316 uint8 uType;
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;
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;
335 } else {
336 uType = m_uType;
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);
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;
388 break;
391 return true;
394 bool CTag::WriteTagToFile(CFileDataIO* file, EUtf8Str WXUNUSED(eStrEncode), bool restrictive) const
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())) {
401 // If this fails, it'll throw.
402 file->WriteTag(*this);
403 return true;
405 } else {
406 printf("%s; Ignored tag with unknown type=0x%02X\n", __FUNCTION__, m_uType);
407 return false;
412 wxString CTag::GetFullInfo() const
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('\"');
424 } else {
425 strTag = CFormat(wxT("0x%02X")) % m_uName;
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;
452 return strTag;
455 CTagHash::CTagHash(const wxString& name, const CMD4Hash& value)
456 : CTag(name) {
457 m_hashVal = new CMD4Hash(value);
458 m_uType = TAGTYPE_HASH16;
461 CTagHash::CTagHash(uint8 name, const CMD4Hash& value)
462 : CTag(name) {
463 m_hashVal = new CMD4Hash(value);
464 m_uType = TAGTYPE_HASH16;
467 void deleteTagPtrListEntries(TagPtrList* taglist)
469 DeleteContents(*taglist);
471 // File_checked_for_headers