BinaryReader.cpp

Go to the documentation of this file.
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 }