ed2k.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 ed2k.cpp Implementation of ED2K Module entry point */
00020 
00021 // basic stuff that we need for ourselves, or for init'ing them
00022 #include <hn/hnprec.h>
00023 #include "ed2k.h"
00024 #include "serverlist.h"
00025 #include "clientlist.h"
00026 #include "creditsdb.h"
00027 #include "downloadlist.h"
00028 #include <hn/log.h>
00029 
00030 // for config location
00031 #include <hn/hydranode.h>
00032 
00033 // need to perform operations on config files etc
00034 #include <boost/filesystem/operations.hpp>
00035 #include <boost/filesystem/exception.hpp>
00036 
00037 // random number generator
00038 #include <boost/random/mersenne_twister.hpp>
00039 
00040 // these are used for starting downloads from link
00041 #include <boost/tokenizer.hpp>
00042 #include <hn/metadb.h>
00043 #include <hn/fileslist.h>
00044 #include <hn/sharedfile.h>
00045 #include <hn/schedbase.h>
00046 #include <hn/metadata.h>
00047 
00048 IMPLEMENT_MODULE(ED2K);
00049 
00050 bool ED2K::onInit() {
00051         m_configDir = HydraNode::instance().getConfigDir()/"ed2k";
00052         logMsg(
00053                 boost::format("ED2K configuration directory: %s") %
00054                 m_configDir.native_directory_string()
00055         );
00056         m_id = 0;
00057 
00058         using namespace boost::filesystem;
00059 
00060         if (!exists(m_configDir)) try {
00061                 create_directory(m_configDir);
00062         } catch (filesystem_error &err) {
00063                 logError(
00064                         boost::format(
00065                                 "Unable to create folder for "
00066                                 "ed2k configuration: %s"
00067                         ) % err.what()
00068                 );
00069                 logError("Ed2k configuration settings will not be saved.");
00070         }
00071         if (!is_directory(m_configDir)) {
00072                 logError("Nondirectory is blocking ed2k configuration ");
00073                 logError(boost::format("at %s") % m_configDir.string());
00074                 logError("ed2k configuration settings will not be saved.");
00075         }
00076 
00077         // Bring up data structures
00078         CreditsDb::instance().initCrypting();
00079         ED2KConfig::instance().load((m_configDir/"ed2k.ini").string());
00080         ServerList::instance().load((m_configDir/"server.met").string());
00081         CreditsDb::instance().load((m_configDir/"clients.met").string());
00082 
00083         // Initialize userhash
00084         std::string hash(
00085                 ED2KConfig::instance().read<std::string>("UserHash", "")
00086         );
00087         if (hash == "") {
00088                 m_userHash = createUserHash();
00089                 ED2KConfig::instance().write("UserHash", m_userHash.decode());
00090         } else {
00091                 m_userHash = Utils::encode(hash);
00092         }
00093         if (m_userHash.getData()[5] != 14 || m_userHash.getData()[14] != 111) {
00094                 // First versions of HN didn't create eMule-type hash - recreate
00095                 logMsg("Re-creating eMule-compatible userhash.");
00096                 m_userHash = createUserHash();
00097                 ED2KConfig::instance().write("UserHash", m_userHash.decode());
00098         }
00099         logDebug(boost::format("Own user hash: %s") % m_userHash.decode());
00100 
00101         // Set nickname
00102         std::string nick = ED2KConfig::instance().read<std::string>("Nick", "");
00103         if (nick.empty()) {
00104                 char *usr = getenv("USER");
00105                 if (usr == 0) {
00106                         nick = "http://hydranode.com";
00107                 } else {
00108                         nick = usr;
00109                         nick += " testing HydraNode core";
00110                 }
00111         }
00112         setNick(nick);
00113 
00114         m_tcpPort = ED2KConfig::instance().read("TCP Port", 4663);
00115         m_udpPort = ED2KConfig::instance().read("UDP Port", 4673);
00116 
00117         uint32_t upLimit = SchedBase::instance().getUpLimit();
00118         uint32_t downLimit = SchedBase::instance().getDownLimit();
00119         boost::format fmt(
00120                 "Lowering download speed limit to %s/s due "
00121                 "to ED2K network netiquette."
00122         );
00123         if (upLimit < 10 * 1024 && downLimit > upLimit * 4) {
00124                 SchedBase::instance().setDownLimit(upLimit * 4);
00125                 logMsg(fmt % Utils::bytesToString(upLimit * 4));
00126         } else if (upLimit < 4 * 1024 && downLimit > upLimit * 3) {
00127                 logMsg(fmt % Utils::bytesToString(upLimit * 3));
00128                 SchedBase::instance().setDownLimit(upLimit * 3);
00129         }
00130 
00131         // Initialize
00132         DownloadList::instance().init();
00133         ClientList::instance().init();
00134         ServerList::instance().init();
00135 
00136         // Finalize
00137         Search::addLinkHandler(boost::bind(&ED2K::linkHandler, this, _1));
00138 
00139         return true;
00140 }
00141 
00142 int ED2K::onExit() {
00143         ED2KConfig::instance().write("TCP Port", m_tcpPort);
00144         ED2KConfig::instance().write("UDP Port", m_udpPort);
00145         ED2KConfig::instance().save();
00146         ServerList::instance().save((m_configDir/"server.met").string());
00147         CreditsDb::instance().save((m_configDir/"clients.met").string());
00148 
00149         ClientList::instance().exit();
00150         DownloadList::instance().exit();
00151         ServerList::instance().exit();
00152 
00153         return 0;
00154 }
00155 
00156 bool ED2K::linkHandler(const std::string &link) try {
00157         typedef boost::char_separator<char> separator;
00158         separator sep("|");
00159         boost::tokenizer<separator> tok(link, sep);
00160 
00161         boost::tokenizer<separator>::iterator tok_iter = tok.begin();
00162 
00163         if (tok_iter == tok.end() || *tok_iter++ != "ed2k://") {
00164                 return false;
00165         }
00166         if (tok_iter == tok.end() || *tok_iter++ != "file") {
00167                 return false;
00168         }
00169         if (tok_iter == tok.end()) {
00170                 logError("Incomplete ed2k:// link: Missing filename.");
00171                 return false;
00172         }
00173 
00174         std::string name = *tok_iter++;
00175 
00176         if (tok_iter == tok.end()) {
00177                 logError("Incomplete ed2k:// link: Missing filesize.");
00178                 return false;
00179         }
00180 
00181         uint32_t size = boost::lexical_cast<uint32_t>(*tok_iter++);
00182 
00183         if (tok_iter == tok.end()) {
00184                 logError("Incomplete ed2k:// link: Missing hash.");
00185                 return false;
00186         }
00187         Hash<ED2KHash> hash(Utils::encode(*tok_iter++));
00188 
00189         SharedFile *sf = MetaDb::instance().findSharedFile(hash);
00190         if (sf) {
00191                 std::string msg;
00192                 if (sf->isPartial()) {
00193                         msg += "You are already trying to download ";
00194                 } else {
00195                         msg += "You alread have file ";
00196                 }
00197                 msg += sf->getName();
00198                 logMsg(msg);
00199                 return false;
00200         }
00201         MetaData *md = new MetaData(size);
00202         md->addFileName(name);
00203         ED2KHashSet *hs = new ED2KHashSet(hash);
00204         md->addHashSet(hs);
00205         MetaDb::instance().push(md);
00206         FilesList::instance().createDownload(name, md);
00207         return true;
00208 } catch (boost::bad_lexical_cast &) {
00209         logError("Error parsing ed2k:// link - wrong data type detected.");
00210         return false;
00211 } catch (std::exception &e) {
00212         logError(boost::format("Error parsing ed2k:// link: %s") % e.what());
00213         return false;
00214 }
00215 MSVC_ONLY(;)
00216 
00217 // Creates new userhash to be used in ed2k network. Note that hash[5] = 14 &&
00218 // hash[14] = 111 is used by emule to identify emule clients, we shouldn't use
00219 // that.
00220 std::string ED2K::createUserHash() const {
00221         logDebug("Creating new eDonkey2000 userhash.");
00222 
00223         boost::mt19937 rng;
00224         char tmp[16];
00225         for (uint8_t i = 0; i < 4; ++i) {
00226                 uint32_t r = rng();
00227                 memcpy(&tmp[i*4], reinterpret_cast<char*>(&r), 4);
00228         }
00229         // Mark us as eMule client (needed to be able to use eMule
00230         // extended protocol features)
00231         tmp[ 5] =  14;
00232         tmp[14] = 111;
00233         return std::string(tmp, 16);
00234 }
00235 
00236 ED2KConfig::ED2KConfig() : Object(&ED2K::instance(), "config") {}
00237 ED2KConfig::~ED2KConfig() {}