/** * Copyright (C) 2004-2005 Alo Sarv <madcat_@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** \file hasher.cpp Implementation of Files Identification Subsystem */ // Precompiled header #include <hn/hnprec.h> #include <hn/hasher.h> #include <hn/hashsetmaker.h> #include <hn/md4transform.h> #include <hn/md5transform.h> #include <hn/sha1transform.h> #include <hn/metadata.h> #include <boost/filesystem/path.hpp> #include <boost/lexical_cast.hpp> #include <boost/progress.hpp> #include <fstream> using boost::filesystem::path; using boost::filesystem::no_check; using namespace CGComm; uint64_t HashWork::s_dataCnt = 0; double HashWork::s_timeCnt = 0.0; boost::mutex HashWork::s_statsLock; static uint32_t s_bufSize = 32*1024; static uint32_t s_sleepTime = 0; static boost::shared_ptr<boost::progress_display> s_prog; IMPLEMENT_EVENT_TABLE(HashWork, boost::shared_ptr<HashWork>, HashEvent); // Full hash job Constructor HashWork::HashWork(const std::string &filename) : m_filename(filename), m_md(), m_begin(), m_end(), m_ref(), m_valid(true), m_full(true), m_inProgress() {} // Range hash verification job HashWork::HashWork( const std::string &filename, uint64_t begin, uint64_t end, const HashBase* ref ) : m_filename(filename), m_md(), m_begin(begin), m_end(end), m_ref(ref), m_valid(true), m_full(false), m_inProgress() { CHECK_THROW(ref); } HashWork::~HashWork() {} bool HashWork::process() { if (!m_inProgress) { initState(); } CHECK_THROW(!isComplete()); Utils::StopWatch s1; doProcess(); if (s_sleepTime) { boost::xtime xt; boost::xtime_get(&xt, boost::TIME_UTC); xt.nsec += s_sleepTime; boost::thread::sleep(xt); boost::mutex::scoped_lock l(s_statsLock); } s_timeCnt += s1.elapsed(); return isComplete(); } void HashWork::initState() { m_file.reset(new std::ifstream(m_filename.c_str(), std::ios::in)); if (!*m_file) { getEventTable().postEvent(shared_from_this(), HASH_FATAL_ERROR); throw std::runtime_error( (boost::format("Unable to open file `%s' for hashing.") % m_filename).str() ); } logMsg(boost::format("Hashing file `%s'") % m_filename); boost::shared_ptr<HashSetMaker> t; if (isFull()) { t.reset(new ED2KHashMaker); m_makers.push_back(t); t.reset(new SHA1HashMaker); m_makers.push_back(t); t.reset(new MD4HashMaker); m_makers.push_back(t); t.reset(new MD5HashMaker); m_makers.push_back(t); m_begin = 0; m_end = Utils::getFileSize(m_filename); s_prog.reset(new boost::progress_display(m_end)); } else { switch (getType()) { case OP_HT_MD4: t.reset(new MD4HashMaker); break; case OP_HT_MD5: t.reset(new MD5HashMaker); break; case OP_HT_ED2K: t.reset(new ED2KHashMaker); break; case OP_HT_SHA1: t.reset(new SHA1HashMaker); break; default: boost::format fmt( "Requested unknown hash of type %s" ); logError(fmt % m_ref->getType()); break; } m_makers.push_back(t); m_file->seekg(m_begin); s_prog.reset(new boost::progress_display(m_end-m_begin+1)); } m_buf.reset(new char[s_bufSize]); m_inProgress = true; } void HashWork::doProcess() { if (s_bufSize + static_cast<uint64_t>(m_file->tellg()) > m_end) { m_file->read(m_buf.get(), m_end - m_file->tellg() + 1); } else { m_file->read(m_buf.get(), s_bufSize); } for (uint32_t i = 0; i < m_makers.size(); ++i) { m_makers[i]->sumUp(m_buf.get(), m_file->gcount()); } boost::mutex::scoped_lock l(s_statsLock); s_dataCnt += m_file->gcount(); *s_prog += m_file->gcount(); if (!*m_file || m_end + 1 == static_cast<uint64_t>(m_file->tellg())) { finish(); } } void HashWork::finish() { if (isFull()) { CHECK_THROW(m_makers.size()); m_md = new MetaData(Utils::getFileSize(m_filename)); for (uint32_t i = 0; i < m_makers.size(); ++i) { m_md->addHashSet(m_makers[i]->getHashSet()); } m_md->setModDate(Utils::getModDate(m_filename)); m_md->addFileName(path(m_filename, no_check).leaf()); getEventTable().postEvent(shared_from_this(), HASH_COMPLETE); } else { CHECK_THROW(m_makers.size() == 1); const HashBase &h = m_makers[0]->getHashSet()->getFileHash(); HashEvent evt(h == *m_ref ? HASH_VERIFIED : HASH_FAILED); getEventTable().postEvent(shared_from_this(), evt); } setComplete(); m_file.reset(); s_prog.reset(); }