hash.cpp

Go to the documentation of this file.
00001 /**
00002  *  Copyright (C) 2004-2005 Alo Sarv <madcat_@users.sourceforge.net>
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017  */
00018 
00019 /** @file hash.cpp Implementation of Hash classes */
00020 
00021 #include <hn/hnprec.h>
00022 #include <hn/hash.h>
00023 
00024 namespace CGComm {
00025 
00026 HashBase* loadHash(std::istream &i) {
00027         uint8_t type = Utils::getVal<uint8_t>(i);
00028         switch (type) {
00029                 case OP_HT_MD4:
00030                         return new Hash<MD4Hash>(i);
00031                 case OP_HT_MD5:
00032                         return new Hash<MD5Hash>(i);
00033                 case OP_HT_SHA1:
00034                         return new Hash<SHA1Hash>(i);
00035                 case OP_HT_ED2K:
00036                         return new Hash<ED2KHash>(i);
00037                 default:
00038                         throw std::runtime_error(
00039                                 (boost::format("Requested creation of "
00040                                 "unsupported Hash type %s")
00041                                 % Utils::hexDump(type)).str()
00042                         );
00043                 }
00044         return 0;
00045 }
00046 
00047 inline void invalidType(uint8_t x, uint8_t y) {
00048         boost::format fmt(
00049                 "Unknown HashSet type found in stream: "
00050                 "FileHashType: %s PartHashType: %s"
00051         );
00052         fmt % Utils::hexDump(x) % Utils::hexDump(y);
00053         throw std::runtime_error(fmt.str());
00054 }
00055 
00056 //! Create a specific object based on information found from stream.
00057 //! \todo This must be one the ugliest constructs I'v ever seen ... it
00058 //! _probably_ could be done in a much nicer way than nested duplicating switch
00059 //! statements...
00060 HashSetBase* loadHashSet(std::istream &i) {
00061         logTrace(TRACE_HASH, "Creating new HashSet object.");
00062         // Types
00063         uint8_t parthashtype = Utils::getVal<uint8_t>(i);
00064         uint8_t filehashtype = Utils::getVal<uint8_t>(i);
00065         logTrace(TRACE_HASH,
00066                 boost::format("FileHashType: %s PartHashType: %s")
00067                 % Utils::hexDump(filehashtype)
00068                 % Utils::hexDump(parthashtype)
00069         );
00070         // Special case - ED2KHashSet is defined with template-parameter
00071         // partsize (9728000), thus we must specifically construct it here
00072         // out of the ordinary, otherwise we will get a similar type, however,
00073         // typecasts between ED2KHashSet and whatever we create here wouldn't
00074         // work anymore. Thus - make this exception.
00075         // Note: Similar exceptions might be needed for other kinds of hashsets
00076         //       we will be supporting in the future which also have static
00077         //       chunk size. So add those here too, and use the below big
00078         //       switch/case statement only on last resort.
00079         if (filehashtype == OP_HT_ED2K && parthashtype == OP_HT_MD4) {
00080                 return new ED2KHashSet(i);
00081         }
00082         switch (filehashtype) {
00083         case OP_HT_MD4:
00084                 switch (parthashtype) {
00085                         case OP_HT_MD4:
00086                                 return new HashSet<MD4Hash, MD4Hash>(i);
00087                         case OP_HT_MD5:
00088                                 return new HashSet<MD5Hash, MD4Hash>(i);
00089                         case OP_HT_SHA1:
00090                                 return new HashSet<SHA1Hash, MD4Hash>(i);
00091                         default:
00092                                 invalidType(filehashtype, parthashtype);
00093                 }
00094         case OP_HT_MD5:
00095                 switch (parthashtype) {
00096                         case OP_HT_MD4:
00097                                 return new HashSet<MD4Hash, MD5Hash>(i);
00098                         case OP_HT_MD5:
00099                                 return new HashSet<MD5Hash, MD5Hash>(i);
00100                         case OP_HT_SHA1:
00101                                 return new HashSet<SHA1Hash, MD5Hash>(i);
00102                         default:
00103                                 invalidType(filehashtype, parthashtype);
00104                 }
00105         case OP_HT_SHA1:
00106                 switch (parthashtype) {
00107                         case OP_HT_MD4:
00108                                 return new HashSet<MD4Hash, SHA1Hash>(i);
00109                         case OP_HT_MD5:
00110                                 return new HashSet<MD5Hash, SHA1Hash>(i);
00111                         case OP_HT_SHA1:
00112                                 return new HashSet<SHA1Hash, SHA1Hash>(i);
00113                         default:
00114                                 invalidType(filehashtype, parthashtype);
00115                 }
00116         case OP_HT_ED2K:
00117                 switch (parthashtype) {
00118                         case OP_HT_MD4:
00119                                 return new HashSet<MD4Hash, ED2KHash>(i);
00120                         case OP_HT_MD5:
00121                                 return new HashSet<MD5Hash, ED2KHash>(i);
00122                         case OP_HT_SHA1:
00123                                 return new HashSet<SHA1Hash, ED2KHash>(i);
00124                         case OP_HT_ED2K:
00125                                 return new HashSet<ED2KHash, ED2KHash>(i);
00126                         default:
00127                                 invalidType(filehashtype, parthashtype);
00128                 }
00129         default:
00130                 invalidType(filehashtype, parthashtype);
00131         }
00132         return 0;
00133 }
00134 
00135 } //! end CGComm namespace
00136 
00137 using namespace CGComm;
00138 
00139 // HashBase
00140 // --------
00141 HashBase::HashBase() {}
00142 HashBase::~HashBase() {}
00143 
00144 std::ostream& operator<<(std::ostream &o, const HashBase &h) {
00145         Utils::putVal<uint8_t>(o, OP_HASH);
00146         Utils::putVal<uint16_t>(o, h.size()+1);
00147         Utils::putVal<uint8_t>(o, h.getTypeId());
00148         Utils::putVal(o, h.getData(), h.size());
00149         return o;
00150 }
00151 
00152 /**
00153  * HashSet object format:
00154  *
00155  * <uint8>OP_HASHSET<uint16>objlength<uint8>filehashtype<uint8>parthashtype
00156  * <uint16>tagcount[<tagcount>*Tag]
00157  */
00158 std::ostream& operator<<(std::ostream &o, const HashSetBase &h) {
00159         Utils::putVal<uint8_t>(o, OP_HASHSET);
00160         uint16_t tagcount = 0;
00161         std::ostringstream str;
00162         if (!h.getFileHash().isEmpty()) {
00163                 Utils::putVal<uint8_t>(str, OP_HS_FILEHASH);
00164                 std::ostringstream tmp;
00165                 tmp << h.getFileHash();
00166                 Utils::putVal<uint16_t>(str, tmp.str().size());
00167                 Utils::putVal(str, tmp.str(), tmp.str().size());
00168                 ++tagcount;
00169         }
00170         for (uint32_t i = 0; i < h.getChunkCnt(); ++i) {
00171                 Utils::putVal<uint8_t>(str, OP_HS_PARTHASH);
00172                 std::ostringstream tmp;
00173                 tmp << h.getChunkHash(i);
00174                 Utils::putVal<uint16_t>(str, tmp.str().size());
00175                 Utils::putVal(str, tmp.str(), tmp.str().size());
00176                 ++tagcount;
00177         }
00178         if (h.getChunkSize()) {
00179                 Utils::putVal<uint8_t>(str, OP_HS_PARTSIZE);
00180                 Utils::putVal<uint16_t>(str, 4);
00181                 Utils::putVal<uint32_t>(str, h.getChunkSize());
00182                 ++tagcount;
00183         }
00184         // +4 -> <uint16>tagcount<uint8>filehashtype<uint8>parthashtype
00185         Utils::putVal<uint16_t>(o, str.str().size() + 4);
00186         Utils::putVal<uint8_t>(o, h.getChunkHashTypeId());
00187         Utils::putVal<uint8_t>(o, h.getFileHashTypeId());
00188         Utils::putVal<uint16_t>(o, tagcount);
00189         Utils::putVal(o, str.str(), str.str().size());
00190         return o;
00191 }
00192 
00193 // Compare us to ref, returning true if 100% matches
00194 bool HashSetBase::compare(const HashSetBase &ref) const {
00195         logTrace(TRACE_HASH, "Comparing two hashsets.");
00196         if (getFileHashTypeId() != ref.getFileHashTypeId()) {
00197                 logTrace(TRACE_HASH,
00198                         boost::format("FileHashType %s != %s")
00199                         % getFileHashTypeId() % ref.getFileHashTypeId()
00200                 );
00201                 return false;
00202         }
00203         if (getChunkHashTypeId() != ref.getChunkHashTypeId()) {
00204                 logTrace(TRACE_HASH,
00205                         boost::format("PartHashType %s != %s")
00206                         % getChunkHashTypeId() % ref.getChunkHashTypeId()
00207                 );
00208                 return false;
00209         }
00210         if (getFileHash() != ref.getFileHash()) {
00211                 logTrace(TRACE_HASH,
00212                         boost::format("FileHash %s != %s")
00213                         % getFileHash().decode() % ref.getFileHash().decode()
00214                 );
00215                 return false;
00216         }
00217         if (getChunkCnt() != ref.getChunkCnt()) {
00218                 logTrace(TRACE_HASH,
00219                         boost::format("PartHashCount %s != %s")
00220                         % getChunkCnt() % ref.getChunkCnt()
00221                 );
00222                 return false;
00223         }
00224         for (uint32_t i = 0; i < getChunkCnt(); ++i) {
00225                 if (getChunkHash(i) != ref.getChunkHash(i)) {
00226                         logTrace(TRACE_HASH,
00227                                 boost::format("PartHash[%d] %s != %s")
00228                                 % i % getChunkHash(i).decode()
00229                                 % ref.getChunkHash(i).decode()
00230                         );
00231                         return false;
00232                 }
00233         }
00234         logTrace(TRACE_HASH, "Hashsets are equal.");
00235         return true;
00236 }
00237