Ggep.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 "Ggep.h"
00025 #include "Ggeps/UnknownGgep.h"
00026 #include "Ggeps/DailyUptime.h"
00027 #include "Ggeps/Ultrapeer.h"
00028 #include "Ggeps/VendorCode.h"
00029 #include "Ggeps/PackedHostCaches.h"
00030 #include "Ggeps/SupportsCachedPongs.h"
00031 #include "Ggeps/UdpHostCache.h"
00032 #include "Ggeps/IpPort.h"
00033 #include "Utils/Encodings/Cobs.h"
00034 #include "Utils/Encodings/Deflate.h"
00035 
00036 using namespace Gnutella::Packets::Extensions;
00037 
00038 enum Constants
00039 {
00040     MaximalGgepDataLength = 262143
00041 };
00042 
00043 Ggep::Ggep (const GgepId &id, int flags, int dataSize)
00044 {
00045     d = new Data;
00046 
00047     d->ref.init (1);
00048     d->isValid = true;
00049     d->id = id;
00050 
00051     if (flags & LastExtension)  d->flags |= LastExtension;
00052     if (flags & Encoding)       d->flags |= Encoding;
00053     if (flags & Compression)    d->flags |= Compression;
00054     if (flags & Reserved)       d->flags |= Reserved;
00055 
00056     p.dataLength = dataSize;
00057 }
00058 
00059 Ggep::~Ggep()
00060 {
00061     if (!d->ref.deref())
00062         delete d;
00063 }
00064 
00065 Ggep & Ggep::operator= (const Ggep &other)
00066 {
00067     Data *x = other.d;
00068     x->ref.ref();
00069     x = qAtomicSetPtr(&d, x);
00070     if (!x->ref.deref())
00071         delete x;
00072 
00073     p = other.p;
00074     return *this;
00075 }
00076 
00077 void Ggep::invalidateData()
00078 {
00079     if (d->ref != 1) {
00080         Data *x = new Data;
00081         x->ref.init (1);
00082 
00083         x->isValid              = d->isValid;
00084         x->flags                = d->flags;
00085         x->id                   = d->id;
00086 
00087         x = qAtomicSetPtr (&d, x);
00088         if (!x->ref.deref())
00089             delete x;
00090     };
00091 }
00092 
00093 Ggep * Ggep::fromId (const GgepId &id, int flags, int dataSize)
00094 {
00095     if (id == Ggeps::Ultrapeer::Id)
00096         return new Ggeps::Ultrapeer (id, flags, dataSize);
00097 
00098     if (id == Ggeps::VendorCode::Id)
00099         return new Ggeps::VendorCode (id, flags, dataSize);
00100 
00101     if (id == Ggeps::DailyUptime::Id)
00102         return new Ggeps::DailyUptime (id, flags, dataSize);
00103 
00104     if (id == Ggeps::PackedHostCaches::Id)
00105         return new Ggeps::PackedHostCaches (id, flags, dataSize);
00106 
00107     if (id == Ggeps::SupportsCachedPongs::Id)
00108         return new Ggeps::SupportsCachedPongs (id, flags, dataSize);
00109 
00110     if (id == Ggeps::UdpHostCache::Id)
00111         return new Ggeps::UdpHostCache (id, flags, dataSize);
00112 
00113     if (id == Ggeps::IpPort::Id)
00114         return new Ggeps::IpPort (id, flags, dataSize);
00115 
00116     return new Ggeps::UnknownGgep (id, flags, dataSize);
00117 }
00118 
00119 void Ggep::prepareRead (const QByteArray &rawExtensionData)
00120 {
00121     invalidateData();
00133     if (isFlagSet (Compression) || isFlagSet (Encoding))
00134         d->isValid = true;
00135     else {
00136         d->isValid = prepareReadData (rawExtensionData);
00137     }
00138 }
00139 
00140 void Ggep::read (QDataStream &in)
00141 {
00142     invalidateData(); // \todo prepareRead must have been called before that, but how to assert it?
00143 
00144     uchar temp;
00145     in >> temp; // Flags
00146     for (int i = id().size(); i > 0 ; i--)
00147         in >> temp; // ID
00148 
00149     do {
00150         in >> temp; // dataSize;
00151     } while ((temp & 0x40) != 0x40);
00152 
00153     QByteArray decodedData;
00154 
00155     if (isFlagSet (Encoding)) { // Decode data (COBS).
00156         QByteArray rawData (p.dataLength, 0);
00157         in.readRawData (rawData.data(), p.dataLength);
00158 
00159         decodedData = Utils::Encodings::Cobs::decode (rawData);
00160     }
00161     if (isFlagSet (Compression)) { // Decompress data (inflate).
00162         QByteArray rawData;
00163         if (decodedData.length() > 0) {
00164             rawData = decodedData;
00165         } else {
00166             rawData.reserve (p.dataLength);
00167             in.readRawData (rawData.data(), p.dataLength);
00168         }
00169         decodedData = Utils::Encodings::Deflate::unCompress (rawData);
00170     }
00171 
00172     if (decodedData.size() > 0) {
00173         QDataStream decodedInStream (decodedData);
00174         p.dataLength = decodedData.length();
00175         // Now perform the delayed prepare (see prepareRead() for details).
00176         d->isValid = prepareReadData (decodedData);
00177         // Try to read data only if structure is valid.
00178         if (d->isValid)
00179             readData (decodedInStream);
00180     } else {
00181         // Try to read data only if structure is valid.
00182         if (d->isValid)
00183             readData (in);
00184         else {
00185             // Even if structure is invalid, make sure the stream pointer is
00186             // advanced correctly.
00187             QByteArray rawData (p.dataLength, 0);
00188             in.readRawData (rawData.data(), p.dataLength);
00189         }
00190     }
00191 }
00192 
00193 int Ggep::prepareWrite() const
00194 {
00195     // \todo It may happen that two objects sharing the same data try to
00196     // change the mutable members, which are actually of importance in between
00197     // the calls of prepareWrite and write().
00198     p.dataLength = prepareWriteData();
00199 
00200     if (isFlagSet (Compression)) {
00201         QByteArray rawData (p.dataLength, 0);
00202         QDataStream unencodedOutStream (rawData);
00203         writeData (unencodedOutStream);
00204         p.encodedData = Utils::Encodings::Deflate::compress (rawData);
00205     }
00206     if (isFlagSet (Encoding)) {
00207         QByteArray rawData;
00208         if (p.encodedData.length() > 0) {
00209             rawData = p.encodedData;
00210         } else {
00211             rawData.resize (p.dataLength);
00212             QDataStream unencodedOutStream (rawData);
00213             writeData (unencodedOutStream);
00214         }
00215         p.encodedData = Utils::Encodings::Cobs::encode (rawData);
00216     }
00217     int encodedDataLength = 0;
00218     if (p.encodedData.length() > 0) {
00219         encodedDataLength = p.encodedData.length();
00220     } else {
00221         encodedDataLength = p.dataLength;
00222     }
00223 
00224     // Validate the condition for the size of the extension data
00225     Q_ASSERT (encodedDataLength >= 0 && encodedDataLength <= MaximalGgepDataLength);
00226 
00227     int totalLength = 1 + d->id.length() + encodedDataLength;
00228 
00229     // We need to calculate the length of the data length field in the header.
00230     do {
00231         totalLength++;
00232         encodedDataLength >>= 6;
00233     } while (encodedDataLength > 0);
00234 
00235     return totalLength;
00236 }
00237 
00238 void Ggep::write (QDataStream &out) const
00239 {
00240     out << static_cast <uchar> (static_cast <int> (d->flags) | id().size());
00241 
00242     for (int i = 0; i < id().size(); i++)
00243         out << static_cast <uchar> (d->id[i]);
00244 
00245     int length = p.dataLength;
00246     if (isFlagSet (Compression) || isFlagSet (Encoding)) {
00247         length = p.encodedData.length();
00248     }
00249     do {
00250         uchar byte = length & 0x3F;
00251         length >>= 6;
00252         byte |= (length > 0) ? 0x80 : 0x40;
00253         out << byte;
00254     } while (length > 0);
00255 
00256     if (isFlagSet (Compression) || isFlagSet (Encoding)) {
00257         out.writeRawData (p.encodedData.data(), p.encodedData.length());
00258         p.encodedData = QByteArray();   // Free allocated memory.
00259     } else {
00260         writeData (out);
00261     }
00262 }
00263 
00264 
00265 /*
00266 int Ggep::prepareWrite() const
00267 {
00268     // If the same extension was copied into more packets, none of which
00269     // changed, than there is no need to build the raw data many times.
00270     // We store rawData in the data shared wmong the extensions.
00271     if (!d->rebuildData)
00272         return d->rawData.length();
00273 
00274     dataLength = prepareWriteData();
00275     QByteArray rawData (dataLength, 0);
00276     {   // Let the stream get quickly out of scope;
00277         QDataStream unencodedOutStream (rawData);
00278         writeData (unencodedOutStream);
00279     }
00280 
00281     if (isFlagSet (Compression))
00282         rawData = Utils::Encodings::Deflate::compress (rawData);
00283     if (isFlagSet (Encoding))
00284         rawData = Utils::Encodings::Cobs::encode (rawData);
00285 
00286     dataLength = rawData.length(); // The length after COBS and "deflate"
00287     // Validate the condition for the size of the extension data
00288     Q_ASSERT (dataLength > 0 && dataLength <= MaximalGgepDataLength);
00289 
00290     int totalLength = 1 + id_.length() + dataLength;
00291 
00292     // We need to calculate the length of the data length field in the header.
00293     for (; dataLength > 0; dataLength >>= 6)
00294         totalLength++;
00295 
00296     // Now write the extension data in the private member.
00297     d->rawData.resize (totalLength);
00298     QDataStream out (d->rawData);
00299 
00300     out << static_cast <uchar> ((int) flags_);
00301 
00302     for (int i = 0; i < id().size(); i++)
00303         out << static_cast <uchar> (id_[i]);
00304 
00305     do {
00306         uchar byte = length & 0x3F;
00307         length >>= 6;
00308         byte |= (length > 0) ? 0x80 : 0x40;
00309         out << byte;
00310     } while (length > 0);
00311 
00312     out.writeRawData (rawData.data(), rawData.length());
00313     Q_ASSERT (d->rawData.length() == totalLength);
00314 
00315     return d->rawData.length();
00316 }
00317 
00318 void Ggep::write (QDataStream &out) const
00319 {
00320     out.writeRawData (d->rawData.data(), d->rawData.length());
00321     // Eventually free the rawData buffer.
00322 }
00323 */