source: src/halTorrentInternal.hpp @ 345

Revision 345, 32.7 KB checked in by Eoin, 12 years ago (diff)

Minor renaming and attempts to decouple GUI from Torrents.

Line 
1
2//         Copyright Eóin O'Callaghan 2006 - 2007.
3// Distributed under the Boost Software License, Version 1.0.
4//    (See accompanying file LICENSE_1_0.txt or copy at
5//          http://www.boost.org/LICENSE_1_0.txt)
6
7#pragma once
8
9#define HAL_TORRENT_INT_BEGIN                           40500
10#define HAL_PEER_INTERESTING                    HAL_TORRENT_INT_BEGIN + 1
11#define HAL_PEER_CHOKED                         HAL_TORRENT_INT_BEGIN + 2
12#define HAL_PEER_REMOTE_INTERESTING                     HAL_TORRENT_INT_BEGIN + 3
13#define HAL_PEER_REMOTE_CHOKED                          HAL_TORRENT_INT_BEGIN + 4
14#define HAL_PEER_SUPPORT_EXTENSIONS                     HAL_TORRENT_INT_BEGIN + 5
15#define HAL_PEER_LOCAL_CONNECTION                       HAL_TORRENT_INT_BEGIN + 6
16#define HAL_PEER_HANDSHAKE                                      HAL_TORRENT_INT_BEGIN + 7
17#define HAL_PEER_CONNECTING                                     HAL_TORRENT_INT_BEGIN + 8
18#define HAL_PEER_QUEUED                                         HAL_TORRENT_INT_BEGIN + 9
19#define HAL_PEER_RC4_ENCRYPTED                          HAL_TORRENT_INT_BEGIN + 10
20#define HAL_PEER_PLAINTEXT_ENCRYPTED            HAL_TORRENT_INT_BEGIN + 11
21#define HAL_TORRENT_QUEUED_CHECKING                     HAL_TORRENT_INT_BEGIN + 12
22#define HAL_TORRENT_CHECKING_FILES                      HAL_TORRENT_INT_BEGIN + 13
23#define HAL_TORRENT_CONNECTING                          HAL_TORRENT_INT_BEGIN + 14
24#define HAL_TORRENT_DOWNLOADING                         HAL_TORRENT_INT_BEGIN + 15
25#define HAL_TORRENT_FINISHED                            HAL_TORRENT_INT_BEGIN + 16
26#define HAL_TORRENT_SEEDING                                     HAL_TORRENT_INT_BEGIN + 17
27#define HAL_TORRENT_ALLOCATING                          HAL_TORRENT_INT_BEGIN + 18
28#define HAL_TORRENT_QUEUED                                      HAL_TORRENT_INT_BEGIN + 19
29#define HAL_TORRENT_STOPPED                                     HAL_TORRENT_INT_BEGIN + 20
30#define HAL_TORRENT_PAUSED                                      HAL_TORRENT_INT_BEGIN + 21
31#define HAL_TORRENT_STOPPING                            HAL_TORRENT_INT_BEGIN + 22
32#define HAL_TORRENT_PAUSING                                     HAL_TORRENT_INT_BEGIN + 23
33
34#ifndef RC_INVOKED
35
36#include <boost/tuple/tuple.hpp>
37#include <boost/multi_index_container.hpp>
38#include <boost/multi_index/ordered_index.hpp>
39#include <boost/multi_index/indexed_by.hpp>
40#include <boost/multi_index/identity.hpp>
41#include <boost/multi_index/member.hpp>
42#include <boost/multi_index/tag.hpp>
43#include <boost/serialization/shared_ptr.hpp>
44
45#include "HaliteIni.hpp"
46
47namespace hal
48{
49class TorrentInternalOld;
50}
51
52BOOST_CLASS_VERSION(hal::TorrentInternalOld, 9)
53
54namespace hal
55{
56
57namespace lbt = libtorrent;
58namespace fs = boost::filesystem;
59namespace pt = boost::posix_time;
60
61typedef std::wstring wstring_t;
62typedef std::string string_t;
63
64typedef boost::wformat wformat_t;
65typedef boost::format format_t;
66
67typedef boost::filesystem::wpath wpath_t;
68typedef boost::filesystem::path path_t;
69
70using boost::serialization::make_nvp;
71
72lbt::entry haldecode(const wpath_t &file) 
73{
74        fs::ifstream ifs(file, fs::ifstream::binary);
75        if (ifs.is_open()) 
76        {
77                ifs.unsetf(fs::ifstream::skipws);
78                return lbt::bdecode(std::istream_iterator<char>(ifs), std::istream_iterator<char>());
79        }
80        else return lbt::entry();
81}
82
83bool halencode(const wpath_t &file, const lbt::entry &e) 
84{
85        fs::ofstream ofs(file, fs::ofstream::binary);
86
87        if (!ofs.is_open()) 
88                return false;
89       
90        lbt::bencode(std::ostream_iterator<char>(ofs), e);
91        return true;
92}
93
94class invalidTorrent : public std::exception
95{
96public:
97        invalidTorrent(const wstring_t& who) :
98                who_(who)
99        {}
100       
101        virtual ~invalidTorrent() throw () {}
102
103        wstring_t who() const throw ()
104        {
105                return who_;
106        }       
107       
108private:
109        wstring_t who_; 
110};
111       
112template<typename T>
113class TransferTracker
114{
115public:
116        TransferTracker() :
117                total_(0),
118                total_offset_(0)
119        {}
120       
121        TransferTracker(T total) :
122                total_(total),
123                total_offset_(0)
124        {}
125       
126        TransferTracker(T total, T offset) :
127                total_(total),
128                total_offset_(offset)
129        {}
130       
131        void reset(T total) const
132        {
133                total_ = total;
134                total_offset_ = 0;
135        }
136       
137        T update(T rel_total) const
138        {
139                total_ += (rel_total - total_offset_);
140                total_offset_ = rel_total;
141               
142                return total_;
143        }
144       
145        void setOffset(T offset) const
146        {
147                total_offset_ = offset;
148        }
149       
150        operator T() const { return total_; }
151       
152        friend class boost::serialization::access;
153        template<class Archive>
154        void serialize(Archive& ar, const unsigned int version)
155        {
156                ar & make_nvp("total", total_);
157        }
158       
159private:
160        mutable T total_;
161        mutable T total_offset_;
162};
163
164class DurationTracker
165{
166public:
167        DurationTracker() :
168                total_(boost::posix_time::time_duration(0,0,0,0), 
169                        boost::posix_time::time_duration(0,0,0,0))
170        {}
171       
172        boost::posix_time::time_duration update() const
173        {
174                if (start_.is_not_a_date_time()) 
175                        start_ = boost::posix_time::second_clock::universal_time();
176
177                if (static_cast<boost::posix_time::time_duration>(total_).is_special()) 
178                        total_.setOffset(boost::posix_time::time_duration(0,0,0,0));
179               
180                return total_.update(boost::posix_time::second_clock::universal_time() - start_);
181        }
182       
183        void reset() const
184        {
185                total_.setOffset(boost::posix_time::time_duration(0,0,0,0));
186                start_ = boost::posix_time::second_clock::universal_time();
187        }
188       
189        friend class boost::serialization::access;
190        template<class Archive>
191        void serialize(Archive& ar, const unsigned int version)
192        {
193                ar & make_nvp("total", total_);
194        }
195       
196        operator boost::posix_time::time_duration() const { return total_; }
197       
198private:
199        TransferTracker<boost::posix_time::time_duration> total_;       
200        mutable boost::posix_time::ptime start_;               
201};
202       
203class TorrentInternalOld
204{
205public: 
206    friend class boost::serialization::access;
207    template<class Archive>
208    void serialize(Archive& ar, const unsigned int version)
209    {
210        ar & make_nvp("transferLimit", transferLimit_);
211        ar & make_nvp("connections", connections_);
212        ar & make_nvp("uploads", uploads_);     
213               
214                if (version > 6) {
215                        ar & make_nvp("filename", filename_);
216                }
217                else 
218                {
219                        wstring_t originalFilename;
220                        ar & make_nvp("filename", originalFilename);
221                       
222                        updatePreVersion7Files(originalFilename);
223                }
224               
225        ar & make_nvp("saveDirectory", save_directory_);
226               
227                if (version > 7) {
228                        ar & make_nvp("payloadUploaded_", payloadUploaded_);
229                        ar & make_nvp("payloadDownloaded_", payloadDownloaded_);
230                        ar & make_nvp("uploaded_", uploaded_);
231                        ar & make_nvp("downloaded_", downloaded_);     
232                        ar & make_nvp("ratio", ratio_); 
233                } 
234                else if (version > 3) {
235                        ar & make_nvp("payloadUploaded_", payloadUploaded_);
236                        ar & make_nvp("payloadDownloaded_", payloadDownloaded_);
237                        ar & make_nvp("uploaded_", uploaded_);
238                        ar & make_nvp("downloaded_", downloaded_);             
239                } 
240                else if (version > 1)
241                {
242                        ar & make_nvp("totalUploaded", totalUploaded_);
243                        ar & make_nvp("ratio", ratio_);
244                       
245                        payloadUploaded_.reset(totalUploaded_);
246                }
247               
248                if (version > 0) {
249                        ar & make_nvp("trackerUsername", trackerUsername_);
250                        ar & make_nvp("trackerPassword", trackerPassword_);
251                }
252                if (version > 1) {
253                        ar & make_nvp("state", state_);
254                        ar & make_nvp("trackers", trackers_);
255                }
256       
257                if (version > 2) {
258                        ar & make_nvp("resolve_countries", resolve_countries_);
259                }
260                if (version > 4) {
261                        ar & make_nvp("file_priorities", filePriorities_);
262                }
263                if (version > 5) {
264                        ar & make_nvp("startTime", startTime_);
265                        ar & make_nvp("activeDuration", activeDuration_);
266                        ar & make_nvp("seedingDuration", seedingDuration_);
267                }
268                if (version > 6) {
269                        ar & make_nvp("name", name_);
270                        ar & make_nvp("compactStorage", compactStorage_);
271                        ar & make_nvp("finishTime", finishTime_);
272                }
273                if (version > 8) {
274                        ar & make_nvp("progress", progress_);
275                }
276    }
277       
278        void extractNames(lbt::entry& metadata)
279        {               
280                lbt::torrent_info info(metadata);                               
281                name_ = hal::from_utf8_safe(info.name());
282               
283                filename_ = name_;
284                if (!boost::find_last(filename_, L".torrent")) 
285                                filename_ += L".torrent";
286               
287                event().post(shared_ptr<EventDetail>(new EventMsg(
288                        wformat_t(L"Loaded names: %1%, %2%") % name_ % filename_)));
289        }
290       
291        void updatePreVersion7Files(wstring_t originalFilename)
292        {
293                try 
294                {
295
296                wpath_t oldFile = app().working_directory()/L"torrents"/originalFilename;
297               
298                if (fs::exists(oldFile)) 
299                        extractNames(haldecode(oldFile));
300               
301                wpath_t oldResumeFile = app().working_directory()/L"resume"/originalFilename;
302               
303                if (filename_ != originalFilename)
304                {
305                        fs::rename(oldFile, app().working_directory()/L"torrents"/filename_);
306                       
307                        if (fs::exists(oldResumeFile))
308                                fs::rename(oldResumeFile, app().working_directory()/L"resume"/filename_);
309                }
310               
311                }
312                catch(std::exception &e) 
313                {               
314                        hal::event().post(boost::shared_ptr<hal::EventDetail>(
315                                new hal::EventStdException(Event::critical, e, L"updatePreVersion7Files"))); 
316                }
317        }
318       
319        std::pair<float, float> transferLimit_;
320       
321        unsigned state_;
322        int connections_;
323        int uploads_;
324        bool in_session_;
325        float ratio_;
326        bool resolve_countries_;
327       
328        wstring_t filename_;
329        wstring_t name_;
330        wstring_t save_directory_;
331        wstring_t originalFilename_;
332        lbt::torrent_handle handle_;   
333       
334        lbt::entry metadata_;
335        lbt::entry resumedata_;
336       
337        wstring_t trackerUsername_;     
338        wstring_t trackerPassword_;
339       
340        boost::int64_t totalUploaded_;
341        boost::int64_t totalBase_;
342       
343        TransferTracker<boost::int64_t> payloadUploaded_;
344        TransferTracker<boost::int64_t> payloadDownloaded_;
345        TransferTracker<boost::int64_t> uploaded_;
346        TransferTracker<boost::int64_t> downloaded_;
347       
348        boost::posix_time::ptime startTime_;
349        boost::posix_time::ptime finishTime_;
350        DurationTracker activeDuration_;
351        DurationTracker seedingDuration_;
352       
353        std::vector<TrackerDetail> trackers_;
354        std::vector<lbt::announce_entry> torrent_trackers_;
355        std::vector<lbt::peer_info> peers_;     
356        std::vector<int> filePriorities_;
357       
358        float progress_;
359       
360        lbt::torrent_info infoMemory_;
361        lbt::torrent_status statusMemory_;
362        FileDetails fileDetailsMemory_;
363       
364        bool compactStorage_;
365};
366
367class TorrentInternal : 
368        boost::noncopyable
369{
370        friend class BitTorrent_impl;   
371public:
372        #define TORRENT_INTERNALS_DEFAULTS \
373                originalFilename_(L""), \
374                transferLimit_(std::pair<float, float>(-1, -1)), \
375                connections_(-1), \
376                uploads_(-1), \
377                ratio_(0), \
378                resolve_countries_(true), \
379                state_(TorrentDetail::torrent_active), \
380                totalUploaded_(0), \
381                totalBase_(0), \
382                progress_(0), \
383                startTime_(boost::posix_time::second_clock::universal_time())
384               
385        TorrentInternal() :     
386                TORRENT_INTERNALS_DEFAULTS,
387                compactStorage_(true),
388                in_session_(false)
389        {}
390       
391        TorrentInternal(wpath_t filename, wpath_t saveDirectory, wpath_t workingDirectory, bool compactStorage) :
392                TORRENT_INTERNALS_DEFAULTS,
393                save_directory_(saveDirectory.string()),
394                compactStorage_(compactStorage),               
395                in_session_(false)
396        {
397                assert(the_session_);
398               
399                prepare(filename, save_directory_);
400        }
401       
402        TorrentInternal(const TorrentInternalOld& t) :
403                transferLimit_(t.transferLimit_),
404                state_(t.state_),
405                connections_(t.connections_),
406                uploads_(t.uploads_),
407                in_session_(false),
408                ratio_(t.ratio_),
409                resolve_countries_(t.resolve_countries_),
410                filename_(t.filename_),
411                name_(t.name_),
412                save_directory_(t.save_directory_),
413                originalFilename_(t.originalFilename_),
414                handle_(t.handle_),
415                metadata_(t.metadata_),
416                resumedata_(t.resumedata_),
417                trackerUsername_(t.trackerUsername_),   
418                trackerPassword_(t.trackerPassword_),
419                totalUploaded_(t.totalUploaded_),
420                totalBase_(t.totalBase_),
421                payloadUploaded_(t.payloadUploaded_),
422                payloadDownloaded_(t.payloadDownloaded_),
423                uploaded_(t.uploaded_),
424                downloaded_(t.downloaded_),
425                startTime_(t.startTime_),
426                finishTime_(t.finishTime_),
427                activeDuration_(t.activeDuration_),
428                seedingDuration_(t.seedingDuration_),
429                trackers_(t.trackers_),
430                torrent_trackers_(t.torrent_trackers_),
431                peers_(t.peers_),
432                filePriorities_(t.filePriorities_),
433                progress_(t.progress_),
434                infoMemory_(t.infoMemory_),
435                statusMemory_(t.statusMemory_),
436                fileDetailsMemory_(t.fileDetailsMemory_),
437                compactStorage_(t.compactStorage_)
438        {}
439       
440        #undef TORRENT_INTERNALS_DEFAULTS
441       
442        TorrentDetail_ptr getTorrentDetail_ptr();
443        void setTransferSpeed(float down, float up);
444        void setConnectionLimit(int maxConn, int maxUpload);
445        std::pair<float, float> getTransferSpeed();
446        std::pair<int, int> getConnectionLimit();
447       
448        const wstring_t& name() const { return name_; }
449       
450        void setRatio(float ratio) 
451        { 
452                if (ratio < 0) ratio = 0;
453                ratio_ = ratio; 
454               
455                applyRatio();
456        }
457       
458        float getRatio()
459        {
460                return ratio_;
461        }
462       
463        void addToSession(bool paused = false)
464        {
465                mutex_t::scoped_lock l(mutex_);
466               
467                event().post(shared_ptr<EventDetail>(
468                        new EventMsg(wformat_t(L"    +-> %1%.") % paused)));   
469               
470                if (!in_session_ && the_session_) 
471                {
472                        event().post(shared_ptr<EventDetail>(
473                                new EventMsg(wformat_t(L"    +-> in session."))));     
474                       
475                        string_t dir = to_utf8(save_directory_);
476                       
477                        lbt::storage_mode_t storage = lbt::storage_mode_sparse;
478                       
479                        if (compactStorage_)
480                                storage = lbt::storage_mode_compact;
481                       
482                        handle_ = the_session_->add_torrent(metadata_, dir, resumedata_, storage, paused);                     
483                        assert(handle_.is_valid());
484                       
485                        clearResumeData();
486                       
487                        in_session_ = true;
488                        if (paused)
489                                state_ = TorrentDetail::torrent_paused; 
490                               
491                        applySettings();
492                }       
493                        event().post(shared_ptr<EventDetail>(
494                                new EventMsg(wformat_t(L"    +-> leaving addToSsession."))));
495        }
496       
497        void removeFromSession()
498        {
499                mutex_t::scoped_lock l(mutex_);
500                assert(inSession());
501               
502                resumedata_ = handle_.write_resume_data(); // Update the fast-resume data
503                writeResumeData();
504               
505                the_session_->remove_torrent(handle_);
506                in_session_ = false;           
507               
508                assert(!inSession());   
509        }
510       
511        bool inSession() const
512        { 
513                mutex_t::scoped_lock l(mutex_);
514               
515                if (in_session_ && (the_session_ != 0))
516                {               
517                        if (handle_.is_valid())
518                        {
519                                return true;
520                        }
521                }
522               
523                return false;
524        }
525       
526        void resume()
527        {
528                mutex_t::scoped_lock l(mutex_);
529                if (state_ == TorrentDetail::torrent_stopped)
530                {       
531                        addToSession(false);
532                        assert(inSession());                   
533                }
534                else
535                {
536                        assert(inSession());
537                        handle_.resume();
538                }       
539               
540                state_ = TorrentDetail::torrent_active;                 
541                assert(!handle_.is_paused());
542        }
543       
544        void pause()
545        {
546                mutex_t::scoped_lock l(mutex_);
547                if (state_ == TorrentDetail::torrent_stopped)
548                {       
549                        addToSession(true);
550                        assert(inSession());
551                        state_ = TorrentDetail::torrent_paused; 
552                }
553                else
554                {
555                        assert(inSession());
556                        handle_.pause();
557                        state_ = TorrentDetail::torrent_pausing;       
558                }       
559               
560                assert(handle_.is_paused());
561        }
562       
563        void stop()
564        {
565                mutex_t::scoped_lock l(mutex_);
566                if (state_ != TorrentDetail::torrent_stopped)
567                {
568                        if (state_ == TorrentDetail::torrent_active)
569                        {
570                                assert(inSession());
571                                handle_.pause();
572                                state_ = TorrentDetail::torrent_stopping;
573                        }
574                        else if (state_ == TorrentDetail::torrent_paused)
575                        {                       
576                                removeFromSession();
577
578                                state_ = TorrentDetail::torrent_stopped;                               
579                        }
580                }
581        }
582       
583        void writeResumeData()
584        {                               
585                wpath_t resumeDir = workingDir_/L"resume";
586               
587                if (!exists(resumeDir))
588                        create_directory(resumeDir);
589                               
590                bool halencode_result = halencode(resumeDir/filename_, resumedata_);
591                assert(halencode_result);
592        }
593       
594        void clearResumeData()
595        {
596                wpath_t resumeFile = workingDir_/L"resume"/filename_;
597               
598                if (exists(resumeFile))
599                        remove(resumeFile);
600        }
601       
602        void finished()
603        {
604                if (finishTime_.is_special())
605                        finishTime_ = boost::posix_time::second_clock::universal_time();
606        }
607       
608        bool isActive() const { return state_ == TorrentDetail::torrent_active; }
609       
610        unsigned state() const { return state_; }
611       
612        void setTrackerLogin(wstring_t username, wstring_t password)
613        {
614                trackerUsername_ = username;
615                trackerPassword_ = password;
616               
617                applyTrackerLogin();
618        }       
619       
620        std::pair<wstring_t, wstring_t> getTrackerLogin() const
621        {
622                return make_pair(trackerUsername_, trackerPassword_);
623        }
624       
625        const wstring_t& filename() const { return filename_; }
626       
627        const wstring_t& originalFilename() const { return originalFilename_; }
628       
629        const lbt::torrent_handle& handle() const { return handle_; }
630
631        void resetTrackers()
632        {
633                if (inSession())
634                {
635                        handle_.replace_trackers(torrent_trackers_);           
636                        trackers_.clear();
637                }
638        }
639       
640        void setTrackers(const std::vector<TrackerDetail>& trackerDetails)
641        {
642                trackers_.clear();
643                trackers_.assign(trackerDetails.begin(), trackerDetails.end());
644               
645                applyTrackers();
646        }
647       
648        const std::vector<TrackerDetail>& getTrackers()
649        {
650                if (inSession() && trackers_.empty())
651                {
652                        std::vector<lbt::announce_entry> trackers = handle_.trackers();
653                       
654                        foreach (const lbt::announce_entry& entry, trackers)
655                        {
656                                trackers_.push_back(
657                                        TrackerDetail(hal::from_utf8(entry.url), entry.tier));
658                        }
659                }               
660                return trackers_;
661        }
662       
663        void setFilePriorities(std::vector<int> fileIndices, int priority)
664        {
665                if (!filePriorities_.empty())
666                {
667                        foreach(int i, fileIndices)
668                                filePriorities_[i] = priority;
669                               
670                        applyFilePriorities();
671                }
672        }
673
674        const wstring_t& saveDirectory() { return save_directory_; }
675       
676    friend class boost::serialization::access;
677    template<class Archive>
678    void serialize(Archive& ar, const unsigned int version)
679    {
680        ar & make_nvp("transferLimit", transferLimit_);
681        ar & make_nvp("connections", connections_);
682        ar & make_nvp("uploads", uploads_);                     
683                ar & make_nvp("filename", filename_);           
684        ar & make_nvp("saveDirectory", save_directory_);
685               
686                ar & make_nvp("payloadUploaded_", payloadUploaded_);
687                ar & make_nvp("payloadDownloaded_", payloadDownloaded_);
688                ar & make_nvp("uploaded_", uploaded_);
689                ar & make_nvp("downloaded_", downloaded_);     
690                ar & make_nvp("ratio", ratio_); 
691                ar & make_nvp("trackerUsername", trackerUsername_);
692                ar & make_nvp("trackerPassword", trackerPassword_);
693               
694                ar & make_nvp("state", state_);
695                ar & make_nvp("trackers", trackers_);
696               
697                ar & make_nvp("resolve_countries", resolve_countries_);
698               
699                ar & make_nvp("file_priorities", filePriorities_);
700               
701                ar & make_nvp("startTime", startTime_);
702                ar & make_nvp("activeDuration", activeDuration_);
703                ar & make_nvp("seedingDuration", seedingDuration_);
704               
705                ar & make_nvp("name", name_);
706                ar & make_nvp("compactStorage", compactStorage_);
707                ar & make_nvp("finishTime", finishTime_);
708               
709                ar & make_nvp("progress", progress_);
710    }
711
712        void setEntryData(libtorrent::entry metadata, libtorrent::entry resumedata)
713        {               
714                metadata_ = metadata;
715                resumedata_ = resumedata;
716        }
717
718        std::vector<lbt::peer_info>& peers() { return peers_; }
719       
720        boost::tuple<size_t, size_t, size_t, size_t> updatePeers()
721        {
722                if (inSession())
723                        handle_.get_peer_info(peers_);
724               
725                size_t totalPeers = 0;
726                size_t peersConnected = 0;
727                size_t totalSeeds = 0;
728                size_t seedsConnected = 0;
729               
730                foreach (lbt::peer_info& peer, peers_) 
731                {
732                        float speedSum = peer.down_speed + peer.up_speed;
733                       
734                        if (!(peer.flags & lbt::peer_info::seed))
735                        {
736                                ++totalPeers;
737                               
738                                if (speedSum > 0)
739                                        ++peersConnected;
740                        }
741                        else
742                        {
743                                ++totalSeeds;
744                               
745                                if (speedSum > 0)
746                                        ++seedsConnected;
747                        }
748                }       
749               
750                return boost::make_tuple(totalPeers, peersConnected, totalSeeds, seedsConnected);
751        }
752       
753        void getPeerDetails(PeerDetails& peerDetails) const
754        {
755                if (inSession())
756                {
757                        foreach (lbt::peer_info peer, peers_) 
758                        {
759                                peerDetails.push_back(peer);
760                        }       
761                }
762        }
763
764        void getFileDetails(FileDetails& fileDetails)
765        {
766                if (fileDetailsMemory_.empty())
767                {
768                        lbt::torrent_info& info = infoMemory();
769                        std::vector<lbt::file_entry> files;
770                       
771                        std::copy(info.begin_files(), info.end_files(), 
772                                std::back_inserter(files));                                     
773                               
774                        if (filePriorities_.size() != files.size())
775                        {
776                                filePriorities_.clear();
777                                filePriorities_.assign(files.size(), 1);
778                        }
779                       
780                        for(size_t i=0, e=files.size(); i<e; ++i)
781                        {
782                                wstring_t fullPath = hal::from_utf8(files[i].path.string());
783                                size_t size = static_cast<size_t>(files[i].size);
784                               
785                                fileDetailsMemory_.push_back(FileDetail(fullPath, size, 0, filePriorities_[i], i));
786                        }       
787                }               
788               
789                if (inSession())
790                {                       
791                        std::vector<float> fileProgress;                       
792                        handle_.file_progress(fileProgress);
793                       
794                        for(size_t i=0, e=fileDetailsMemory_.size(); i<e; ++i)
795                        {
796                                fileDetailsMemory_[i].progress =  fileProgress[i];
797                                fileDetailsMemory_[i].priority =  filePriorities_[i];
798                        }                       
799                }
800               
801                fileDetails = fileDetailsMemory_;
802        }
803       
804        void prepare(wpath_t filename, wpath_t saveDirectory)
805        {
806                mutex_t::scoped_lock l(mutex_);
807               
808                if (fs::exists(filename)) 
809                        metadata_ = haldecode(filename);
810               
811                extractNames(metadata_);                       
812               
813                const wpath_t resumeFile = workingDir_/L"resume"/filename_;
814                const wpath_t torrentFile = workingDir_/L"torrents"/filename_;
815               
816                event().post(shared_ptr<EventDetail>(new EventMsg(
817                        wformat_t(L"File: %1%, %2%.") % resumeFile % torrentFile)));
818               
819                if (exists(resumeFile)) 
820                        resumedata_ = haldecode(resumeFile);
821
822                if (!exists(workingDir_/L"torrents"))
823                        create_directory(workingDir_/L"torrents");
824
825                if (!exists(torrentFile))
826                        copy_file(filename.string(), torrentFile);
827
828                if (!exists(saveDirectory))
829                        create_directory(saveDirectory);
830        }
831       
832        void extractNames(lbt::entry& metadata)
833        {
834                mutex_t::scoped_lock l(mutex_);
835               
836                lbt::torrent_info info(metadata);                               
837                name_ = hal::from_utf8_safe(info.name());
838               
839                filename_ = name_;
840                if (!boost::find_last(filename_, L".torrent")) 
841                                filename_ += L".torrent";
842               
843                event().post(shared_ptr<EventDetail>(new EventMsg(
844                        wformat_t(L"Loaded names: %1%, %2%") % name_ % filename_)));
845        }
846       
847        lbt::torrent_info& infoMemory()
848        {
849                if (!infoMemory_.is_valid()) infoMemory_ = lbt::torrent_info(metadata_);
850               
851                return infoMemory_;
852        }
853
854private:       
855        void applySettings()
856        {               
857                applyTransferSpeed();
858                applyConnectionLimit();
859                applyRatio();
860                applyTrackers();
861                applyTrackerLogin();
862                applyFilePriorities();
863                applyResolveCountries();
864        }
865       
866        void applyTransferSpeed()
867        {
868                if (inSession())
869                {
870                        int down = (transferLimit_.first > 0) ? static_cast<int>(transferLimit_.first*1024) : -1;
871                        handle_.set_download_limit(down);
872                       
873                        int up = (transferLimit_.second > 0) ? static_cast<int>(transferLimit_.second*1024) : -1;
874                        handle_.set_upload_limit(up);
875                }
876        }
877
878        void applyConnectionLimit()
879        {
880                if (inSession())
881                {
882                        handle_.set_max_connections(connections_);
883                        handle_.set_max_uploads(uploads_);
884                }
885        }
886       
887        void applyRatio()
888        { 
889                if (inSession())
890                        handle_.set_ratio(ratio_);
891        }
892       
893        void applyTrackers()
894        {
895                if (inSession())
896                {
897                        if (torrent_trackers_.empty())
898                                torrent_trackers_ = handle_.trackers();
899                       
900                        if (!trackers_.empty())
901                        {
902                                std::vector<lbt::announce_entry> trackers;
903                               
904                                foreach (const TrackerDetail& tracker, trackers_)
905                                {
906                                        trackers.push_back(
907                                                lbt::announce_entry(hal::to_utf8(tracker.url)));
908                                        trackers.back().tier = tracker.tier;
909                                }
910                                handle_.replace_trackers(trackers);
911                        }
912                }
913        }
914       
915        void applyTrackerLogin()
916        {
917                if (inSession())
918                {
919                        if (trackerUsername_ != L"")
920                        {
921                                handle_.set_tracker_login(hal::to_utf8(trackerUsername_),
922                                        hal::to_utf8(trackerPassword_));
923                        }
924                }
925        }
926       
927        void applyFilePriorities()
928        {               
929                if (inSession()) 
930                {
931                        if (!filePriorities_.empty())
932                                handle_.prioritize_files(filePriorities_);
933                }
934        }       
935       
936        void applyResolveCountries()
937        {
938                if (inSession())
939                        handle_.resolve_countries(resolve_countries_);
940        }
941       
942        void completedPauseEvent()
943        {
944                mutex_t::scoped_lock l(mutex_);
945               
946                event().post(shared_ptr<EventDetail>(
947                        new EventInfo(L"completedPauseEvent")));
948               
949                assert(inSession());           
950               
951                if (TorrentDetail::torrent_pausing == state_)
952                {
953                        state_ = TorrentDetail::torrent_paused; 
954                        assert(handle_.is_paused());
955                }
956                else if (TorrentDetail::torrent_stopping == state_)
957                {
958                        removeFromSession();
959                       
960                        state_ = TorrentDetail::torrent_stopped;
961                }
962        }
963       
964        static lbt::session* the_session_;
965        static wpath_t workingDir_;
966       
967        mutable mutex_t mutex_;
968       
969        std::pair<float, float> transferLimit_;
970       
971        unsigned state_;
972        int connections_;
973        int uploads_;
974        bool in_session_;
975        float ratio_;
976        bool resolve_countries_;
977       
978        wstring_t filename_;
979        wstring_t name_;
980        wstring_t save_directory_;
981        wstring_t originalFilename_;
982        lbt::torrent_handle handle_;   
983       
984        lbt::entry metadata_;
985        lbt::entry resumedata_;
986       
987        wstring_t trackerUsername_;     
988        wstring_t trackerPassword_;
989       
990        boost::int64_t totalUploaded_;
991        boost::int64_t totalBase_;
992       
993        TransferTracker<boost::int64_t> payloadUploaded_;
994        TransferTracker<boost::int64_t> payloadDownloaded_;
995        TransferTracker<boost::int64_t> uploaded_;
996        TransferTracker<boost::int64_t> downloaded_;
997       
998        pt::ptime startTime_;
999        pt::ptime finishTime_;
1000        DurationTracker activeDuration_;
1001        DurationTracker seedingDuration_;
1002       
1003        std::vector<TrackerDetail> trackers_;
1004        std::vector<lbt::announce_entry> torrent_trackers_;
1005        std::vector<lbt::peer_info> peers_;     
1006        std::vector<int> filePriorities_;
1007       
1008        float progress_;
1009       
1010        lbt::torrent_info infoMemory_;
1011        lbt::torrent_status statusMemory_;
1012        FileDetails fileDetailsMemory_;
1013       
1014        bool compactStorage_;
1015};
1016
1017typedef std::map<std::string, TorrentInternalOld> TorrentMap;
1018typedef std::pair<std::string, TorrentInternalOld> TorrentPair;
1019typedef shared_ptr<TorrentInternal> TorrentInternal_ptr;
1020
1021class TorrentManager : 
1022        public hal::IniBase<TorrentManager>
1023{
1024        typedef TorrentManager thisClass;
1025        typedef hal::IniBase<thisClass> iniClass;
1026
1027        struct TorrentHolder
1028        {
1029                mutable TorrentInternal_ptr torrent;
1030               
1031                wstring_t filename;
1032                wstring_t name;         
1033               
1034                TorrentHolder()
1035                {}
1036               
1037                explicit TorrentHolder(TorrentInternal_ptr t) :
1038                        torrent(t), filename(torrent->filename()), name(torrent->name())
1039                {}
1040                                               
1041                friend class boost::serialization::access;
1042                template<class Archive>
1043                void serialize(Archive& ar, const unsigned int version)
1044                {
1045                        if (version < 1)
1046                        {
1047                                TorrentInternalOld t;
1048                                ar & make_nvp("torrent", t);
1049                               
1050                                torrent.reset(new TorrentInternal(t));
1051                        }
1052                        else
1053                        {
1054                                ar & make_nvp("torrent", torrent);
1055                        } 
1056                       
1057                        ar & make_nvp("filename", filename);
1058                        ar & make_nvp("name", name);
1059                }
1060        };
1061       
1062        struct byFilename{};
1063        struct byName{};
1064       
1065        typedef boost::multi_index_container<
1066                TorrentHolder,
1067                boost::multi_index::indexed_by<
1068                        boost::multi_index::ordered_unique<
1069                                boost::multi_index::tag<byFilename>,
1070                                boost::multi_index::member<
1071                                        TorrentHolder, wstring_t, &TorrentHolder::filename> 
1072                                >,
1073                        boost::multi_index::ordered_unique<
1074                                boost::multi_index::tag<byName>,
1075                                boost::multi_index::member<
1076                                        TorrentHolder, wstring_t, &TorrentHolder::name> 
1077                                >
1078                >
1079        > TorrentMultiIndex;
1080       
1081public:
1082        typedef TorrentMultiIndex::index<byFilename>::type torrentByFilename;
1083        typedef TorrentMultiIndex::index<byName>::type torrentByName;
1084       
1085        TorrentManager(ini_file& ini) :
1086                iniClass("bittorrent", "TorrentManager", ini)
1087        {}
1088       
1089        TorrentManager& operator=(const TorrentMap& map)
1090        {
1091                torrents_.clear();
1092               
1093                for (TorrentMap::const_iterator i=map.begin(), e=map.end(); i != e; ++i)
1094                {       
1095                        TorrentInternal_ptr TIp(new TorrentInternal((*i).second));
1096                       
1097                        event().post(shared_ptr<EventDetail>(new EventMsg(
1098                                wformat_t(L"Converting %1%.") % TIp->name())));
1099                       
1100                        torrents_.insert(TorrentHolder(TIp));
1101                }
1102               
1103                return *this;
1104        }
1105       
1106        std::pair<torrentByName::iterator, bool> insert(const TorrentHolder& h)
1107        {
1108                return torrents_.get<byName>().insert(h);
1109        }
1110       
1111        std::pair<torrentByName::iterator, bool> insert(TorrentInternal_ptr t)
1112        {
1113                return insert(TorrentHolder(t));
1114        }
1115
1116        TorrentInternal_ptr getByFile(const wstring_t& filename)
1117        {
1118                torrentByFilename::iterator it = torrents_.get<byFilename>().find(filename);
1119               
1120                if (it != torrents_.get<byFilename>().end() && (*it).torrent)
1121                {
1122                        return (*it).torrent;
1123                }
1124               
1125                throw invalidTorrent(filename);
1126        }
1127       
1128        TorrentInternal_ptr get(const wstring_t& name)
1129        {
1130                torrentByName::iterator it = torrents_.get<byName>().find(name);
1131               
1132                if (it != torrents_.get<byName>().end() && (*it).torrent)
1133                {
1134                        return (*it).torrent;
1135                }
1136               
1137                throw invalidTorrent(name);
1138        }
1139       
1140        torrentByName::iterator erase(torrentByName::iterator where)
1141        {
1142                return torrents_.get<byName>().erase(where);
1143        }
1144       
1145        size_t size()
1146        {
1147                return torrents_.size();
1148        }
1149       
1150        size_t erase(const wstring_t& name)
1151        {
1152                return torrents_.get<byName>().erase(name);
1153        }
1154       
1155        bool exists(const wstring_t& name)
1156        {
1157                torrentByName::iterator it = torrents_.get<byName>().find(name);
1158               
1159                if (it != torrents_.get<byName>().end())
1160                        return true;
1161                else
1162                        return false;
1163        }
1164       
1165        torrentByName::iterator begin() { return torrents_.get<byName>().begin(); }
1166        torrentByName::iterator end() { return torrents_.get<byName>().end(); }
1167       
1168        friend class boost::serialization::access;
1169        template<class Archive>
1170        void serialize(Archive& ar, const unsigned int version)
1171        {
1172                ar & make_nvp("torrents", torrents_);
1173        }       
1174       
1175private:
1176        TorrentMultiIndex torrents_;
1177};
1178
1179void TorrentInternal::setConnectionLimit(int maxConn, int maxUpload)
1180{
1181        connections_ = maxConn;
1182        uploads_ = maxUpload;
1183       
1184        applyConnectionLimit();
1185}
1186
1187std::pair<int, int> TorrentInternal::getConnectionLimit()
1188{
1189        return std::make_pair(connections_, uploads_);
1190}
1191
1192void TorrentInternal::setTransferSpeed(float download, float upload)
1193{       
1194        transferLimit_ = std::make_pair(download, upload);
1195       
1196        applyTransferSpeed();
1197}
1198
1199std::pair<float, float> TorrentInternal::getTransferSpeed()
1200{
1201        return transferLimit_;
1202}
1203
1204TorrentDetail_ptr TorrentInternal::getTorrentDetail_ptr()
1205{       
1206        try
1207        {
1208
1209        if (inSession())
1210        {
1211                statusMemory_ = handle_.status();
1212                progress_ = statusMemory_.progress;
1213        }
1214        else
1215        {
1216                // Wipe these cause they don't make sense for a non-active torrent.
1217               
1218                statusMemory_.download_payload_rate = 0;
1219                statusMemory_.upload_payload_rate = 0;
1220                statusMemory_.next_announce = boost::posix_time::seconds(0);           
1221        }
1222       
1223        wstring_t state;
1224       
1225        switch (state_)
1226        {
1227        case TorrentDetail::torrent_paused:
1228                state = app().res_wstr(HAL_TORRENT_PAUSED);
1229                break;
1230               
1231        case TorrentDetail::torrent_pausing:
1232                state = app().res_wstr(HAL_TORRENT_PAUSING);
1233                break;
1234               
1235        case TorrentDetail::torrent_stopped:
1236                state = app().res_wstr(HAL_TORRENT_STOPPED);
1237                break;
1238               
1239        case TorrentDetail::torrent_stopping:
1240                state = app().res_wstr(HAL_TORRENT_STOPPING);
1241                break;
1242               
1243        default:
1244                switch (statusMemory_.state)
1245                {
1246                case lbt::torrent_status::queued_for_checking:
1247                        state = app().res_wstr(HAL_TORRENT_QUEUED_CHECKING);
1248                        break;
1249                case lbt::torrent_status::checking_files:
1250                        state = app().res_wstr(HAL_TORRENT_CHECKING_FILES);
1251                        break;
1252                case lbt::torrent_status::connecting_to_tracker:
1253                        state = app().res_wstr(HAL_TORRENT_CONNECTING);
1254                        break;
1255                case lbt::torrent_status::downloading_metadata:
1256                        state = app().res_wstr(HAL_TORRENT_METADATA);
1257                        break;
1258                case lbt::torrent_status::downloading:
1259                        state = app().res_wstr(HAL_TORRENT_DOWNLOADING);
1260                        break;
1261                case lbt::torrent_status::finished:
1262                        state = app().res_wstr(HAL_TORRENT_FINISHED);
1263                        break;
1264                case lbt::torrent_status::seeding:
1265                        state = app().res_wstr(HAL_TORRENT_SEEDING);
1266                        break;
1267                case lbt::torrent_status::allocating:
1268                        state = app().res_wstr(HAL_TORRENT_ALLOCATING);
1269                        break;
1270                }       
1271        }
1272       
1273        pt::time_duration td(pt::pos_infin);
1274       
1275        if (statusMemory_.download_payload_rate != 0)
1276        {
1277                td = boost::posix_time::seconds(       
1278                        long(float(statusMemory_.total_wanted-statusMemory_.total_wanted_done) / statusMemory_.download_payload_rate));
1279        }
1280       
1281        totalUploaded_ += (statusMemory_.total_payload_upload - totalBase_);
1282        totalBase_ = statusMemory_.total_payload_upload;
1283       
1284        uploaded_.update(statusMemory_.total_upload);
1285        payloadUploaded_.update(statusMemory_.total_payload_upload);
1286        downloaded_.update(statusMemory_.total_download);
1287        payloadDownloaded_.update(statusMemory_.total_payload_download);
1288       
1289        if (isActive())
1290        {
1291                activeDuration_.update();
1292               
1293                if (lbt::torrent_status::seeding == statusMemory_.state)
1294                        seedingDuration_.update();
1295        }       
1296       
1297        boost::tuple<size_t, size_t, size_t, size_t> connections = updatePeers();               
1298
1299        return TorrentDetail_ptr(new TorrentDetail(name_, filename_, state, hal::from_utf8(statusMemory_.current_tracker), 
1300                std::pair<float, float>(statusMemory_.download_payload_rate, statusMemory_.upload_payload_rate),
1301                progress_, statusMemory_.distributed_copies, statusMemory_.total_wanted_done, statusMemory_.total_wanted, uploaded_, payloadUploaded_,
1302                downloaded_, payloadDownloaded_, connections, ratio_, td, statusMemory_.next_announce, activeDuration_, seedingDuration_, startTime_, finishTime_));
1303
1304        }
1305        catch (const lbt::invalid_handle&)
1306        {
1307                event().post(shared_ptr<EventDetail>(
1308                        new EventInvalidTorrent(Event::critical, Event::invalidTorrent, to_utf8(name_), "getTorrentDetail_ptr")));
1309        }
1310        catch (const std::exception& e)
1311        {
1312                event().post(shared_ptr<EventDetail>(
1313                        new EventTorrentException(Event::critical, Event::torrentException, e.what(), to_utf8(name_), "getTorrentDetail_ptr")));
1314        }
1315       
1316        return TorrentDetail_ptr(new TorrentDetail(name_, filename_, app().res_wstr(HAL_TORRENT_STOPPED),  app().res_wstr(IDS_NA)));
1317}
1318
1319} // namespace hal
1320
1321BOOST_CLASS_VERSION(hal::TorrentManager::TorrentHolder, 1)
1322
1323#endif // RC_INVOKED
1324
Note: See TracBrowser for help on using the repository browser.