source: trunk/src/halTorrentInternal.hpp @ 614

Revision 614, 36.8 KB checked in by Eoin, 12 years ago (diff)

ListView? grouping works. But a lacking on the API side means it's incompatible with sorting!

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