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
);
108 t
->storage
= t
->client
->getStorage();
111 return 0; // no client found
115 /* {{{ TorrentManager::getClientID() */
116 const String
&TorrentManager::getClientID()
122 /* {{{ TorrentManager::hexToBin(char) */
123 char TorrentManager::hexToBin(char c
)
125 if ((c
>= '0') && (c
<= '9'))
131 /* {{{ TorrentManager::hexToBin(const String &) */
132 String
TorrentManager::hexToBin(const String
&data
)
135 ret
.resize(data
.length() / 2);
138 const char *d
= data
;
140 ret
[i
++] = (hexToBin(*d
) << 4) | (hexToBin(*(d
+ 1)));
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
);
153 WARNING((const char *) String("Cannot load informations from " + filename
));
157 Tairent::Core::BEncode
*ret
= new Tairent::Core::BEncode();
160 } catch (const Tairent::Core::BEncodeException
&e
) {
161 WARNING((const char *) String("Cannot load metainfo from " + filename
+ ": " + (const String
&) e
));
170 /* {{{ TorrentManager::loadTorrent(TiXmlNode *) */
171 void TorrentManager::loadTorrent(TiXmlNode
*t
)
173 TiXmlElement
*element
= t
->ToElement();
174 const char *data
= element
->Attribute("file");
176 WARNING("Missing torrent's filename");
180 INFO((const char *) String(String("Loading informations about ") + data
));
182 TorrentStruct
*torrent
= new TorrentStruct
;
184 torrent
->storage
= 0;
186 torrent
->torrentFile
= data
;
188 data
= element
->Attribute("complete");
189 if (data
&& (data
[0] == '1'))
190 torrent
->complete
= true;
192 torrent
->complete
= false;
194 torrent
->metaInfo
= loadMetaInfo(torrent
->torrentFile
);
195 if (!torrent
->metaInfo
)
198 data
= element
->Attribute("hash");
200 torrents
[hexToBin(data
)] = torrent
;
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
)
210 if (node
->ValueStr() == "storage") {
211 torrent
->storage
= new Storage((*torrent
->metaInfo
)["info"], node
);
212 if (torrent
->complete
)
213 torrent
->storage
->complete();
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");
229 TiXmlElement
*root
= document
.RootElement();
233 if (root
->ValueStr() != "torrents") // invalid file
236 for (TiXmlNode
*node
= root
->FirstChild(); node
; node
= node
->NextSibling()) {
237 if (node
->Type() != TiXmlNode::ELEMENT
)
239 if (node
->ValueStr() == "torrent")
245 /* {{{ TorrentManager::makeClientID() */
246 void TorrentManager::makeClientID()
250 clientID
= "TA-" TAIRENT_VERSION
"-";
251 size_t len
= clientID
.length();
254 std::ifstream
f("/dev/urandom", std::ios_base::in
);
256 for (size_t i
= len
; i
< 20; ++i
)
257 clientID
[i
] = (char) f
.get();
259 } else { // cannot open /dev/urandom? fall back to standard random
261 for (size_t i
= len
; i
< 20; ++i
)
262 clientID
[i
] = random() % 256;
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.");
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
;
298 if (!t
->metaInfo
) { // metainfo loading failed
299 WARNING((const char *) String("Cannot start torrent " + t
->torrentFile
));
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();
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();
323 }; // namespace Tairent
325 // vim: ai sw=4 ts=4 noet fdm=marker