source: src/halTorrentInternal.hpp @ 344

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

Deletes files correctly for stopped torrents

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