- License texts modified to GPLv2
[qtrapids] / src / server / TorrentSession.cpp
1 /***************************************************************************
2  *   Copyright (C) 2010 by Ixonos Plc   *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; version 2 of the License.               *
7  *                                                                         *
8  *   This program is distributed in the hope that it will be useful,       *
9  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
10  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
11  *   GNU General Public License for more details.                          *
12  *                                                                         *
13  *   You should have received a copy of the GNU General Public License     *
14  *   along with this program; if not, write to the                         *
15  *   Free Software Foundation, Inc.,                                       *
16  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
17  ***************************************************************************/
18 #include "TorrentSession.hpp"
19 #include "TorrentHandle.hpp"
20 #include "AlertWaiterThread.hpp"
21 #include "ServerDb.hpp"
22 #include <qtrapids/error.hpp>
23
24 namespace qtrapids
25 {
26
27
28
29 TorrentSession::TorrentSession(QObject *parent, QSettings *settings)
30                 : QObject(parent)
31                 , btSession_()
32                 , alertWaiter_(new AlertWaiterThread(&btSession_, this))
33                 , settings_(new ServerSettings(*settings))
34                 , db_(new ServerDb(settings_.get()))
35 {
36         qDBusRegisterMetaType<qtrapids::TorrentState>();
37         qDBusRegisterMetaType<qtrapids::ParamsMap_t>();
38         new QtRapidsServer(this);
39
40         QDBusConnection dbus = QDBusConnection::sessionBus();
41         dbus.registerObject("/qtrapids", this);
42         dbus.registerService("com.ixonos.qtrapids");
43         
44         alertWaiter_->allAlerts();
45         connect(alertWaiter_, SIGNAL(alert()), this, SLOT(on_alert()));
46         alertWaiter_->start();
47
48         loadState();
49         
50         // Lets force applying rate limits to local network also, at least for testing.
51         // Also, on the N900 device, rate limiting is necessary due to limited processign power
52         session_settings_t sessionSettings;
53         sessionSettings.ignore_limits_on_local_network = false;
54         btSession_.set_settings(sessionSettings);
55         
56 }
57
58
59 TorrentSession::~TorrentSession()
60 {
61         alertWaiter_->stop();
62         alertWaiter_->wait();
63         emit sessionTerminated();
64 }
65
66
67 void TorrentSession::loadState()
68 {
69         TorrentDownloadInfo info;
70         TorrentsStorage storage(*db_);
71         while (storage.nextTorrent(info)) {
72 #ifdef QTRAPIDS_DEBUG
73                 qDebug() << "adding " << info.path;
74 #endif
75                 addTorrent_(info.path, info.download_path, ParamsMap_t(), true);
76         }
77         btSession_.listen_on(settings_->getListenPorts());
78 }
79
80 void TorrentSession::on_alert()
81 {
82         std::auto_ptr<alert_t> alertPtr = btSession_.pop_alert();
83
84         if (alertPtr.get() != NULL) {
85
86                 torrent_alert_t *ta = dynamic_cast<torrent_alert_t*> (alertPtr.get());
87
88 #ifdef QTRAPIDS_DEBUG
89                 qDebug()
90                 << "TorrentSession::on_alert(): "
91                 << QString::fromStdString(alertPtr->message());
92 #endif
93
94                 if (ta) {
95                         if (!ta->handle.is_valid()) {
96 #ifdef QTRAPIDS_DEBUG
97                                 qDebug() << "handle is invalid";
98 #endif
99                                 return;
100                         }
101
102                         TorrentHandle handle(ta->handle);
103                         TorrentState state;
104
105                         state.hash = Hash2QStr(handle.hash());
106                         state.action = TorrentState::action_update;
107                         state.state = handle.state();
108                         state.progress = handle.progress() * torrent_progress_max;
109                         state.down_rate = handle.downloadRate();
110                         state.up_rate = handle.uploadRate();
111                         state.seeds = handle.numSeeds();
112                         state.leeches = handle.numLeeches();
113                         state.total_size = handle.getTotalSize();
114                         state.total_done = handle.getTotalDone();
115                         
116                         ParamsMap_t params;
117                         emit alert(state, params);
118                 }
119
120         }
121 }
122
123 void TorrentSession::getState()
124 {
125         torrents_t::const_iterator p;
126         for (p = torrents_.constBegin(); p != torrents_.constEnd(); ++p) {
127                 TorrentHandlePtr handle = *p;
128                 TorrentState state;
129                 QString hash = Hash2QStr(handle->hash());
130
131                 state.hash = hash;
132                 state.name = handle->name();
133                 state.action = TorrentState::action_add;
134                 state.state = handle->state();
135                 state.progress = handle->progress() * torrent_progress_max;
136                 state.down_rate = handle->downloadRate();
137                 state.up_rate = handle->uploadRate();
138                 state.seeds = handle->numSeeds();
139                 state.leeches = handle->numLeeches();
140                 state.total_size = handle->getTotalSize();
141
142                 emit alert(state, ParamsMap_t());
143         }
144 }
145
146 void TorrentSession::addTorrent(const QString &path, const QString &save_path
147                                 , qtrapids::ParamsMap_t other_params)
148 {
149         return addTorrent_(path, save_path, other_params, false);
150 }
151
152 void TorrentSession::addTorrent_(const QString &path, const QString &save_path
153                                  , const qtrapids::ParamsMap_t &other_params
154                                  , bool is_restore_session)
155 {
156         add_torrent_params_t addParams;
157         QFile torrent_file(path);
158         QDir::home().mkdir(settings_->getTorrentsSubDir());
159
160         if (!torrent_file.exists()) {
161                 qWarning() << "Torrent file " << path << "doesn't exist";
162                 return;
163         }
164
165         QString new_torrent_fname(QDir(settings_->getTorrentsDir())
166                                   .filePath(QFileInfo(path).fileName()));
167 #ifdef QTRAPIDS_DEBUG
168         qDebug() << "copy to " << new_torrent_fname;
169 #endif
170
171         torrent_file.copy(new_torrent_fname);
172         
173 #ifdef QTRAPIDS_DEBUG
174         qDebug() << "addTorrent: " << path << " save to " << save_path;
175 #endif
176
177         boost::intrusive_ptr<libtorrent::torrent_info> tiTmp
178         = new libtorrent::torrent_info
179         (boost::filesystem::path(new_torrent_fname.toStdString()));
180         addParams.ti = tiTmp;
181
182         QString download_dir;
183         if (!save_path.isEmpty()) {
184                 download_dir = save_path;
185         } else {
186                 download_dir = settings_->getDownloadDir();
187         }
188         // save_path is the only mandatory parameter, rest are optional.
189         addParams.save_path = boost::filesystem::path(download_dir.toStdString());
190         //addParams.storage_mode = libtorrent::storage_mode_allocate;
191
192         TorrentHandlePtr handle(new TorrentHandle(btSession_.add_torrent(addParams)));
193         QString hash = Hash2QStr(handle->hash());
194
195         if (!is_restore_session) {
196                 db_->addTorrent(hash, path, save_path);
197         }
198
199         TorrentState state;
200
201         state.hash = hash;
202         state.name = handle->name();
203         state.action = TorrentState::action_add;
204         state.state = handle->state();
205         state.progress = handle->progress() * torrent_progress_max;
206         state.down_rate = handle->downloadRate();
207         state.up_rate = handle->uploadRate();
208         state.seeds = handle->numSeeds();
209         state.leeches = handle->numLeeches();
210         state.total_size = handle->getTotalSize();
211
212         torrents_[hash] = handle;
213
214         emit alert(state, ParamsMap_t());
215 }
216
217 void TorrentSession::removeTorrent(const QString &hash)
218 {
219         torrents_t::iterator p = torrents_.find(hash);
220
221         if (p == torrents_.end()) {
222 #ifdef QTRAPIDS_DEBUG
223                 qDebug() << "Invalid request to remove torrent with hash " << hash;
224 #endif
225                 return;
226         }
227         try {
228                 btSession_.remove_torrent(p.value()->getHandle());
229         } catch (torrent_exception_t e) {
230                 qDebug() << // e.what()
231                 "exception catched"
232                 ;
233         }
234
235         TorrentState state;
236         state.hash = hash;
237         state.action = TorrentState::action_remove;
238         emit alert(state, ParamsMap_t());
239         torrents_.erase(p);
240         db_->removeTorrent(hash);
241 }
242
243
244 void TorrentSession::setOptions(qtrapids::ParamsMap_t options)
245 {
246         qtrapids::ParamsMapConstIterator_t end = options.end();
247         qtrapids::ParamsMapConstIterator_t tmpIter = options.find("net/downloadRate");
248         int rate = -1;
249
250         // Apply settings immediately to Bittorrent session:
251         // NOTE: QHash interface is not quite STL-like
252         
253         if (tmpIter != end) {
254                 rate = tmpIter.value().toInt();
255                 btSession_.set_download_rate_limit(rate);
256         }
257         
258         tmpIter = options.find("net/uploadRate");
259         if (tmpIter != end) {
260                 rate = tmpIter.value().toInt();
261                 btSession_.set_upload_rate_limit(rate);
262         }
263         
264         /// @todo Add more immediately applicable settings here, if needed.
265         
266         // Finally, save settings to persistent storage:
267         settings_->setOptions(options);
268 }
269
270
271 qtrapids::ParamsMap_t TorrentSession::getOptions()
272 {
273         return settings_->getOptions();
274 }
275
276
277 void TorrentSession::terminateSession()
278 {
279         qDebug() << "Terminate called";
280         // Emiting terminate() here causes the server application to quit in main()
281         emit terminate();
282 }
283
284 } // namespace qtrapids