1 /***************************************************************************
3 * Copyright (C) 2006 David Brodsky *
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. *
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. *
15 ***************************************************************************/
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"
31 #include "torrentclient.h"
33 #define TAIRENT_VERSION "0.1"
35 static const char *binTable
= "0123456789abcdef";
43 TorrentManager
*TorrentManager::torrentManager
= 0;
45 /* {{{ TorrentManager::TorrentManager() */
46 TorrentManager::TorrentManager()
49 throw Tairon::Core::SingletonException("Cannot make another instance of Tairent::Main::TorrentManager");
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;
62 /* {{{ TorrentManager::~TorrentManager() */
63 TorrentManager::~TorrentManager()
70 /* {{{ TorrentManager::binToHex(const String &) */
71 String
TorrentManager::binToHex(const String
&data
)
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];
85 /* {{{ TorrentManager::destroy() */
86 void TorrentManager::destroy()
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
;
99 /* {{{ TorrentManager::getClient(const String &) */
100 TorrentClient
*TorrentManager::getClient(const String
&infoHash
)
102 if (torrents
.count(infoHash
)) {
103 TorrentStruct
*t
= torrents
[infoHash
];
107 t
->client
= new TorrentClient(t
, infoHash
, t
->storage
);
110 return 0; // no client found
114 /* {{{ TorrentManager::getClientID() */
115 const String
&TorrentManager::getClientID()
121 /* {{{ TorrentManager::hexToBin(char) */
122 char TorrentManager::hexToBin(char c
)
124 if ((c
>= '0') && (c
<= '9'))
130 /* {{{ TorrentManager::hexToBin(const String &) */
131 String
TorrentManager::hexToBin(const String
&data
)
134 ret
.resize(data
.length() / 2);
137 const char *d
= data
;
139 ret
[i
++] = (hexToBin(*d
) << 4) | (hexToBin(*(d
+ 1)));
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
);
152 WARNING((const char *) String("Cannot load informations from " + filename
));
156 Tairent::Core::BEncode
*ret
= new Tairent::Core::BEncode();
159 } catch (const Tairent::Core::BEncodeException
&e
) {
160 WARNING((const char *) String("Cannot load metainfo from " + filename
+ ": " + (const String
&) e
));
169 /* {{{ TorrentManager::loadTorrent(TiXmlNode *) */
170 void TorrentManager::loadTorrent(TiXmlNode
*t
)
172 TiXmlElement
*element
= t
->ToElement();
173 const char *data
= element
->Attribute("file");
175 WARNING("Missing torrent's filename");
179 INFO((const char *) String(String("Loading informations about ") + data
));
181 TorrentStruct
*torrent
= new TorrentStruct
;
183 torrent
->storage
= 0;
185 torrent
->torrentFile
= data
;
187 data
= element
->Attribute("complete");
188 if (data
&& (data
[0] == '1'))
189 torrent
->complete
= true;
191 torrent
->complete
= false;
193 torrent
->metaInfo
= loadMetaInfo(torrent
->torrentFile
);
194 if (!torrent
->metaInfo
)
197 data
= element
->Attribute("hash");
199 torrents
[hexToBin(data
)] = torrent
;
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
)
209 if (node
->ValueStr() == "storage")
210 torrent
->storage
= new Storage((*torrent
->metaInfo
)["info"], node
);
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");
225 TiXmlElement
*root
= document
.RootElement();
229 if (root
->ValueStr() != "torrents") // invalid file
232 for (TiXmlNode
*node
= root
->FirstChild(); node
; node
= node
->NextSibling()) {
233 if (node
->Type() != TiXmlNode::ELEMENT
)
235 if (node
->ValueStr() == "torrent")
241 /* {{{ TorrentManager::makeClientID() */
242 void TorrentManager::makeClientID()
246 clientID
= "TA-" TAIRENT_VERSION
"-";
247 size_t len
= clientID
.length();
250 std::ifstream
f("/dev/urandom", std::ios_base::in
);
252 for (size_t i
= len
; i
< 20; ++i
)
253 clientID
[i
] = (char) f
.get();
255 } else { // cannot open /dev/urandom? fall back to standard random
257 for (size_t i
= len
; i
< 20; ++i
)
258 clientID
[i
] = random() % 256;
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.");
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
;
292 if (!t
->metaInfo
) { // metainfo loading failed
293 WARNING((const char *) String("Cannot start torrent " + t
->torrentFile
));
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
);
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();
316 }; // namespace Tairent
318 // vim: ai sw=4 ts=4 noet fdm=marker