00001 /* 00002 00003 Copyright (C) 2006-2007 by Peter Dimov. 00004 00005 This file is part of Calitko (http://www.calitko.org). 00006 00007 Calitko is free software; you can redistribute it and/or modify 00008 it under the terms of the GNU General Public License as published by 00009 the Free Software Foundation; either version 2 of the License, or 00010 (at your option) any later version. 00011 00012 Calitko is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 GNU General Public License for more details. 00016 00017 You should have received a copy of the GNU General Public License 00018 along with Calitko; if not, write to the Free Software 00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 00020 00021 */ 00022 00023 #include "Qt.h" 00024 #include "BinaryReader.h" 00025 #include "Imports.cpp" 00026 00028 BinaryReader::BinaryReader (const QByteArray &rawData) 00029 : BinaryReaderBase (rawData, LittleEndian) 00030 { 00031 } 00032 00034 00043 QUuid BinaryReader::readUuid() 00044 { 00045 QUuid uuid; 00046 setByteOrder (BigEndian); 00047 uuid.data1 = readUInt32(); 00048 uuid.data2 = readUInt16(); 00049 uuid.data3 = readUInt16(); 00050 uuid.data4[0] = readByte(); 00051 uuid.data4[1] = readByte(); 00052 uuid.data4[2] = readByte(); 00053 uuid.data4[3] = readByte(); 00054 uuid.data4[4] = readByte(); 00055 uuid.data4[5] = readByte(); 00056 uuid.data4[6] = readByte(); 00057 uuid.data4[7] = readByte(); 00058 setByteOrder (LittleEndian); 00059 return uuid; 00060 } 00061 00063 00067 QHostAddress BinaryReader::readIPv4Address() 00068 { 00069 setByteOrder (BigEndian); 00070 QHostAddress address (readUInt32()); 00071 setByteOrder (LittleEndian); 00072 return address; 00073 } 00074 00076 00084 GgepBlock BinaryReader::readGgepBlock() 00085 { 00086 if(lookAhead (1) != GgepBlock::MagicByte) 00087 return GgepBlock(); 00088 readByte(); // Eat the GgepBlock::MagicByte. 00089 00090 GgepBlock block; 00091 uchar flags = 0; 00092 QByteArray id; 00093 int dataLength = 0; 00094 while (!(flags & Extensions::Ggep::LastExtension)) { 00095 if (!readGgepBlockExtensionHeader (flags, id)) 00096 return GgepBlock(); 00097 00098 if (!readGgepBlockDataLength (dataLength)) 00099 return GgepBlock(); 00100 00101 QByteArray rawData = readBytes (dataLength); 00102 auto_ptr <Extensions::Ggep> extension; 00103 extension.reset (Extensions::Ggep::fromId (id, flags, rawData)); 00104 Q_ASSERT (extension.get() != 0); 00105 block.addExtension (*extension); 00106 } 00107 return block; 00108 } 00109 00111 00118 bool BinaryReader::readGgepBlockExtensionHeader (uchar &flags, QByteArray &id) 00119 { 00120 flags = readByte(); 00121 int idLength = flags & Extensions::Ggep::IDLength; 00122 // ID must not be zero length. 00123 if (idLength == 0) 00124 return false; 00125 id = readBytes(idLength); 00126 return true; 00127 } 00128 00130 00137 bool BinaryReader::readGgepBlockDataLength (int &dataLength) 00138 { 00139 bool readMore = false; 00140 int bytesRead = 0; 00141 dataLength = 0; // We'll be ORing so make sure it's reset. 00142 do { 00143 char byte = readByte(); 00144 dataLength = (dataLength << 6) | (byte & 0x3F); 00145 readMore = (byte & 0x40) != 0x40; 00146 bytesRead++; 00147 } while (readMore && bytesRead < 3); 00148 // The data length encoding was corrupt if readMore is still true: 00149 return readMore == false; 00150 } 00151 00153 00157 QueryData BinaryReader::readQueryData() 00158 { 00159 QByteArray bytes = readAll(); 00160 return readHugeGemBlock (bytes); 00161 } 00162 00164 00169 MinSpeed BinaryReader::readMinSpeed() 00170 { 00171 MinSpeed speed; 00172 uchar byte = readByte(); 00173 if (byte & 0x80) { 00174 speed.isValid = true; 00175 speed.isFirewalled = byte & 0x40; 00176 speed.wantXmlMetadata = byte & 0x20; 00177 speed.isLeafGuidedDynamicQuery = byte & 0x10; 00178 speed.isAllowedGgepH = byte & 0x08; 00179 speed.isOutOfBoundQuery = byte & 0x04; 00180 speed.maxQueryHits = readByte(); 00181 } else { 00182 readByte(); // invalid speed, ignore next byte 00183 } 00184 return speed; 00185 } 00186 00188 00191 VendorCode BinaryReader::readVendorCode() 00192 { 00193 return VendorCode (readBytes (VendorCode::VendorCodeLength)); 00194 } 00195 00197 00203 ResultSet BinaryReader::readQueryHitsResultSet (int hitsNumber) 00204 { 00205 ResultSet resultSet; 00206 Result result; 00207 for(int i=0;i<hitsNumber;++i) 00208 { 00209 result.fileIndex = readUInt32(); 00210 result.fileSize = readUInt32(); 00211 result.fullFileName = readString(); 00212 const QByteArray bytes = readString ('\0'); 00213 00214 result.resultData = readHugeGemBlock (bytes); 00215 resultSet.append (result); 00216 } 00217 return resultSet; 00218 } 00219 00221 00224 QueryHitsData BinaryReader::readQueryHitsData() 00225 { 00226 QueryHitsData queryHitsData; 00227 00228 // Read the serventId from the last 16 bytes. 00229 setReadOrigin (ReadFromEnd); 00230 QByteArray tempBytes = readBytes(16); 00231 queryHitsData.serventId = BinaryReader (tempBytes).readUuid(); 00232 setReadOrigin (ReadFromStart); 00233 00234 // Return if no more data is available (everything but serventId is Null). 00235 if(!canRead(1)) 00236 return queryHitsData; 00237 00238 // Read the vendorCode 00239 queryHitsData.vendorCode = readVendorCode(); 00240 uchar openAreaSize = readByte(); 00241 00242 // Read openAreaFlags 00243 uchar flags1 = readByte(); 00244 uchar flags2 = readByte(); 00245 00246 bool hasGgep = false; 00247 if (flags1 & 0x20) hasGgep = flags2 & 0x20; 00248 if (flags1 & 0x10) queryHitsData.flagUploadSpeed = flags2 & 0x10; 00249 if (flags1 & 0x08) queryHitsData.flagHaveUploaded = flags2 & 0x08; 00250 if (flags1 & 0x04) queryHitsData.flagBusy = flags2 & 0x04; 00251 if (flags2 & 0x01) queryHitsData.flagPush = flags1 & 0x01; 00252 00253 // See if there's an xmlBlock in the private area: 00254 int xmlLength = 0; 00255 // \todo Changing '\x04' to '4' in any of the test in BinaryReaderTest would 00256 // not result in a failing test! The reason is that we just read the value 00257 // and check >= 4. We do not later verify whether that nuber of bytes has 00258 // been read! Should we run such a check? 00259 if(openAreaSize >= 4) 00260 xmlLength = readUInt16(); 00261 00262 // Read a GgepBlock if one is available: 00263 if(hasGgep) { 00264 while(canRead(1)) { 00265 // \todo Could it contain multiple GGEP blocks?? If yes, couldn't 00266 // we better modify readGgepBlock() to read-and-combine any number 00267 // of subesquent GGEP blocks? 00268 if(lookAhead(1) == GgepBlock::MagicByte) { 00269 // Read the GgepBlock one the MacigByte is found: 00270 queryHitsData.ggepBlock = readGgepBlock(); 00271 break; 00272 } else { 00273 // Just read the unknown private data: 00274 queryHitsData.privateVendor.append (readByte()); 00275 } 00276 } 00277 } 00278 00279 // Read any XML data if available: 00280 if(xmlLength > 0) { 00281 setReadOrigin (ReadFromEnd); 00282 readByte(); 00283 queryHitsData.xmlData = readBytes (xmlLength - 1); 00284 setReadOrigin (ReadFromStart); 00285 } 00286 00287 // Append all data between the GgepBlock and the XML data to privateVendor. 00288 queryHitsData.privateVendor.append (readAll()); 00289 return queryHitsData; 00290 } 00291 00293 00296 HugeGemBlock BinaryReader::readHugeGemBlock (const QByteArray &bytes) 00297 { 00298 HugeGemBlock block; 00299 const int bytesLength = bytes.length(); 00300 // For each '\x1c' separated extension: 00301 for (int pos = 0, nextPos = 0; pos < bytesLength; pos = nextPos + 1) { 00302 nextPos = bytes.indexOf ('\x1c', pos); 00303 // No more '\x1c', or this is a GGEP block, that's the last extension: 00304 if (nextPos < 0 || bytes [pos] == '\xc3') 00305 nextPos = bytesLength; // Assume a '\x1c' terminates bytes. 00306 00307 // The extension consists of all bytes up to but excluding the '\x1c': 00308 QByteArray extension = bytes.mid (pos, nextPos - pos); 00309 if (extension.isEmpty()) 00310 continue; 00311 00312 // The extension is a GGEP block: 00313 if (extension.startsWith ('\xc3')) { 00314 BinaryReader reader (extension); 00315 block.ggepBlock = reader.readGgepBlock(); 00316 // If readGgepBlock() failed, then we got a Null GgepBlock. If not 00317 // all bytes from extension were read, then we just silently ignore 00318 // them. 00319 break; // Last extension, break the loop 00320 } 00321 00322 // Remove trailing null byte if one exists (may happen in a Query): 00323 if (extension.endsWith ('\0')) 00324 extension.chop (1); 00325 00326 if (extension.startsWith ("urn:")) 00327 block.urnList += extension; 00328 // \todo GnutellaProtocol-v0.4-r1.8 A.2.4. says: 00329 // "URLs MAY also be used, but they should only use well-known and 00330 // registered schemes (such as "http:" or "ftp:")" 00331 // else if (extension.startsWith ("http:") || extension.startsWith ("ftp:")) 00332 else if (extension.startsWith ('<') || extension.startsWith ('{')) 00333 block.xmlList += extension; 00334 else 00335 block.unknownList += extension; 00336 } 00337 return block; 00338 }