Fixed problem in the CFile write_safe concept
[amule.git] / src / MuleCollection.cpp
blob1906978b429d691b883f6c53bb3548b51990086e
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
11 // Marcelo Malheiros <mgmalheiros@gmail.com>
12 // * fixed error with FT_FILEHASH
13 // * added inital 5 tag/file support
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.
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.
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
35 #include "MuleCollection.h"
38 #include <fstream>
39 #include <iostream>
40 #include <sstream>
41 #include <string>
42 #include <vector>
45 CollectionFile::CollectionFile(
46 const std::string &fileName,
47 uint64_t fileSize,
48 const std::string &fileHash)
50 m_fileName(fileName),
51 m_fileSize(fileSize),
52 m_fileHash(fileHash)
57 CMuleCollection::CMuleCollection()
59 vCollection(0)
64 CMuleCollection::~CMuleCollection()
69 bool CMuleCollection::Open(const std::string &File)
71 return OpenBinary(File) || OpenText(File);
75 std::string CMuleCollection::GetEd2kLink(size_t index) const
77 if (index >= GetFileCount()) {
78 return "Invalid Index!";
81 std::stringstream retvalue;
82 // ed2k://|file|fileName|fileSize|fileHash|/
83 retvalue
84 << "ed2k://|file|" << GetFileName(index)
85 << "|" << GetFileSize(index)
86 << "|" << GetFileHash(index)
87 << "|/";
89 return retvalue.str();
93 std::string CMuleCollection::GetFileName(size_t index) const
95 if (index >= GetFileCount()) {
96 return "Invalid Index!";
99 std::string retvalue = vCollection[index].m_fileName;
100 if (retvalue.empty()) {
101 return "Empty String!";
104 return retvalue;
108 uint64_t CMuleCollection::GetFileSize(size_t index) const
110 if (index >= GetFileCount()) {
111 return 0;
114 return vCollection[index].m_fileSize;
118 std::string CMuleCollection::GetFileHash(size_t index) const
120 if (index >= GetFileCount()) {
121 return "Invalid Index!";
123 std::string retvalue = vCollection[index].m_fileHash;
124 if (retvalue.empty()) {
125 return "Empty String!";
128 return retvalue;
131 template <typename intType>
132 intType CMuleCollection::ReadInt(std::ifstream& infile)
134 intType integer = 0;
135 infile.read(reinterpret_cast<char *>(&integer),sizeof(intType));
136 return integer;
139 std::string CMuleCollection::ReadString(std::ifstream& infile, int TagType = 0x02)
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());
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());
156 return std::string();
159 bool CMuleCollection::OpenBinary(const std::string &File)
161 std::ifstream infile;
163 infile.open(File.c_str(), std::ifstream::in|std::ifstream::binary);
164 if(!infile.is_open()) {
165 return false;
168 uint32_t cVersion = ReadInt<uint32_t>(infile);
170 if (!infile.good() ||
171 ( cVersion != 0x01 && cVersion != 0x02)) {
172 infile.close();
173 return false;
176 uint32_t hTagCount = ReadInt<uint32_t>(infile);
177 if (!infile.good() ||
178 hTagCount > 3) {
179 infile.close();
180 return false;
183 for (size_t hTi = 0; hTi < hTagCount;hTi++) {
184 int hTagType = infile.get();
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;
193 int hTag = infile.get();
194 if (!infile.good()) {
195 infile.close();
196 return false;
198 switch (hTag) {
199 // FT_FILENAME
200 case 0x01: {
201 std::string fileName = ReadString(infile, hTagType);
202 break;
204 // FT_COLLECTIONAUTHOR
205 case 0x31: {
206 std::string CollectionAuthor = ReadString(infile, hTagType);
207 break;
209 // FT_COLLECTIONAUTHORKEY
210 case 0x32: {
211 uint32_t hTagBlobSize = ReadInt<uint32_t>(infile);
212 if (!infile.good()) {
213 infile.close();
214 return false;
216 std::vector<char> CollectionAuthorKey(hTagBlobSize);
217 infile.read(&CollectionAuthorKey[0], hTagBlobSize);
218 break;
220 // UNDEFINED TAG
221 default:
222 if (!infile.good()) {
223 infile.close();
224 return false;
226 break;
230 uint32_t cFileCount = ReadInt<uint32_t>(infile);
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.
239 if(!infile.good() ||
240 cFileCount > 1024) {
241 infile.close();
242 return false;
245 for (size_t cFi = 0; cFi < cFileCount; ++cFi) {
246 uint32_t fTagCount = ReadInt<uint32_t>(infile);
248 if (!infile.good() ||
249 fTagCount > 5) {
250 infile.close();
251 return false;
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;
265 int fTag = infile.get();
266 if (!infile.good()) {
267 infile.close();
268 return false;
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];
281 break;
283 // FT_FILESIZE
284 case 0x02: {
285 switch(fTagType) {
286 case 0x83: {
287 fileSize = ReadInt<uint32_t>(infile);
288 break;
290 case 0x88: {
291 fileSize = ReadInt<uint16_t>(infile);
292 break;
294 case 0x89: {
295 fileSize = infile.get();
296 break;
298 case 0x8b: {
299 fileSize = ReadInt<uint64_t>(infile);
300 break;
302 default: // Invalid file structure
303 infile.close();
304 return false;
305 break;
307 break;
309 // FT_FILENAME
310 case 0x01: {
311 fileName = ReadString(infile, fTagType^0x80);
312 break;
314 // FT_FILECOMMENT
315 case 0xF6: {
316 FileComment = ReadString(infile, fTagType^0x80);
317 break;
319 // FT_FILERATING
320 case 0xF7: {
321 if (fTagType == 0x89) { // TAGTYPE_UINT8
322 // uint8_t FileRating =
323 infile.get();
325 } else {
326 infile.close();
327 return false;
329 break;
331 // UNDEFINED TAG
332 default:
333 infile.close();
334 return false;
335 break;
337 if( !infile.good() ) {
338 infile.close();
339 return false;
342 AddFile(fileName, fileSize, fileHash);
344 infile.close();
346 return true;
350 bool CMuleCollection::OpenText(const std::string &File)
352 int numLinks = 0;
353 std::string line;
354 std::ifstream infile;
356 infile.open(File.c_str(), std::ifstream::in|std::ifstream::binary);
357 if (!infile.is_open()) {
358 return false;
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);
366 if (AddLink(line)) {
367 numLinks++;
370 infile.close();
372 if(numLinks == 0) {
373 return false;
376 return true;
380 bool CMuleCollection::AddLink(const std::string &Link)
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;
390 size_t iName = Link.find("|",13);
391 if (iName == std::string::npos) {
392 return false;
394 std::string fileName = Link.substr(13,iName-13);
396 size_t iSize = Link.find("|",iName+1);
397 if (iSize == std::string::npos) {
398 return false;
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;
407 size_t iHash = Link.find("|",iSize+1);
408 if (iHash == std::string::npos) {
409 return false;
411 std::string fileHash = Link.substr(iSize+1,32);
413 return AddFile(fileName, fileSize, fileHash);
417 bool CMuleCollection::AddFile(
418 const std::string &fileName,
419 uint64_t fileSize,
420 const std::string &fileHash)
422 if (fileName == "" ||
423 fileSize == 0 ||
424 fileSize > 0xffffffffLL ||
425 !IsValidHash(fileHash)) {
426 return false;
429 vCollection.push_back(
430 CollectionFile(fileName, fileSize, fileHash));
431 return true;
435 bool CMuleCollection::IsValidHash(const std::string &fileHash)
437 if (fileHash.size() != 32 || fileHash == "") {
438 return false;
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;
448 return true;