1// 2// This file is part of the aMule Project. 3// 4// Copyright (c) 2007-2011 Johannes Krampf ( wuischke@amule.org ) 5// 6// Other code by: 7// 8// Angel Vidal Veiga aka Kry <kry@amule.org> 9// * changed class names 10// 11// Marcelo Malheiros <mgmalheiros@gmail.com> 12// * fixed error with FT_FILEHASH 13// * added inital 5 tag/file support 14// 15// Any parts of this program derived from the xMule, lMule or eMule project, 16// or contributed by third-party developers are copyrighted by their 17// respective authors. 18// 19// This program is free software; you can redistribute it and/or modify 20// it under the terms of the GNU General Public License as published by 21// the Free Software Foundation; either version 2 of the License, or 22// (at your option) any later version. 23// 24// This program is distributed in the hope that it will be useful, 25// but WITHOUT ANY WARRANTY; without even the implied warranty of 26// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27// GNU General Public License for more details. 28// 29// You should have received a copy of the GNU General Public License 30// along with this program; if not, write to the Free Software 31// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 32// 33 34 35#include "MuleCollection.h" 36 37 38#include <fstream> 39#include <iostream> 40#include <sstream> 41#include <string> 42#include <vector> 43 44 45CollectionFile::CollectionFile( 46 const std::string &fileName, 47 uint64_t fileSize, 48 const std::string &fileHash) 49: 50m_fileName(fileName), 51m_fileSize(fileSize), 52m_fileHash(fileHash) 53{ 54} 55 56 57CMuleCollection::CMuleCollection() 58: 59vCollection(0) 60{ 61} 62 63 64CMuleCollection::~CMuleCollection() 65{ 66} 67 68 69bool CMuleCollection::Open(const std::string &File) 70{ 71 return OpenBinary(File) || OpenText(File); 72} 73 74 75std::string CMuleCollection::GetEd2kLink(size_t index) const 76{ 77 if (index >= GetFileCount()) { 78 return "Invalid Index!"; 79 } 80 81 std::stringstream retvalue; 82 // ed2k://|file|fileName|fileSize|fileHash|/ 83 retvalue 84 << "ed2k://|file|" << GetFileName(index) 85 << "|" << GetFileSize(index) 86 << "|" << GetFileHash(index) 87 << "|/"; 88 89 return retvalue.str(); 90} 91 92 93std::string CMuleCollection::GetFileName(size_t index) const 94{ 95 if (index >= GetFileCount()) { 96 return "Invalid Index!"; 97 } 98 99 std::string retvalue = vCollection[index].m_fileName; 100 if (retvalue.empty()) { 101 return "Empty String!"; 102 } 103 104 return retvalue; 105} 106 107 108uint64_t CMuleCollection::GetFileSize(size_t index) const 109{ 110 if (index >= GetFileCount()) { 111 return 0; 112 } 113 114 return vCollection[index].m_fileSize; 115} 116 117 118std::string CMuleCollection::GetFileHash(size_t index) const 119{ 120 if (index >= GetFileCount()) { 121 return "Invalid Index!"; 122 } 123 std::string retvalue = vCollection[index].m_fileHash; 124 if (retvalue.empty()) { 125 return "Empty String!"; 126 } 127 128 return retvalue; 129} 130 131template <typename intType> 132intType CMuleCollection::ReadInt(std::ifstream& infile) 133{ 134 intType integer = 0; 135 infile.read(reinterpret_cast<char *>(&integer),sizeof(intType)); 136 return integer; 137} 138 139std::string CMuleCollection::ReadString(std::ifstream& infile, int TagType = 0x02) 140{ 141 if (TagType >= 0x11 && TagType <= 0x20) { 142 std::vector<char> buffer(TagType - 0x10); 143 infile.read(&buffer[0], TagType - 0x10); 144 return buffer.empty() ? 145 std::string() : 146 std::string (buffer.begin(), buffer.end()); 147 } 148 if (TagType == 0x02) { 149 uint16_t TagStringSize = ReadInt<uint16_t>(infile); 150 std::vector<char> buffer (TagStringSize); 151 infile.read(&buffer[0], TagStringSize); 152 return buffer.empty() ? 153 std::string() : 154 std::string (buffer.begin(), buffer.end()); 155 } 156 return std::string(); 157} 158 159bool CMuleCollection::OpenBinary(const std::string &File) 160{ 161 std::ifstream infile; 162 163 infile.open(File.c_str(), std::ifstream::in|std::ifstream::binary); 164 if(!infile.is_open()) { 165 return false; 166 } 167 168 uint32_t cVersion = ReadInt<uint32_t>(infile); 169 170 if (!infile.good() || 171 ( cVersion != 0x01 && cVersion != 0x02)) { 172 infile.close(); 173 return false; 174 } 175 176 uint32_t hTagCount = ReadInt<uint32_t>(infile); 177 if (!infile.good() || 178 hTagCount > 3) { 179 infile.close(); 180 return false; 181 } 182 183 for (size_t hTi = 0; hTi < hTagCount;hTi++) { 184 int hTagType = infile.get(); 185 186 // hTagFormat == 1 -> FT-value is given 187 uint16_t hTagFormat = ReadInt<uint16_t>(infile); 188 if (hTagFormat != 0x0001) { 189 infile.close(); 190 return false; 191 } 192 193 int hTag = infile.get(); 194 if (!infile.good()) { 195 infile.close(); 196 return false; 197 } 198 switch (hTag) { 199 // FT_FILENAME 200 case 0x01: { 201 std::string fileName = ReadString(infile, hTagType); 202 break; 203 } 204 // FT_COLLECTIONAUTHOR 205 case 0x31: { 206 std::string CollectionAuthor = ReadString(infile, hTagType); 207 break; 208 } 209 // FT_COLLECTIONAUTHORKEY 210 case 0x32: { 211 uint32_t hTagBlobSize = ReadInt<uint32_t>(infile); 212 if (!infile.good()) { 213 infile.close(); 214 return false; 215 } 216 std::vector<char> CollectionAuthorKey(hTagBlobSize); 217 infile.read(&CollectionAuthorKey[0], hTagBlobSize); 218 break; 219 } 220 // UNDEFINED TAG 221 default: 222 if (!infile.good()) { 223 infile.close(); 224 return false; 225 } 226 break; 227 } 228 } 229 230 uint32_t cFileCount = ReadInt<uint32_t>(infile); 231 232 /* 233 softlimit is set to 1024 to avoid problems with big uint32_t values 234 I don't believe anyone would want to use an emulecollection file 235 to store more than 1024 files, but just raise below value in case 236 you know someone who does. 237 */ 238 239 if(!infile.good() || 240 cFileCount > 1024) { 241 infile.close(); 242 return false; 243 } 244 245 for (size_t cFi = 0; cFi < cFileCount; ++cFi) { 246 uint32_t fTagCount = ReadInt<uint32_t>(infile); 247 248 if (!infile.good() || 249 fTagCount > 5) { 250 infile.close(); 251 return false; 252 } 253 254 std::string fileHash = std::string(32, '0'); 255 uint64_t fileSize = 0; 256 std::string fileName; 257 std::string FileComment; 258 for(size_t fTi = 0; fTi < fTagCount; ++fTi) { 259 int fTagType = infile.get(); 260 if (!infile.good()) { 261 infile.close(); 262 return false; 263 } 264 265 int fTag = infile.get(); 266 if (!infile.good()) { 267 infile.close(); 268 return false; 269 } 270 271 switch (fTag) { 272 // FT_FILEHASH 273 case 0x28: { 274 std::vector<char> bFileHash(16); 275 infile.read(&bFileHash[0], 16); 276 std::string hex = "0123456789abcdef"; 277 for (int pos = 0; pos < 16; pos++) { 278 fileHash[pos*2] = hex[((bFileHash[pos] >> 4) & 0xF)]; 279 fileHash[(pos*2) + 1] = hex[(bFileHash[pos]) & 0x0F]; 280 } 281 break; 282 } 283 // FT_FILESIZE 284 case 0x02: { 285 switch(fTagType) { 286 case 0x83: { 287 fileSize = ReadInt<uint32_t>(infile); 288 break; 289 } 290 case 0x88: { 291 fileSize = ReadInt<uint16_t>(infile); 292 break; 293 } 294 case 0x89: { 295 fileSize = infile.get(); 296 break; 297 } 298 case 0x8b: { 299 fileSize = ReadInt<uint64_t>(infile); 300 break; 301 } 302 default: // Invalid file structure 303 infile.close(); 304 return false; 305 break; 306 } 307 break; 308 } 309 // FT_FILENAME 310 case 0x01: { 311 fileName = ReadString(infile, fTagType^0x80); 312 break; 313 } 314 // FT_FILECOMMENT 315 case 0xF6: { 316 FileComment = ReadString(infile, fTagType^0x80); 317 break; 318 } 319 // FT_FILERATING 320 case 0xF7: { 321 if (fTagType == 0x89) { // TAGTYPE_UINT8 322 // uint8_t FileRating = 323 infile.get(); 324 325 } else { 326 infile.close(); 327 return false; 328 } 329 break; 330 } 331 // UNDEFINED TAG 332 default: 333 infile.close(); 334 return false; 335 break; 336 } 337 if( !infile.good() ) { 338 infile.close(); 339 return false; 340 } 341 } 342 AddFile(fileName, fileSize, fileHash); 343 } 344 infile.close(); 345 346 return true; 347} 348 349 350bool CMuleCollection::OpenText(const std::string &File) 351{ 352 int numLinks = 0; 353 std::string line; 354 std::ifstream infile; 355 356 infile.open(File.c_str(), std::ifstream::in|std::ifstream::binary); 357 if (!infile.is_open()) { 358 return false; 359 } 360 361 while (getline(infile, line, (char)10 /* LF */)) { 362 int last = line.size()-1; 363 if ((1 < last) && ((char)13 /* CR */ == line.at(last))) { 364 line.erase(last); 365 } 366 if (AddLink(line)) { 367 numLinks++; 368 } 369 } 370 infile.close(); 371 372 if(numLinks == 0) { 373 return false; 374 } 375 376 return true; 377} 378 379 380bool CMuleCollection::AddLink(const std::string &Link) 381{ 382 // 12345678901234 56 7 + 32 + 89 = 19+32=51 383 // ed2k://|file|fileName|fileSize|fileHash|/ 384 if (Link.size() < 51 || 385 Link.substr(0,13) != "ed2k://|file|" || 386 Link.substr(Link.size()-2) != "|/") { 387 return false; 388 } 389 390 size_t iName = Link.find("|",13); 391 if (iName == std::string::npos) { 392 return false; 393 } 394 std::string fileName = Link.substr(13,iName-13); 395 396 size_t iSize = Link.find("|",iName+1); 397 if (iSize == std::string::npos) { 398 return false; 399 } 400 std::stringstream sFileSize; 401 sFileSize << Link.substr(iName+1,iSize-iName-1); 402 uint64_t fileSize; 403 if ((sFileSize >> std::dec >> fileSize).fail()) { 404 return false; 405 } 406 407 size_t iHash = Link.find("|",iSize+1); 408 if (iHash == std::string::npos) { 409 return false; 410 } 411 std::string fileHash = Link.substr(iSize+1,32); 412 413 return AddFile(fileName, fileSize, fileHash); 414} 415 416 417bool CMuleCollection::AddFile( 418 const std::string &fileName, 419 uint64_t fileSize, 420 const std::string &fileHash) 421{ 422 if (fileName == "" || 423 fileSize == 0 || 424 fileSize > 0xffffffffLL || 425 !IsValidHash(fileHash)) { 426 return false; 427 } 428 429 vCollection.push_back( 430 CollectionFile(fileName, fileSize, fileHash)); 431 return true; 432} 433 434 435bool CMuleCollection::IsValidHash(const std::string &fileHash) 436{ 437 if (fileHash.size() != 32 || fileHash == "") { 438 return false; 439 } 440 441 // fileHash needs to be a valid MD4Hash 442 std::string hex = "0123456789abcdefABCDEF"; 443 for(size_t i = 0; i < fileHash.size(); ++i) { 444 if (hex.find(fileHash[i]) == std::string::npos) { 445 return false; 446 } 447 } 448 return true; 449} 450 451