Send correct informations to a http tracker.
[tairent.git] / src / main / torrentmanager.cpp
blobbe740beeadd1fb2797db9c8cc9e83e4aae825088
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>
26 #include "torrentmanager.h"
28 #include "core/bencode.h"
29 #include "storage.h"
30 #include "torrentclient.h"
32 #define TAIRENT_VERSION "0.1"
34 static const char *binTable = "0123456789abcdef";
36 namespace Tairent
39 namespace Main
42 TorrentManager *TorrentManager::torrentManager = 0;
44 /* {{{ TorrentManager::TorrentManager() */
45 TorrentManager::TorrentManager()
47 if (torrentManager)
48 throw Tairon::Core::SingletonException("Cannot make another instance of Tairent::Main::TorrentManager");
50 makeClientID();
51 loadTorrents();
53 torrentManager = this;
55 /* }}} */
57 /* {{{ TorrentManager::~TorrentManager() */
58 TorrentManager::~TorrentManager()
60 torrentManager = 0;
62 /* }}} */
64 /* {{{ TorrentManager::binToHex(const String &) */
65 String TorrentManager::binToHex(const String &data)
67 String ret;
68 ret.resize(data.size() * 2);
70 for (unsigned int i = 0; i < data.size(); ++i) {
71 ret[2 * i] = binTable[(data[i] & 0xf0) >> 4];
72 ret[2 * i + 1] = binTable[data[i] & 0x0f];
75 return ret;
77 /* }}} */
79 /* {{{ TorrentManager::destroy() */
80 void TorrentManager::destroy()
82 save();
83 for (std::map<String, TorrentStruct *>::const_iterator it = torrents.begin(); it != torrents.end(); ++it) {
84 delete it->second->client;
85 delete it->second->storage;
86 delete it->second->metaInfo;
87 delete it->second;
89 torrents.clear();
91 /* }}} */
93 /* {{{ TorrentManager::getClient(const String &) */
94 TorrentClient *TorrentManager::getClient(const String &infoHash)
96 if (torrents.count(infoHash)) {
97 TorrentStruct *t = torrents[infoHash];
98 if (t->client)
99 return t->client;
101 // TODO: create client
104 return 0; // no client found
106 /* }}} */
108 /* {{{ TorrentManager::getClientID() */
109 const String &TorrentManager::getClientID()
111 return clientID;
113 /* }}} */
115 /* {{{ TorrentManager::hexToBin(char) */
116 char TorrentManager::hexToBin(char c)
118 if ((c >= '0') && (c <= '9'))
119 return c - '0';
120 return c - 'a' + 10;
122 /* }}} */
124 /* {{{ TorrentManager::hexToBin(const String &) */
125 String TorrentManager::hexToBin(const String &data)
127 String ret;
128 ret.resize(data.length() / 2);
130 unsigned int i = 0;
131 const char *d = data;
132 while (*d) {
133 ret[i++] = (hexToBin(*d) << 4) | (hexToBin(*(d + 1)));
134 d += 2;
137 return ret;
139 /* }}} */
141 /* {{{ TorrentManager::loadMetaInfo(const String &) */
142 Tairent::Core::BEncode *TorrentManager::loadMetaInfo(const String &filename)
144 std::ifstream f(filename, std::ios_base::in);
145 if (!f.is_open()) {
146 WARNING((const char *) String("Cannot load informations from " + filename));
147 return 0;
150 Tairent::Core::BEncode *ret = new Tairent::Core::BEncode();
151 try {
152 f >> (*ret);
153 } catch (const Tairent::Core::BEncodeException &e) {
154 WARNING((const char *) String("Cannot load metainfo from " + filename + ": " + (const String &) e));
155 delete ret;
156 return 0;
159 return ret;
161 /* }}} */
163 /* {{{ TorrentManager::loadTorrent(TiXmlNode *) */
164 void TorrentManager::loadTorrent(TiXmlNode *t)
166 TiXmlElement *element = t->ToElement();
167 const char *data = element->Attribute("file");
168 if (!data)
169 return;
171 INFO((const char *) String(String("Loading informations about ") + data));
173 TorrentStruct *torrent = new TorrentStruct;
174 torrent->client = 0;
175 torrent->storage = 0;
177 torrent->torrentFile = data;
179 data = element->Attribute("complete");
180 if (data && (data[0] == '1'))
181 torrent->complete = true;
182 else
183 torrent->complete = false;
185 torrent->metaInfo = loadMetaInfo(torrent->torrentFile);
186 if (!torrent->metaInfo)
187 return;
189 data = element->Attribute("hash");
190 if (data)
191 torrents[hexToBin(data)] = torrent;
192 else {
193 String hash = (*torrent->metaInfo)["info"].computeSHA1();
194 torrents[hash] = torrent;
197 // load storage, if available
198 for (TiXmlNode *node = element->FirstChild(); node; node = node->NextSibling()) {
199 if (node->Type() != TiXmlNode::ELEMENT)
200 continue;
201 if (node->ValueStr() == "storage")
202 torrent->storage = new Storage((*torrent->metaInfo)["info"], node);
205 /* }}} */
207 /* {{{ TorrentManager::loadTorrents() */
208 void TorrentManager::loadTorrents()
210 TiXmlDocument document;
212 if (!document.LoadFile((*Tairon::Core::Config::self())["torrents-directory"] + "/torrents.xml"))
213 return;
215 TiXmlElement *root = document.RootElement();
216 if (!root)
217 return;
219 if (root->ValueStr() != "torrents") // invalid file
220 return;
222 for (TiXmlNode *node = root->FirstChild(); node; node = node->NextSibling()) {
223 if (node->Type() != TiXmlNode::ELEMENT)
224 continue;
225 if (node->ValueStr() == "torrent")
226 loadTorrent(node);
229 /* }}} */
231 /* {{{ TorrentManager::makeClientID() */
232 void TorrentManager::makeClientID()
234 srandom(time(0));
236 clientID = "TA-" TAIRENT_VERSION "-";
237 size_t len = clientID.length();
238 clientID.resize(20);
240 std::ifstream f("/dev/urandom", std::ios_base::in);
241 if (f.is_open()) {
242 for (size_t i = len; i < 20; ++i)
243 clientID[i] = (char) f.get();
244 f.close();
245 } else { // cannot open /dev/urandom? fall back to standard random
246 srandom(time(0));
247 for (size_t i = len; i < 20; ++i)
248 clientID[i] = random() % 256;
251 /* }}} */
253 /* {{{ TorrentManager::save() */
254 void TorrentManager::save()
256 TiXmlDocument document((*Tairon::Core::Config::self())["torrents-directory"] + "/torrents.xml");
258 TiXmlElement *root = new TiXmlElement("torrents");
259 document.LinkEndChild(root);
261 for (std::map<String, TorrentStruct *>::const_iterator it = torrents.begin(); it != torrents.end(); ++it) {
262 TiXmlElement *element = new TiXmlElement("torrent");
263 element->SetAttribute(String("hash"), binToHex(it->first));
264 element->SetAttribute(String("file"), it->second->torrentFile);
265 element->LinkEndChild(it->second->client->getStorage()->save());
266 root->LinkEndChild(element);
269 // TODO: error checking
270 document.SaveFile();
272 /* }}} */
274 /* {{{ TorrentManager::startTorrents() */
275 void TorrentManager::startTorrents()
277 for (std::map<String, TorrentStruct *>::const_iterator it = torrents.begin(); it != torrents.end(); ++it) {
278 TorrentStruct *t = it->second;
279 if (t->complete)
280 continue;
282 if (!t->metaInfo) { // metainfo loading failed
283 WARNING((const char *) String("Cannot start torrent " + t->torrentFile));
284 continue;
287 if (!t->storage) // no fast resume date was loaded
288 INFO((const char *) String("No fast-resume data for torrent " + t->torrentFile));
290 t->client = new TorrentClient(t, it->first, t->storage);
294 /* }}} */
296 /* {{{ TorrentManager::stopTorrents() */
297 void TorrentManager::stopTorrents()
299 for (std::map<String, TorrentStruct *>::const_iterator it = torrents.begin(); it != torrents.end(); ++it)
300 it->second->client->stop();
302 /* }}} */
304 }; // namespace Main
306 }; // namespace Tairent
308 // vim: ai sw=4 ts=4 noet fdm=marker