Add main page for Doxygen generated documentation.
[tairent.git] / src / main / torrentmanager.cpp
blob08640b51c38234b92e2024336498c5a8435f8a43
1 /***************************************************************************
2 * *
3 * Copyright (C) 2006 David Brodsky *
4 * *
5 * This program is free software; you can redistribute it and/or *
6 * modify it under the terms of the GNU General Public License as *
7 * published by the Free Software Foundation and appearing *
8 * in the file LICENSE.GPL included in the packaging of this file. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
13 * General Public License for more details. *
14 * *
15 ***************************************************************************/
17 #include <cstdlib>
18 #include <ctime>
19 #include <fstream>
21 #include <tairon/core/config.h>
22 #include <tairon/core/exceptions.h>
23 #include <tairon/core/log.h>
24 #include <tairon/core/tinyxml.h>
25 #include <tairon/net/timer.h>
27 #include "torrentmanager.h"
29 #include "core/bencode.h"
30 #include "storage.h"
31 #include "torrentclient.h"
33 #define TAIRENT_VERSION "0.1"
35 static const char *binTable = "0123456789abcdef";
37 namespace Tairent
40 namespace Main
43 TorrentManager *TorrentManager::torrentManager = 0;
45 /* {{{ TorrentManager::TorrentManager() */
46 TorrentManager::TorrentManager()
48 if (torrentManager)
49 throw Tairon::Core::SingletonException("Cannot make another instance of Tairent::Main::TorrentManager");
51 makeClientID();
52 loadTorrents();
54 saveTimer = new Tairon::Net::Timer();
55 saveTimer->timeoutSignal.connect(Tairon::Core::threadMethodDFunctor(Tairon::Core::Thread::current(), this, &TorrentManager::save));
56 saveTimer->start(60000); // 1 minute
58 torrentManager = this;
60 /* }}} */
62 /* {{{ TorrentManager::~TorrentManager() */
63 TorrentManager::~TorrentManager()
65 saveTimer->destroy();
66 torrentManager = 0;
68 /* }}} */
70 /* {{{ TorrentManager::binToHex(const String &) */
71 String TorrentManager::binToHex(const String &data)
73 String ret;
74 ret.resize(data.size() * 2);
76 for (unsigned int i = 0; i < data.size(); ++i) {
77 ret[2 * i] = binTable[(data[i] & 0xf0) >> 4];
78 ret[2 * i + 1] = binTable[data[i] & 0x0f];
81 return ret;
83 /* }}} */
85 /* {{{ TorrentManager::destroy() */
86 void TorrentManager::destroy()
88 save();
89 for (std::map<String, TorrentStruct *>::const_iterator it = torrents.begin(); it != torrents.end(); ++it) {
90 delete it->second->client;
91 delete it->second->storage;
92 delete it->second->metaInfo;
93 delete it->second;
95 torrents.clear();
97 /* }}} */
99 /* {{{ TorrentManager::getClient(const String &) */
100 TorrentClient *TorrentManager::getClient(const String &infoHash)
102 if (torrents.count(infoHash)) {
103 TorrentStruct *t = torrents[infoHash];
104 if (t->client)
105 return t->client;
107 t->client = new TorrentClient(t, infoHash, t->storage);
110 return 0; // no client found
112 /* }}} */
114 /* {{{ TorrentManager::getClientID() */
115 const String &TorrentManager::getClientID()
117 return clientID;
119 /* }}} */
121 /* {{{ TorrentManager::hexToBin(char) */
122 char TorrentManager::hexToBin(char c)
124 if ((c >= '0') && (c <= '9'))
125 return c - '0';
126 return c - 'a' + 10;
128 /* }}} */
130 /* {{{ TorrentManager::hexToBin(const String &) */
131 String TorrentManager::hexToBin(const String &data)
133 String ret;
134 ret.resize(data.length() / 2);
136 unsigned int i = 0;
137 const char *d = data;
138 while (*d) {
139 ret[i++] = (hexToBin(*d) << 4) | (hexToBin(*(d + 1)));
140 d += 2;
143 return ret;
145 /* }}} */
147 /* {{{ TorrentManager::loadMetaInfo(const String &) */
148 Tairent::Core::BEncode *TorrentManager::loadMetaInfo(const String &filename)
150 std::ifstream f(filename.contains('/') ? filename : String((*Tairon::Core::Config::self())["torrents-directory"] + '/' + filename), std::ios_base::in);
151 if (!f.is_open()) {
152 WARNING((const char *) String("Cannot load informations from " + filename));
153 return 0;
156 Tairent::Core::BEncode *ret = new Tairent::Core::BEncode();
157 try {
158 f >> (*ret);
159 } catch (const Tairent::Core::BEncodeException &e) {
160 WARNING((const char *) String("Cannot load metainfo from " + filename + ": " + (const String &) e));
161 delete ret;
162 return 0;
165 return ret;
167 /* }}} */
169 /* {{{ TorrentManager::loadTorrent(TiXmlNode *) */
170 void TorrentManager::loadTorrent(TiXmlNode *t)
172 TiXmlElement *element = t->ToElement();
173 const char *data = element->Attribute("file");
174 if (!data) {
175 WARNING("Missing torrent's filename");
176 return;
179 INFO((const char *) String(String("Loading informations about ") + data));
181 TorrentStruct *torrent = new TorrentStruct;
182 torrent->client = 0;
183 torrent->storage = 0;
185 torrent->torrentFile = data;
187 data = element->Attribute("complete");
188 if (data && (data[0] == '1'))
189 torrent->complete = true;
190 else
191 torrent->complete = false;
193 torrent->metaInfo = loadMetaInfo(torrent->torrentFile);
194 if (!torrent->metaInfo)
195 return;
197 data = element->Attribute("hash");
198 if (data)
199 torrents[hexToBin(data)] = torrent;
200 else {
201 String hash = (*torrent->metaInfo)["info"].computeSHA1();
202 torrents[hash] = torrent;
205 // load storage, if available
206 for (TiXmlNode *node = element->FirstChild(); node; node = node->NextSibling()) {
207 if (node->Type() != TiXmlNode::ELEMENT)
208 continue;
209 if (node->ValueStr() == "storage")
210 torrent->storage = new Storage((*torrent->metaInfo)["info"], node);
213 /* }}} */
215 /* {{{ TorrentManager::loadTorrents() */
216 void TorrentManager::loadTorrents()
218 TiXmlDocument document;
220 if (!document.LoadFile((*Tairon::Core::Config::self())["torrents-directory"] + "/torrents.xml")) {
221 ERROR("Cannot load informations about torrents");
222 return;
225 TiXmlElement *root = document.RootElement();
226 if (!root)
227 return;
229 if (root->ValueStr() != "torrents") // invalid file
230 return;
232 for (TiXmlNode *node = root->FirstChild(); node; node = node->NextSibling()) {
233 if (node->Type() != TiXmlNode::ELEMENT)
234 continue;
235 if (node->ValueStr() == "torrent")
236 loadTorrent(node);
239 /* }}} */
241 /* {{{ TorrentManager::makeClientID() */
242 void TorrentManager::makeClientID()
244 srandom(time(0));
246 clientID = "TA-" TAIRENT_VERSION "-";
247 size_t len = clientID.length();
248 clientID.resize(20);
250 std::ifstream f("/dev/urandom", std::ios_base::in);
251 if (f.is_open()) {
252 for (size_t i = len; i < 20; ++i)
253 clientID[i] = (char) f.get();
254 f.close();
255 } else { // cannot open /dev/urandom? fall back to standard random
256 srandom(time(0));
257 for (size_t i = len; i < 20; ++i)
258 clientID[i] = random() % 256;
261 /* }}} */
263 /* {{{ TorrentManager::save() */
264 void TorrentManager::save()
266 TiXmlDocument document((*Tairon::Core::Config::self())["torrents-directory"] + "/torrents.xml");
268 TiXmlElement *root = new TiXmlElement("torrents");
269 document.LinkEndChild(root);
271 for (std::map<String, TorrentStruct *>::const_iterator it = torrents.begin(); it != torrents.end(); ++it) {
272 TiXmlElement *element = new TiXmlElement("torrent");
273 element->SetAttribute(String("hash"), binToHex(it->first));
274 element->SetAttribute(String("file"), it->second->torrentFile);
275 element->LinkEndChild(it->second->client->getStorage()->save());
276 root->LinkEndChild(element);
279 if (!document.SaveFile())
280 ERROR("Cannot save informations about torrents.");
282 /* }}} */
284 /* {{{ TorrentManager::startTorrents() */
285 void TorrentManager::startTorrents()
287 for (std::map<String, TorrentStruct *>::const_iterator it = torrents.begin(); it != torrents.end(); ++it) {
288 TorrentStruct *t = it->second;
289 if (t->complete)
290 continue;
292 if (!t->metaInfo) { // metainfo loading failed
293 WARNING((const char *) String("Cannot start torrent " + t->torrentFile));
294 continue;
297 if (!t->storage) // no fast resume date was loaded
298 INFO((const char *) String("No fast-resume data for torrent " + t->torrentFile));
300 t->client = new TorrentClient(t, it->first, t->storage);
304 /* }}} */
306 /* {{{ TorrentManager::stopTorrents() */
307 void TorrentManager::stopTorrents()
309 for (std::map<String, TorrentStruct *>::const_iterator it = torrents.begin(); it != torrents.end(); ++it)
310 it->second->client->stop();
312 /* }}} */
314 }; // namespace Main
316 }; // namespace Tairent
318 // vim: ai sw=4 ts=4 noet fdm=marker