source: trunk/src/halTorrentInternal.hpp @ 384

Revision 384, 33.3 KB checked in by Eoin, 12 years ago (diff)

Added 'Open Download Folder'.

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