source: trunk/src/halTorrentInternal.hpp @ 392

Revision 392, 34.9 KB checked in by Eoin, 12 years ago (diff)

Tweaked for precompiled headers.

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