Fix configuration loading in tairentctl.
[tairent.git] / src / main / torrentmanager.cpp
blob22eee1922079a3ff40164a9b4440802ab2b7adf1
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);
108 t->storage = t->client->getStorage();
111 return 0; // no client found
113 /* }}} */
115 /* {{{ TorrentManager::getClientID() */
116 const String &TorrentManager::getClientID()
118 return clientID;
120 /* }}} */
122 /* {{{ TorrentManager::hexToBin(char) */
123 char TorrentManager::hexToBin(char c)
125 if ((c >= '0') && (c <= '9'))
126 return c - '0';
127 return c - 'a' + 10;
129 /* }}} */
131 /* {{{ TorrentManager::hexToBin(const String &) */
132 String TorrentManager::hexToBin(const String &data)
134 String ret;
135 ret.resize(data.length() / 2);
137 unsigned int i = 0;
138 const char *d = data;
139 while (*d) {
140 ret[i++] = (hexToBin(*d) << 4) | (hexToBin(*(d + 1)));
141 d += 2;
144 return ret;
146 /* }}} */
148 /* {{{ TorrentManager::loadMetaInfo(const String &) */
149 Tairent::Core::BEncode *TorrentManager::loadMetaInfo(const String &filename)
151 std::ifstream f(filename.contains('/') ? filename : String((*Tairon::Core::Config::self())["torrents-directory"] + '/' + filename), std::ios_base::in);
152 if (!f.is_open()) {
153 WARNING((const char *) String("Cannot load informations from " + filename));
154 return 0;
157 Tairent::Core::BEncode *ret = new Tairent::Core::BEncode();
158 try {
159 f >> (*ret);
160 } catch (const Tairent::Core::BEncodeException &e) {
161 WARNING((const char *) String("Cannot load metainfo from " + filename + ": " + (const String &) e));
162 delete ret;
163 return 0;
166 return ret;
168 /* }}} */
170 /* {{{ TorrentManager::loadTorrent(TiXmlNode *) */
171 void TorrentManager::loadTorrent(TiXmlNode *t)
173 TiXmlElement *element = t->ToElement();
174 const char *data = element->Attribute("file");
175 if (!data) {
176 WARNING("Missing torrent's filename");
177 return;
180 INFO((const char *) String(String("Loading informations about ") + data));
182 TorrentStruct *torrent = new TorrentStruct;
183 torrent->client = 0;
184 torrent->storage = 0;
186 torrent->torrentFile = data;
188 data = element->Attribute("complete");
189 if (data && (data[0] == '1'))
190 torrent->complete = true;
191 else
192 torrent->complete = false;
194 torrent->metaInfo = loadMetaInfo(torrent->torrentFile);
195 if (!torrent->metaInfo)
196 return;
198 data = element->Attribute("hash");
199 if (data)
200 torrents[hexToBin(data)] = torrent;
201 else {
202 String hash = (*torrent->metaInfo)["info"].computeSHA1();
203 torrents[hash] = torrent;
206 // load storage, if available
207 for (TiXmlNode *node = element->FirstChild(); node; node = node->NextSibling()) {
208 if (node->Type() != TiXmlNode::ELEMENT)
209 continue;
210 if (node->ValueStr() == "storage") {
211 torrent->storage = new Storage((*torrent->metaInfo)["info"], node);
212 if (torrent->complete)
213 torrent->storage->complete();
217 /* }}} */
219 /* {{{ TorrentManager::loadTorrents() */
220 void TorrentManager::loadTorrents()
222 TiXmlDocument document;
224 if (!document.LoadFile((*Tairon::Core::Config::self())["torrents-directory"] + "/torrents.xml")) {
225 ERROR("Cannot load informations about torrents");
226 return;
229 TiXmlElement *root = document.RootElement();
230 if (!root)
231 return;
233 if (root->ValueStr() != "torrents") // invalid file
234 return;
236 for (TiXmlNode *node = root->FirstChild(); node; node = node->NextSibling()) {
237 if (node->Type() != TiXmlNode::ELEMENT)
238 continue;
239 if (node->ValueStr() == "torrent")
240 loadTorrent(node);
243 /* }}} */
245 /* {{{ TorrentManager::makeClientID() */
246 void TorrentManager::makeClientID()
248 srandom(time(0));
250 clientID = "TA-" TAIRENT_VERSION "-";
251 size_t len = clientID.length();
252 clientID.resize(20);
254 std::ifstream f("/dev/urandom", std::ios_base::in);
255 if (f.is_open()) {
256 for (size_t i = len; i < 20; ++i)
257 clientID[i] = (char) f.get();
258 f.close();
259 } else { // cannot open /dev/urandom? fall back to standard random
260 srandom(time(0));
261 for (size_t i = len; i < 20; ++i)
262 clientID[i] = random() % 256;
265 /* }}} */
267 /* {{{ TorrentManager::save() */
268 void TorrentManager::save()
270 TiXmlDocument document((*Tairon::Core::Config::self())["torrents-directory"] + "/torrents.xml");
272 TiXmlElement *root = new TiXmlElement("torrents");
273 document.LinkEndChild(root);
275 for (std::map<String, TorrentStruct *>::const_iterator it = torrents.begin(); it != torrents.end(); ++it) {
276 TiXmlElement *element = new TiXmlElement("torrent");
277 element->SetAttribute(String("hash"), binToHex(it->first));
278 element->SetAttribute(String("file"), it->second->torrentFile);
279 if (it->second->storage->isComplete())
280 element->SetAttribute("complete", 1);
281 element->LinkEndChild(it->second->storage->save());
282 root->LinkEndChild(element);
285 if (!document.SaveFile())
286 ERROR("Cannot save informations about torrents.");
288 /* }}} */
290 /* {{{ TorrentManager::startTorrents() */
291 void TorrentManager::startTorrents()
293 for (std::map<String, TorrentStruct *>::const_iterator it = torrents.begin(); it != torrents.end(); ++it) {
294 TorrentStruct *t = it->second;
295 if (t->complete)
296 continue;
298 if (!t->metaInfo) { // metainfo loading failed
299 WARNING((const char *) String("Cannot start torrent " + t->torrentFile));
300 continue;
303 if (!t->storage) // no fast resume date was loaded
304 INFO((const char *) String("No fast-resume data for torrent " + t->torrentFile));
306 t->client = new TorrentClient(t, it->first, t->storage);
307 t->storage = t->client->getStorage();
310 /* }}} */
312 /* {{{ TorrentManager::stopTorrents() */
313 void TorrentManager::stopTorrents()
315 for (std::map<String, TorrentStruct *>::const_iterator it = torrents.begin(); it != torrents.end(); ++it)
316 if (it->second->client)
317 it->second->client->stop();
319 /* }}} */
321 }; // namespace Main
323 }; // namespace Tairent
325 // vim: ai sw=4 ts=4 noet fdm=marker