GgepBlock.cpp

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 2005-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 "GgepBlock.h"
00025 #include "Ggep.h"
00026 
00027 using namespace Gnutella::Packets::Extensions;
00028 
00029 GgepBlock::GgepBlock()
00030  :  Extension(), extensions_(), rawBlockLength_()
00031 {
00032     setIsValid (true);
00033 }
00034 
00035 GgepBlock::GgepBlock (const GgepBlock &block)
00036  :  Extension(), extensions_(), rawBlockLength_()
00037 {
00038     foreach (const Ggep *extension, block.extensions_)
00039         addExtension (*extension);
00040 }
00041 
00042 GgepBlock * GgepBlock::copy() const
00043 {
00044     return new GgepBlock (*this);
00045 }
00046 
00047 GgepBlock& GgepBlock::operator= (const GgepBlock &block)
00048 {
00049     if (this == &block)
00050         return *this;
00051 
00052     foreach (const Ggep *extension, extensions_)
00053         delete extension;
00054 
00055     extensions_.clear();
00056     foreach (const Ggep *extension, block.extensions_)
00057         addExtension (*extension);
00058 
00059     return *this;
00060 }
00061 
00062 GgepBlock::~GgepBlock()
00063 {
00064     deleteExtensions();
00065 }
00066 
00067 void GgepBlock::deleteExtensions()
00068 {
00069     foreach (const Ggep *extension, extensions_)
00070         delete extension;
00071     extensions_.clear();
00072 }
00073 
00074 int GgepBlock::prepareRead (const QByteArray &rawData)
00075 {
00076     deleteExtensions();
00077 
00078     setIsValid (false);
00079     int rawDataSize = rawData.size();
00080     rawBlockLength_ = rawDataSize;
00081 
00082      // Minimal size is 1 (MagicByte) + 1 (Flags) + 1 (ID) + 1 (Data Length)
00083     if (rawDataSize < 4)
00084         return 0;
00085 
00086     int currentPos = 0;
00087     if (rawData[currentPos++] != MagicByte)
00088         return 0;
00089 
00090     bool isLastExtension = false;
00091     while (!isLastExtension) {
00092         // Read the extension header.
00093         uchar flags = rawData[currentPos++];
00094         isLastExtension = flags & Ggep::LastExtension;
00095         int idLength = flags & Ggep::IDLength;
00096 
00097         // Check minimal required rawData size. Still need to read the ID and at least 1 size byte.
00098         if (currentPos + idLength + 1 > rawDataSize)
00099             return 0;
00100 
00101         Ggep::GgepId id (rawData.data() + currentPos, idLength);
00102         currentPos += idLength;
00103 
00104         int dataLength = 0;
00105         bool readMore = false;
00106         int bytesRead = 0;
00107         do {
00108             char byte = rawData[currentPos++];
00109             dataLength = (dataLength << 6) | (byte & 0x3F);
00110             readMore = (byte & 0x40) != 0x40;
00111             bytesRead++;
00112         } while (readMore && bytesRead < 3 && currentPos < rawDataSize);
00113         // Could not finish reading the data length:
00114         if (readMore)
00115             return 0;
00116         // Less than dataLength bytes left to read:
00117         if (currentPos + dataLength > rawDataSize)
00118             return 0;
00119 
00120         if (currentPos + dataLength == rawDataSize)
00121             isLastExtension = true;
00122 
00123         currentPos += dataLength; // Move pointer to the first byte of the next extension.
00124 
00125         Ggep *extension = Ggep::fromId (id, flags, dataLength);
00126         Q_ASSERT (extension != 0);
00127         extensions_.append (extension);
00128 
00129         // Ggep::prepareRead() requires the raw extension data, WITHOUT header.
00130         // We checked the header already.
00131         // \todo Maybe do the header parsing again in Ggep, or ONLY there?
00132         int rawExtensionDataStart = currentPos - dataLength;
00133         int rawExtensionDataLength = currentPos - rawExtensionDataStart;
00134         QByteArray extensionData = QByteArray::fromRawData (rawData.data() + rawExtensionDataStart, rawExtensionDataLength);
00135         // \todo Should we mark the block as invalid is a single extension is invalid, aka. prepareRead fails?
00136         extension->prepareRead (extensionData);
00137     }
00138     setIsValid (true);
00139     return currentPos;
00140 }
00141 
00142 void GgepBlock::read (QDataStream &stream)
00143 {
00144     // For packets, which may have optional GGEP blocks somewhere in the middle
00145     // of the payload it is important to advance the stream pointer with a number
00146     // of bytes exactly equal to the expected GGEP block size. That also needs to
00147     // be done in the case when the GGEP block is invalid or is missing.
00148     if (!isValid()) {
00149         deleteExtensions();
00150         QByteArray buffer;
00151         buffer.resize (rawBlockLength_); // Missing GGEP block would mean zero length.
00152         stream.readRawData (buffer.data(), rawBlockLength_);
00153         return;
00154     }
00155 
00156     uchar temp;
00157     stream >> temp; // MagicByte
00158 
00159     Q_ASSERT (temp == uchar (MagicByte));
00160 
00161     /*
00162         We store const Ggep * although we may require non-const access
00163         to the objects, like in this case. We require an ugly cast here, but ease
00164         the implementation and increase the performance of extensions(). Otherwise,
00165         we sould either keep two lists (one const and one non-const), or would have
00166         to create a new const one each time extensions() is called.
00167     */
00168     Ggep *extension = 0;
00169     foreach (const Ggep *constExtension, extensions_) {
00170         extension = const_cast <Ggep *> (constExtension);
00171         extension->read (stream);
00172     }
00173 }
00174 
00175 int GgepBlock::prepareWrite() const
00176 {
00177     if (extensions_.count() == 0)
00178         return 0;
00179 
00180     int writeBytesCount = 1; // MagicByte.
00181 
00182     foreach (const Ggep * extension, extensions_) {
00183         writeBytesCount += extension->prepareWrite();
00184     }
00185     return writeBytesCount;
00186 }
00187 
00188 void GgepBlock::write (QDataStream &stream) const
00189 {
00190     if (extensions_.count() == 0)
00191         return;
00192 
00193     stream << static_cast <uchar> (MagicByte);
00194 
00195     foreach (const Ggep * extension, extensions_) {
00196         extension->write (stream);
00197     }
00198 }
00199 
00200 void GgepBlock::addExtension (const Ggep &extension)
00201 {
00202     Ggep *newExtension = extension.copy();
00203     newExtension->setFlag (Ggep::LastExtension);
00204 
00205     if (extensions_.size() > 0) {
00206         Ggep *oldLastExtension = const_cast <Ggep *> (extensions_.last());
00207         oldLastExtension->resetFlag (Ggep::LastExtension);
00208     }
00209     extensions_.append (newExtension);
00210 }
00211 
00212 GgepBlock::Extensions GgepBlock::extensions() const
00213 {
00214     return extensions_;
00215 }