Release 2.3.3
[amule.git] / src / MuleCollection.cpp
blob6c36f0b6a68218f4aeabb009f7897af431b430c7
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.
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"
37 #include <fstream>
38 #include <iostream>
39 #include <sstream>
42 bool CMuleCollection::Open(const std::string &File)
44 return OpenBinary(File) || OpenText(File);
48 template <typename intType>
49 intType CMuleCollection::ReadInt(std::ifstream& infile)
51 intType integer = 0;
52 infile.read(reinterpret_cast<char *>(&integer),sizeof(intType));
53 // TODO: byte-sex
54 return integer;
57 std::string CMuleCollection::ReadString(std::ifstream& infile, int TagType = 0x02)
59 if (TagType >= 0x11 && TagType <= 0x20) {
60 std::vector<char> buffer(TagType - 0x10);
61 infile.read(&buffer[0], TagType - 0x10);
62 return buffer.empty() ?
63 std::string() :
64 std::string (buffer.begin(), buffer.end());
66 if (TagType == 0x02) {
67 uint16_t TagStringSize = ReadInt<uint16_t>(infile);
68 std::vector<char> buffer (TagStringSize);
69 infile.read(&buffer[0], TagStringSize);
70 return buffer.empty() ?
71 std::string() :
72 std::string (buffer.begin(), buffer.end());
74 return std::string();
77 bool CMuleCollection::OpenBinary(const std::string &File)
79 std::ifstream infile;
81 infile.open(File.c_str(), std::ifstream::in|std::ifstream::binary);
82 if(!infile.is_open()) {
83 return false;
86 uint32_t cVersion = ReadInt<uint32_t>(infile);
88 if (!infile.good() ||
89 ( cVersion != 0x01 && cVersion != 0x02)) {
90 infile.close();
91 return false;
94 uint32_t hTagCount = ReadInt<uint32_t>(infile);
95 if (!infile.good() ||
96 hTagCount > 3) {
97 infile.close();
98 return false;
101 for (size_t hTi = 0; hTi < hTagCount;hTi++) {
102 int hTagType = infile.get();
104 // hTagFormat == 1 -> FT-value is given
105 uint16_t hTagFormat = ReadInt<uint16_t>(infile);
106 if (hTagFormat != 0x0001) {
107 infile.close();
108 return false;
111 int hTag = infile.get();
112 if (!infile.good()) {
113 infile.close();
114 return false;
116 switch (hTag) {
117 // FT_FILENAME
118 case 0x01: {
119 /*std::string fileName =*/ ReadString(infile, hTagType);
120 break;
122 // FT_COLLECTIONAUTHOR
123 case 0x31: {
124 /*std::string CollectionAuthor =*/ ReadString(infile, hTagType);
125 break;
127 // FT_COLLECTIONAUTHORKEY
128 case 0x32: {
129 uint32_t hTagBlobSize = ReadInt<uint32_t>(infile);
130 if (!infile.good()) {
131 infile.close();
132 return false;
134 std::vector<char> CollectionAuthorKey(hTagBlobSize);
135 infile.read(&CollectionAuthorKey[0], hTagBlobSize);
136 break;
138 // UNDEFINED TAG
139 default:
140 if (!infile.good()) {
141 infile.close();
142 return false;
144 break;
148 uint32_t cFileCount = ReadInt<uint32_t>(infile);
151 softlimit is set to 1024 to avoid problems with big uint32_t values
152 I don't believe anyone would want to use an emulecollection file
153 to store more than 1024 files, but just raise below value in case
154 you know someone who does.
157 if(!infile.good() ||
158 cFileCount > 1024) {
159 infile.close();
160 return false;
163 vCollection.reserve(cFileCount);
165 for (size_t cFi = 0; cFi < cFileCount; ++cFi) {
166 uint32_t fTagCount = ReadInt<uint32_t>(infile);
168 if (!infile.good() ||
169 fTagCount > 6) {
170 infile.close();
171 return false;
174 std::string fileHash = std::string(32, '0');
175 uint64_t fileSize = 0;
176 std::string fileName;
177 std::string rootHash;
178 for(size_t fTi = 0; fTi < fTagCount; ++fTi) {
179 int fTagType = infile.get();
180 if (!infile.good()) {
181 infile.close();
182 return false;
185 int fTag = infile.get();
186 if (!infile.good()) {
187 infile.close();
188 return false;
191 switch (fTag) {
192 // FT_FILEHASH
193 case 0x28: {
194 std::vector<char> bFileHash(16);
195 infile.read(&bFileHash[0], 16);
196 std::string hex = "0123456789abcdef";
197 for (int pos = 0; pos < 16; pos++) {
198 fileHash[pos*2] = hex[((bFileHash[pos] >> 4) & 0xF)];
199 fileHash[(pos*2) + 1] = hex[(bFileHash[pos]) & 0x0F];
201 break;
203 // FT_AICH_FILEHASH
204 case 0x27: {
205 rootHash = ReadString(infile, 0x02);
206 break;
208 // FT_FILESIZE
209 case 0x02: {
210 switch(fTagType) {
211 case 0x83: {
212 fileSize = ReadInt<uint32_t>(infile);
213 break;
215 case 0x88: {
216 fileSize = ReadInt<uint16_t>(infile);
217 break;
219 case 0x89: {
220 fileSize = infile.get();
221 break;
223 case 0x8b: {
224 fileSize = ReadInt<uint64_t>(infile);
225 break;
227 default: // Invalid file structure
228 infile.close();
229 return false;
230 break;
232 break;
234 // FT_FILENAME
235 case 0x01: {
236 fileName = ReadString(infile, fTagType^0x80);
237 break;
239 // FT_FILECOMMENT
240 case 0xF6: {
241 /* std::string FileComment =*/ ReadString(infile, fTagType^0x80);
242 break;
244 // FT_FILERATING
245 case 0xF7: {
246 if (fTagType == 0x89) { // TAGTYPE_UINT8
247 // uint8_t FileRating =
248 infile.get();
250 } else {
251 infile.close();
252 return false;
254 break;
256 // UNDEFINED TAG
257 default:
258 infile.close();
259 return false;
260 break;
262 if( !infile.good() ) {
263 infile.close();
264 return false;
268 if (!fileName.empty() && fileSize > 0) {
269 std::stringstream link;
270 // ed2k://|file|fileName|fileSize|fileHash|/
271 link << "ed2k://|file|" << fileName
272 << "|" << fileSize
273 << "|" << fileHash;
274 if (!rootHash.empty()) {
275 link << "|h=" << rootHash;
277 link << "|/";
278 vCollection.push_back(link.str());
281 infile.close();
283 return true;
287 bool CMuleCollection::OpenText(const std::string &File)
289 std::string line;
290 std::ifstream infile;
292 infile.open(File.c_str(), std::ifstream::in|std::ifstream::binary);
293 if (!infile.is_open()) {
294 return false;
297 while (getline(infile, line, (char)10 /* LF */)) {
298 size_t last = line.size()-1;
299 if ((1 < last) && ((char)13 /* CR */ == line.at(last))) {
300 line.erase(last);
302 if (line.size() > 50 &&
303 line.substr(0, 13) == "ed2k://|file|" &&
304 line.substr(line.size() - 2) == "|/") {
305 vCollection.push_back(line);
308 infile.close();
310 return !vCollection.empty();