source: trunk/src/halTorrentInternal.hpp @ 612

Revision 612, 36.3 KB checked in by Eoin, 12 years ago (diff)

Allocation mode combobox.

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                startTime_(boost::posix_time::second_clock::universal_time()), \
335                in_session_(false), \
336                queue_position_(0)
337               
338        torrent_internal() :   
339                TORRENT_INTERNALS_DEFAULTS,
340                allocation_(bit::sparse_allocation),
341                state_(torrent_details::torrent_stopped)
342        {
343                TORRENT_STATE_LOG(L"Torrent state machine initiate");
344                machine_.initiate();
345        }
346       
347                torrent_internal(wpath filename, wpath saveDirectory, bit::allocations alloc, wpath move_to_directory=L"") :
348                TORRENT_INTERNALS_DEFAULTS,
349                save_directory_(saveDirectory.string()),
350                move_to_directory_(move_to_directory.string()),
351                allocation_(alloc),     
352                state_(torrent_details::torrent_stopped)
353        {
354                assert(the_session_);   
355               
356                TORRENT_STATE_LOG(L"Torrent state machine initiate");
357                machine_.initiate();
358
359                prepare(filename);
360        }
361
362        #undef TORRENT_INTERNALS_DEFAULTS
363       
364        torrent_details_ptr gettorrent_details_ptr()
365        {       
366                mutex_t::scoped_lock l(mutex_);
367
368                try
369                {
370
371                if (in_session())
372                {
373                        statusMemory_ = handle_.status();
374                        progress_ = statusMemory_.progress;
375
376                        queue_position_ = handle_.queue_position();
377                }
378                else
379                {
380                        // Wipe these cause they don't make sense for a non-active torrent.
381                       
382                        statusMemory_.download_payload_rate = 0;
383                        statusMemory_.upload_payload_rate = 0;
384                        statusMemory_.next_announce = boost::posix_time::seconds(0);           
385                }
386               
387                wstring state;
388               
389                switch (state_)
390                {
391                case torrent_details::torrent_paused:
392                        state = app().res_wstr(HAL_TORRENT_PAUSED);
393                        break;
394                       
395                case torrent_details::torrent_pausing:
396                        state = app().res_wstr(HAL_TORRENT_PAUSING);
397                        break;
398                       
399                case torrent_details::torrent_stopped:
400                        state = app().res_wstr(HAL_TORRENT_STOPPED);
401                        break;
402                       
403                case torrent_details::torrent_stopping:
404                        state = app().res_wstr(HAL_TORRENT_STOPPING);
405                        break;
406                       
407                default:
408                        switch (statusMemory_.state)
409                        {
410                        case libt::torrent_status::queued_for_checking:
411                                state = app().res_wstr(HAL_TORRENT_QUEUED_CHECKING);
412                                break;
413                        case libt::torrent_status::checking_files:
414                                state = app().res_wstr(HAL_TORRENT_CHECKING_FILES);
415                                break;
416//                      case libt::torrent_status::connecting_to_tracker:
417//                              state = app().res_wstr(HAL_TORRENT_CONNECTING);
418//                              break;
419                        case libt::torrent_status::downloading_metadata:
420                                state = app().res_wstr(HAL_TORRENT_METADATA);
421                                break;
422                        case libt::torrent_status::downloading:
423                                state = app().res_wstr(HAL_TORRENT_DOWNLOADING);
424                                break;
425                        case libt::torrent_status::finished:
426                                state = app().res_wstr(HAL_TORRENT_FINISHED);
427                                break;
428                        case libt::torrent_status::seeding:
429                                state = app().res_wstr(HAL_TORRENT_SEEDING);
430                                break;
431                        case libt::torrent_status::allocating:
432                                state = app().res_wstr(HAL_TORRENT_ALLOCATING);
433                                break;
434                        }       
435                }
436               
437                pt::time_duration td(pt::pos_infin);
438               
439                if (statusMemory_.download_payload_rate != 0)
440                {
441                        td = boost::posix_time::seconds(       
442                                long(float(statusMemory_.total_wanted-statusMemory_.total_wanted_done) / statusMemory_.download_payload_rate));
443                }
444               
445                totalUploaded_ += (statusMemory_.total_payload_upload - totalBase_);
446                totalBase_ = statusMemory_.total_payload_upload;
447               
448                uploaded_.update(statusMemory_.total_upload);
449                payloadUploaded_.update(statusMemory_.total_payload_upload);
450                downloaded_.update(statusMemory_.total_download);
451                payloadDownloaded_.update(statusMemory_.total_payload_download);
452               
453                if (is_active())
454                {
455                        activeDuration_.update();
456                       
457                        if (libt::torrent_status::seeding == statusMemory_.state)
458                                seedingDuration_.update();
459                }       
460               
461                boost::tuple<size_t, size_t, size_t, size_t> connections = updatePeers();       
462
463                return torrent_details_ptr(new torrent_details(name_, filename_, saveDirectory().string(), state, hal::from_utf8(statusMemory_.current_tracker), 
464                        std::pair<float, float>(statusMemory_.download_payload_rate, statusMemory_.upload_payload_rate),
465                        progress_, statusMemory_.distributed_copies, statusMemory_.total_wanted_done, statusMemory_.total_wanted, uploaded_, payloadUploaded_,
466                        downloaded_, payloadDownloaded_, connections, ratio_, td, statusMemory_.next_announce, activeDuration_, seedingDuration_, startTime_, finishTime_, queue_position_));
467
468                }
469                catch (const libt::invalid_handle&)
470                {
471                        event_log.post(shared_ptr<EventDetail>(
472                                new EventInvalidTorrent(event_logger::critical, event_logger::invalidTorrent, to_utf8(name_), "gettorrent_details_ptr")));
473                }
474                catch (const std::exception& e)
475                {
476                        event_log.post(shared_ptr<EventDetail>(
477                                new EventTorrentException(event_logger::critical, event_logger::torrentException, e.what(), to_utf8(name_), "gettorrent_details_ptr")));
478                }
479               
480                return torrent_details_ptr(new torrent_details(name_, filename_, saveDirectory().string(), app().res_wstr(HAL_TORRENT_STOPPED), app().res_wstr(HAL_NA)));
481        }
482
483        void setTransferSpeed(float down, float up)
484        {       
485                mutex_t::scoped_lock l(mutex_);
486
487                transferLimit_ = std::make_pair(down, up);
488               
489                applyTransferSpeed();
490        }
491
492        void setConnectionLimit(int maxConn, int maxUpload)             
493        {
494                mutex_t::scoped_lock l(mutex_);
495
496                connections_ = maxConn;
497                uploads_ = maxUpload;
498               
499                applyConnectionLimit();
500        }
501
502        std::pair<float, float> getTransferSpeed()
503        {
504                return transferLimit_;
505        }
506
507        std::pair<int, int> getConnectionLimit()
508        {
509                return std::make_pair(connections_, uploads_);
510        }
511       
512        const wstring& name() const { return name_; }
513       
514        void set_ratio(float ratio) 
515        { 
516                if (ratio < 0) ratio = 0;
517                ratio_ = ratio; 
518               
519                apply_ratio();
520        }
521       
522        float get_ratio()
523        {
524                return ratio_;
525        }
526       
527        void add_to_session(bool paused = false)
528        {
529                try
530                {
531
532                mutex_t::scoped_lock l(mutex_); 
533                assert(the_session_ != 0);
534
535                HAL_DEV_MSG(hal::wform(L"add_to_session() paused=%1%") % paused);
536               
537                if (!in_session()) 
538                {       
539                        libt::add_torrent_params p;
540
541                        string torrent_file = to_utf8((hal::app().get_working_directory()/L"torrents"/filename_).string());
542                        info_memory_.reset(new libt::torrent_info(torrent_file.c_str()));
543
544                        std::string resume_file = to_utf8((hal::app().get_working_directory()/L"resume" / (name_ + L".fastresume")).string());
545
546                        std::vector<char> buf;
547                        if (libt::load_file(resume_file.c_str(), buf) == 0)
548                        {
549                                HAL_DEV_MSG(L"Using resume data");
550                                p.resume_data = &buf;
551                        }
552
553                        p.ti = info_memory_;
554                        p.save_path = path_to_utf8(save_directory_);
555                        p.storage_mode = hal_allocation_to_libt(allocation_);
556                        p.paused = paused;
557                        p.duplicate_is_error = false;
558                        p.auto_managed = false;
559
560                        handle_ = the_session_->add_torrent(p);         
561                        assert(handle_.is_valid());
562                        in_session_ = true;
563                       
564                //      clear_resume_data();
565                //      handle_.force_reannounce();
566                }       
567
568                assert(in_session());
569                HAL_DEV_MSG(L"Added to session");
570
571                if (handle_.is_paused())
572                        state_ = torrent_details::torrent_paused;       
573
574                }
575                catch(std::exception& e)
576                {
577                        hal::event_log.post(boost::shared_ptr<hal::EventDetail>(
578                                new hal::EventStdException(event_logger::critical, e, L"addToSession"))); 
579                }
580        }
581       
582        bool remove_from_session(bool write_data=true)
583        {
584                try
585                {
586
587                mutex_t::scoped_lock l(mutex_);
588                assert(in_session());
589
590                HAL_DEV_MSG(hal::wform(L"remove_from_session() writeData=%1%") % write_data);
591               
592                if (write_data)
593                {
594                        HAL_DEV_MSG(L"requesting resume data");                 
595               
596                        signaler_wrapper<>* sig = new signaler_wrapper<>(bind(&torrent_internal::remove_from_session, this, false));
597                        signals().resume_data.connect(bind(&signaler_wrapper<>::operator(), sig));
598                       
599                        handle_.save_resume_data();
600
601                        return false;
602                }
603                else
604                {               
605                        HAL_DEV_MSG(L"removing handle from session");
606                        the_session_->remove_torrent(handle_);
607                        in_session_ = false;
608
609                        assert(!in_session()); 
610                        HAL_DEV_MSG(L"Removed from session!");
611
612                        return true;
613                }
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"remove_fromSession"))); 
620                        return false;
621                }
622        }
623       
624        bool in_session() const
625        { 
626                mutex_t::scoped_lock l(mutex_);
627
628                return (in_session_ && the_session_ != 0 && handle_.is_valid());
629        }
630       
631        void resume()
632        {
633                mutex_t::scoped_lock l(mutex_);
634
635                if (state_ == torrent_details::torrent_stopped)
636                {       
637                        add_to_session(false);
638                        assert(in_session());                   
639                }
640                else
641                {
642                        assert(in_session());
643                        handle_.resume();
644                }       
645               
646                state_ = torrent_details::torrent_active;                       
647                //assert(!handle_.is_paused());
648        }
649       
650        void pause()
651        {
652                mutex_t::scoped_lock l(mutex_);
653
654                if (state_ == torrent_details::torrent_stopped)
655                {       
656                        add_to_session(true);
657
658                        assert(in_session());
659                        assert(handle_.is_paused());
660                }
661                else
662                {
663                        assert(in_session());
664
665                        HAL_DEV_MSG(hal::wform(L"pause() - handle_.pause()"));
666                        handle_.pause();
667
668                        signaler_wrapper<>* sig = new signaler_wrapper<>(bind(&torrent_internal::completed_pause, this));
669                        signals().torrent_paused.connect(bind(&signaler_wrapper<>::operator(), sig));
670
671                        state_ = torrent_details::torrent_pausing;     
672                }                       
673        }
674       
675        void stop()
676        {
677                mutex_t::scoped_lock l(mutex_);
678
679                if (state_ != torrent_details::torrent_stopped)
680                {
681                        if (state_ == torrent_details::torrent_active)
682                        {
683                                assert(in_session());
684
685                                signaler_wrapper<>* sig = new signaler_wrapper<>(bind(&torrent_internal::completed_stop, this));
686                                signals().torrent_paused.connect(bind(&signaler_wrapper<>::operator(), sig));
687                               
688                                HAL_DEV_MSG(hal::wform(L"stop() - handle_.pause()"));
689                                handle_.pause();
690
691                                state_ = torrent_details::torrent_stopping;
692                        }
693                        else if (state_ == torrent_details::torrent_paused)
694                        {                       
695                                remove_from_session();
696                                state_ = torrent_details::torrent_stopped;                             
697                        }
698                }
699        }
700
701        void set_state_stopped()
702        {
703                state_ = torrent_details::torrent_stopped;
704        }
705
706        void force_recheck()
707        {
708                mutex_t::scoped_lock l(mutex_);         
709                HAL_DEV_MSG(L"force_recheck()");
710
711                switch (state_)
712                {
713                case torrent_details::torrent_stopped:
714                        clear_resume_data();
715                        resume();
716                        break;
717
718                case torrent_details::torrent_stopping:
719                case torrent_details::torrent_pausing:
720//                      signals().torrent_paused.disconnect_all_once();
721
722                case torrent_details::torrent_active:
723//                      signals().torrent_paused.disconnect_all_once();
724//                      signals().torrent_paused.connect_once(bind(&torrent_internal::handle_recheck, this));
725                        handle_.pause();
726                        state_ = torrent_details::torrent_pausing;
727                        break;
728
729                default:
730                        assert(false);
731                };
732        }
733       
734        void write_resume_data(const libt::entry& ent)
735        {                                       
736                HAL_DEV_MSG(L"write_resume_data()");
737
738                wpath resume_dir = hal::app().get_working_directory()/L"resume";
739               
740                if (!exists(resume_dir))
741                        create_directory(resume_dir);
742
743                boost::filesystem::ofstream out(resume_dir/(name_ + L".fastresume"), std::ios_base::binary);
744                out.unsetf(std::ios_base::skipws);
745                bencode(std::ostream_iterator<char>(out), ent);
746
747                HAL_DEV_MSG(L"Written!");
748        }
749       
750        void clear_resume_data()
751        {
752                wpath resume_file = hal::app().get_working_directory()/L"resume"/filename_;
753               
754                if (exists(resume_file))
755                        remove(resume_file);
756
757//              resumedata_ = libt::entry();
758        }
759
760        const wpath get_save_directory()
761        {
762                return save_directory_;
763        }
764
765        void set_save_directory(wpath s, bool force=false)
766        {
767                if (in_session() && !is_finished() &&
768                                s != path_from_utf8(handle_.save_path()))
769                {
770                        handle_.move_storage(path_to_utf8(s));
771                        save_directory_ = s;
772                }
773                else if (!in_session() && force)
774                {
775                        save_directory_ = s;
776                }
777        }
778
779        const wpath get_move_to_directory()
780        {
781                return move_to_directory_;
782        }
783       
784        void set_move_to_directory(wpath m)
785        {
786                if (is_finished() && !m.empty())
787                {
788                        if (m != path_from_utf8(handle_.save_path()))
789                        {
790                                handle_.move_storage(path_to_utf8(m));
791                                save_directory_ = move_to_directory_ = m;
792                        }
793                }
794                else
795                {
796                        move_to_directory_ = m;
797                }
798        }
799
800        bool is_finished()
801        {
802                if (in_session())
803                {
804                        libt::torrent_status::state_t s = handle_.status().state;
805
806                        return (s == libt::torrent_status::seeding ||
807                                                s == libt::torrent_status::finished);
808                }
809                else return false;
810        }
811       
812        void finished()
813        {
814                if (finishTime_.is_special())
815                        finishTime_ = boost::posix_time::second_clock::universal_time();
816
817                if (is_finished())
818                {
819                        if (!move_to_directory_.empty() && 
820                                        move_to_directory_ !=  path_from_utf8(handle_.save_path()))
821                        {
822                                handle_.move_storage(path_to_utf8(move_to_directory_));
823                                save_directory_ = move_to_directory_;
824                        }
825                }
826        }
827       
828        bool is_active() const { return state_ == torrent_details::torrent_active; }
829       
830        unsigned state() const { return state_; }
831       
832        void setTrackerLogin(wstring username, wstring password)
833        {
834                trackerUsername_ = username;
835                trackerPassword_ = password;
836               
837                applyTrackerLogin();
838        }       
839       
840        std::pair<wstring, wstring> getTrackerLogin() const
841        {
842                return make_pair(trackerUsername_, trackerPassword_);
843        }
844       
845        const wstring& filename() const { return filename_; }
846       
847        const wstring& originalFilename() const { return originalFilename_; }
848       
849        const libt::torrent_handle& handle() const { return handle_; }
850
851        void resetTrackers()
852        {
853                if (in_session())
854                {
855                        handle_.replace_trackers(torrent_trackers_);           
856                        trackers_.clear();
857                }
858        }
859       
860        void setTrackers(const std::vector<tracker_detail>& tracker_details)
861        {
862                trackers_.clear();
863                trackers_.assign(tracker_details.begin(), tracker_details.end());
864               
865                applyTrackers();
866        }
867       
868        const std::vector<tracker_detail>& getTrackers()
869        {
870                if (trackers_.empty() && info_memory_)
871                {
872                        std::vector<libt::announce_entry> trackers = info_memory_->trackers();
873                       
874                        foreach (const libt::announce_entry& entry, trackers)
875                        {
876                                trackers_.push_back(
877                                        tracker_detail(hal::from_utf8(entry.url), entry.tier));
878                        }
879                }               
880                return trackers_;
881        }
882       
883        void setFilePriorities(std::vector<int> fileIndices, int priority)
884        {
885                if (!filePriorities_.empty())
886                {
887                        foreach(int i, fileIndices)
888                                filePriorities_[i] = priority;
889                               
890                        applyFilePriorities();
891                }
892        }
893
894        const wpath& saveDirectory() { return save_directory_; }
895       
896    friend class boost::serialization::access;
897    template<class Archive>
898    void serialize(Archive& ar, const unsigned int version)
899    {
900                using boost::serialization::make_nvp;
901
902                if (version > 1) {
903                        ar & make_nvp("transfer_limits", transferLimit_);
904                        ar & make_nvp("connection_limits", connections_);
905                        ar & make_nvp("upload_limits", uploads_);       
906
907                        ar & make_nvp("name", name_);
908                        ar & make_nvp("filename", filename_);   
909
910                        ar & make_nvp("ratio", ratio_); 
911                        ar & make_nvp("progress", progress_);
912                        ar & make_nvp("state", state_);
913//                      ar & make_nvp("compact_storage", compactStorage_);     
914                        ar & make_nvp("allocation_type", allocation_); 
915                        ar & make_nvp("resolve_countries", resolve_countries_); 
916
917                        ar & make_nvp("tracker_username", trackerUsername_);
918                        ar & make_nvp("tracker_password", trackerPassword_);
919                        ar & make_nvp("trackers", trackers_);
920
921                        ar & make_nvp("save_directory", save_directory_);
922                        ar & make_nvp("move_to_directory", move_to_directory_);
923                       
924                        ar & make_nvp("payload_uploaded", payloadUploaded_);
925                        ar & make_nvp("payload_downloaded", payloadDownloaded_);
926                        ar & make_nvp("uploaded", uploaded_);
927                        ar & make_nvp("downloaded", downloaded_);                       
928                                       
929                        ar & make_nvp("file_priorities", filePriorities_);
930                       
931                        ar & make_nvp("start_time", startTime_);
932                        ar & make_nvp("finish_time", finishTime_);
933                        ar & make_nvp("active_duration", activeDuration_);
934                        ar & make_nvp("seeding_duration", seedingDuration_);
935                                       
936                } 
937                else 
938                {
939                    ar & make_nvp("transferLimit", transferLimit_);
940                        ar & make_nvp("connections", connections_);
941                        ar & make_nvp("uploads", uploads_);                     
942                        ar & make_nvp("filename", filename_);   
943
944                        wstring s;
945                        ar & make_nvp("saveDirectory", s);
946                        save_directory_ = s;
947
948                        if (version == 2) {
949                                wstring m;
950                                ar & make_nvp("moveToDirectory", m);
951                                move_to_directory_ = m;
952                        } else {
953                                move_to_directory_ = save_directory_;
954                        }
955                       
956                        ar & make_nvp("payloadUploaded_", payloadUploaded_);
957                        ar & make_nvp("payloadDownloaded_", payloadDownloaded_);
958                        ar & make_nvp("uploaded_", uploaded_);
959                        ar & make_nvp("downloaded_", downloaded_);     
960                        ar & make_nvp("ratio", ratio_); 
961                        ar & make_nvp("trackerUsername", trackerUsername_);
962                        ar & make_nvp("trackerPassword", trackerPassword_);
963                       
964                        ar & make_nvp("state", state_);
965                        ar & make_nvp("trackers", trackers_);
966                       
967                        ar & make_nvp("resolve_countries", resolve_countries_);
968                       
969                        ar & make_nvp("file_priorities", filePriorities_);
970                       
971                        ar & make_nvp("startTime", startTime_);
972                        ar & make_nvp("activeDuration", activeDuration_);
973                        ar & make_nvp("seedingDuration", seedingDuration_);
974                       
975                        ar & make_nvp("name", name_);
976                        ar & make_nvp("compactStorage", compactStorage_);
977                        ar & make_nvp("finishTime", finishTime_);
978                       
979                        ar & make_nvp("progress", progress_);
980        }
981    }
982
983        void setEntryData(boost::intrusive_ptr<libt::torrent_info> metadata, libtorrent::entry resumedata)
984        {               
985                info_memory_ = metadata;
986//              resumedata_ = resumedata;
987        }
988
989        std::vector<libt::peer_info>& peers() { return peers_; }
990       
991        boost::tuple<size_t, size_t, size_t, size_t> updatePeers()
992        {
993                if (in_session())
994                        handle_.get_peer_info(peers_);
995               
996                size_t totalPeers = 0;
997                size_t peersConnected = 0;
998                size_t totalSeeds = 0;
999                size_t seedsConnected = 0;
1000               
1001                foreach (libt::peer_info& peer, peers_) 
1002                {
1003                        float speedSum = peer.down_speed + peer.up_speed;
1004                       
1005                        if (!(peer.flags & libt::peer_info::seed))
1006                        {
1007                                ++totalPeers;
1008                               
1009                                if (speedSum > 0)
1010                                        ++peersConnected;
1011                        }
1012                        else
1013                        {
1014                                ++totalSeeds;
1015                               
1016                                if (speedSum > 0)
1017                                        ++seedsConnected;
1018                        }
1019                }       
1020               
1021                return boost::make_tuple(totalPeers, peersConnected, totalSeeds, seedsConnected);
1022        }
1023       
1024        void getPeerDetails(PeerDetails& peerDetails) const
1025        {
1026                if (in_session())
1027                {
1028                        foreach (libt::peer_info peer, peers_) 
1029                        {
1030                                peerDetails.push_back(peer);
1031                        }       
1032                }
1033        }
1034
1035        void getFileDetails(FileDetails& fileDetails)
1036        {
1037                if (fileDetailsMemory_.empty())
1038                {
1039                        boost::intrusive_ptr<libt::torrent_info> info = info_memory();
1040                        std::vector<libt::file_entry> files;
1041                       
1042                        std::copy(info->begin_files(), info->end_files(), 
1043                                std::back_inserter(files));                                     
1044                               
1045                        if (filePriorities_.size() != files.size())
1046                        {
1047                                filePriorities_.clear();
1048                                filePriorities_.assign(files.size(), 1);
1049                        }
1050                       
1051                        for(size_t i=0, e=files.size(); i<e; ++i)
1052                        {
1053                                wstring fullPath = hal::from_utf8(files[i].path.string());
1054                                boost::int64_t size = static_cast<boost::int64_t>(files[i].size);
1055                               
1056                                fileDetailsMemory_.push_back(FileDetail(fullPath, size, 0, filePriorities_[i], i));
1057                        }       
1058                }               
1059               
1060                if (in_session())
1061                {                       
1062                        std::vector<libt::size_type> fileProgress;                     
1063                        handle_.file_progress(fileProgress);
1064                       
1065                        for(size_t i=0, e=fileDetailsMemory_.size(); i<e; ++i)
1066                                fileDetailsMemory_[i].progress =  fileProgress[i];                     
1067                }
1068
1069                for(size_t i=0, e=fileDetailsMemory_.size(); i<e; ++i)
1070                        fileDetailsMemory_[i].priority =  filePriorities_[i];
1071               
1072                fileDetails = fileDetailsMemory_;
1073        }
1074       
1075        void prepare(wpath filename)
1076        {
1077                mutex_t::scoped_lock l(mutex_);
1078               
1079                if (fs::exists(filename)) 
1080                        info_memory_ = new libt::torrent_info(path_to_utf8(filename));
1081               
1082                extractNames(info_memory());                   
1083               
1084                const wpath resumeFile = hal::app().get_working_directory()/L"resume"/filename_;
1085                const wpath torrentFile = hal::app().get_working_directory()/L"torrents"/filename_;
1086               
1087                event_log.post(shared_ptr<EventDetail>(new EventMsg(
1088                        hal::wform(L"File: %1%, %2%.") % resumeFile % torrentFile)));
1089               
1090        //      if (exists(resumeFile))
1091        //              resumedata_ = haldecode(resumeFile);
1092
1093                if (!exists(hal::app().get_working_directory()/L"torrents"))
1094                        create_directory(hal::app().get_working_directory()/L"torrents");
1095
1096                if (!exists(torrentFile))
1097                        copy_file(filename.string(), torrentFile);
1098
1099                if (!fs::exists(save_directory_))
1100                        fs::create_directory(save_directory_);
1101
1102                if (state_ == torrent_details::torrent_stopping)
1103                        state_ = torrent_details::torrent_stopped;
1104                else if (state_ == torrent_details::torrent_pausing)
1105                        state_ = torrent_details::torrent_paused;
1106        }
1107       
1108        void extractNames(boost::intrusive_ptr<libt::torrent_info> metadata)
1109        {
1110                mutex_t::scoped_lock l(mutex_);
1111                               
1112                name_ = hal::from_utf8_safe(metadata->name());
1113               
1114                filename_ = name_;
1115                if (!boost::find_last(filename_, L".torrent")) 
1116                                filename_ += L".torrent";
1117               
1118                event_log.post(shared_ptr<EventDetail>(new EventMsg(
1119                        hal::wform(L"Loaded names: %1%, %2%") % name_ % filename_)));
1120        }
1121       
1122        boost::intrusive_ptr<libt::torrent_info> info_memory()
1123        {
1124                if (!info_memory_) 
1125                        info_memory_ = 
1126                                boost::intrusive_ptr<libt::torrent_info>(new libt::torrent_info(path_to_utf8(filename())));
1127               
1128                return info_memory_;
1129        }
1130       
1131        signalers& signals()
1132        {
1133                mutex_t::scoped_lock l(mutex_);
1134                return signals_;
1135        }
1136
1137private:       
1138        signalers signals_;
1139
1140        void applySettings()
1141        {               
1142                applyTransferSpeed();
1143                applyConnectionLimit();
1144                apply_ratio();
1145                applyTrackers();
1146                applyTrackerLogin();
1147                applyFilePriorities();
1148                applyResolveCountries();
1149        }
1150       
1151        void applyTransferSpeed()
1152        {
1153                mutex_t::scoped_lock l(mutex_);
1154                if (in_session())
1155                {
1156                        int down = (transferLimit_.first > 0) ? static_cast<int>(transferLimit_.first*1024) : -1;
1157                        handle_.set_download_limit(down);
1158                       
1159                        int up = (transferLimit_.second > 0) ? static_cast<int>(transferLimit_.second*1024) : -1;
1160                        handle_.set_upload_limit(up);
1161
1162                        HAL_DEV_MSG(hal::wform(L"Applying Transfer Speed %1% - %2%") % down % up);
1163                }
1164        }
1165
1166        void applyConnectionLimit()
1167        {
1168                mutex_t::scoped_lock l(mutex_);
1169                if (in_session())
1170                {
1171                        handle_.set_max_connections(connections_);
1172                        handle_.set_max_uploads(uploads_);
1173
1174                        HAL_DEV_MSG(hal::wform(L"Applying Connection Limit %1% - %2%") % connections_ % uploads_);
1175                }
1176        }
1177       
1178        void apply_ratio()
1179        { 
1180                mutex_t::scoped_lock l(mutex_);
1181                if (in_session())
1182                {
1183                        handle_.set_ratio(ratio_);
1184
1185                        HAL_DEV_MSG(hal::wform(L"Applying Ratio %1%") % ratio_);
1186                }
1187        }
1188       
1189        void applyTrackers()
1190        {
1191                mutex_t::scoped_lock l(mutex_);
1192                if (in_session())
1193                {
1194                        if (torrent_trackers_.empty())
1195                                torrent_trackers_ = handle_.trackers();
1196                       
1197                        if (!trackers_.empty())
1198                        {
1199                                std::vector<libt::announce_entry> trackers;
1200                               
1201                                foreach (const tracker_detail& tracker, trackers_)
1202                                {
1203                                        trackers.push_back(
1204                                                libt::announce_entry(hal::to_utf8(tracker.url)));
1205                                        trackers.back().tier = tracker.tier;
1206                                }
1207                                handle_.replace_trackers(trackers);
1208                        }
1209                       
1210                        HAL_DEV_MSG(L"Applying Trackers");
1211                }
1212        }
1213       
1214        void applyTrackerLogin()
1215        {
1216                mutex_t::scoped_lock l(mutex_);
1217                if (in_session())
1218                {
1219                        if (trackerUsername_ != L"")
1220                        {
1221                                handle_.set_tracker_login(hal::to_utf8(trackerUsername_),
1222                                        hal::to_utf8(trackerPassword_));
1223                        }
1224
1225                        HAL_DEV_MSG(hal::wform(L"Applying Tracker Login User: %1%, Pass: %2%") % trackerUsername_ % trackerPassword_ );
1226                }
1227        }
1228       
1229        void applyFilePriorities()
1230        {               
1231                mutex_t::scoped_lock l(mutex_);
1232                if (in_session()) 
1233                {
1234                        if (!filePriorities_.empty())
1235                                handle_.prioritize_files(filePriorities_);
1236                       
1237                        HAL_DEV_MSG(L"Applying File Priorities");
1238                }
1239        }       
1240       
1241        void applyResolveCountries()
1242        {
1243                mutex_t::scoped_lock l(mutex_);
1244                if (in_session())
1245                {
1246                        handle_.resolve_countries(resolve_countries_);
1247                       
1248                        HAL_DEV_MSG(hal::wform(L"Applying Resolve Countries %1%") % resolve_countries_);
1249                }
1250        }
1251       
1252        bool completed_pause()
1253        {
1254                mutex_t::scoped_lock l(mutex_);
1255                assert(in_session());
1256                assert(handle_.is_paused());   
1257
1258                HAL_DEV_MSG(L"completed_pause()");
1259                               
1260                state_ = torrent_details::torrent_paused;       
1261
1262                return true;
1263        }
1264
1265        bool completed_stop()
1266        {
1267                mutex_t::scoped_lock l(mutex_);
1268                assert(in_session());
1269                assert(handle_.is_paused());                   
1270               
1271                if (remove_from_session())
1272                {
1273                        assert(!in_session());
1274                        HAL_DEV_MSG(L"completed_stop()");
1275                }
1276
1277                state_ = torrent_details::torrent_stopped;
1278
1279                return true;
1280        }
1281
1282        void handle_recheck()
1283        {
1284                mutex_t::scoped_lock l(mutex_);
1285                state_ = torrent_details::torrent_stopped;
1286
1287                remove_from_session(false);
1288                assert(!in_session());
1289
1290                clear_resume_data();
1291
1292                resume();
1293                assert(in_session());
1294
1295                HAL_DEV_MSG(L"handle_recheck()");
1296        }
1297               
1298        static libt::session* the_session_;
1299       
1300        mutable mutex_t mutex_;
1301
1302        torrent_state_machine machine_;
1303       
1304        std::pair<float, float> transferLimit_;
1305       
1306        unsigned state_;
1307        int connections_;
1308        int uploads_;
1309        bool in_session_;
1310        float ratio_;
1311        bool resolve_countries_;
1312       
1313        wstring filename_;
1314        wstring name_;
1315        wpath save_directory_;
1316        wpath move_to_directory_;
1317        wstring originalFilename_;
1318        libt::torrent_handle handle_;   
1319       
1320//      boost::intrusive_ptr<libt::torrent_info> metadata_;
1321//      boost::shared_ptr<libt::entry> resumedata_;
1322       
1323        wstring trackerUsername_;       
1324        wstring trackerPassword_;
1325       
1326        boost::int64_t totalUploaded_;
1327        boost::int64_t totalBase_;
1328       
1329        transfer_tracker<boost::int64_t> payloadUploaded_;
1330        transfer_tracker<boost::int64_t> payloadDownloaded_;
1331        transfer_tracker<boost::int64_t> uploaded_;
1332        transfer_tracker<boost::int64_t> downloaded_;
1333       
1334        pt::ptime startTime_;
1335        pt::ptime finishTime_;
1336        duration_tracker activeDuration_;
1337        duration_tracker seedingDuration_;
1338       
1339        std::vector<tracker_detail> trackers_;
1340        std::vector<libt::announce_entry> torrent_trackers_;
1341        std::vector<libt::peer_info> peers_;   
1342        std::vector<int> filePriorities_;
1343       
1344        float progress_;
1345       
1346        boost::intrusive_ptr<libt::torrent_info> info_memory_;
1347        libt::torrent_status statusMemory_;
1348        FileDetails fileDetailsMemory_;
1349       
1350        int queue_position_;
1351        bool compactStorage_;
1352        bit::allocations allocation_;
1353};
1354
1355typedef std::map<std::string, TorrentInternalOld> TorrentMap;
1356typedef std::pair<std::string, TorrentInternalOld> TorrentPair;
1357
1358class TorrentManager : 
1359        public hal::IniBase<TorrentManager>
1360{
1361        typedef TorrentManager thisClass;
1362        typedef hal::IniBase<thisClass> iniClass;
1363
1364        struct TorrentHolder
1365        {
1366                mutable torrent_internal_ptr torrent;
1367               
1368                wstring filename;
1369                wstring name;           
1370               
1371                TorrentHolder()
1372                {}
1373               
1374                explicit TorrentHolder(torrent_internal_ptr t) :
1375                        torrent(t), filename(torrent->filename()), name(torrent->name())
1376                {}
1377                                               
1378                friend class boost::serialization::access;
1379                template<class Archive>
1380                void serialize(Archive& ar, const unsigned int version)
1381                {
1382                        using boost::serialization::make_nvp;
1383
1384                        ar & make_nvp("torrent", torrent);
1385                        ar & make_nvp("filename", filename);
1386                        ar & make_nvp("name", name);
1387                }
1388        };
1389       
1390        struct byFilename{};
1391        struct byName{};
1392       
1393        typedef boost::multi_index_container<
1394                TorrentHolder,
1395                boost::multi_index::indexed_by<
1396                        boost::multi_index::ordered_unique<
1397                                boost::multi_index::tag<byFilename>,
1398                                boost::multi_index::member<
1399                                        TorrentHolder, wstring, &TorrentHolder::filename> 
1400                                >,
1401                        boost::multi_index::ordered_unique<
1402                                boost::multi_index::tag<byName>,
1403                                boost::multi_index::member<
1404                                        TorrentHolder, wstring, &TorrentHolder::name> 
1405                                >
1406                >
1407        > TorrentMultiIndex;
1408       
1409public:
1410        typedef TorrentMultiIndex::index<byFilename>::type torrentByFilename;
1411        typedef TorrentMultiIndex::index<byName>::type torrentByName;
1412       
1413        TorrentManager(ini_file& ini) :
1414                iniClass("bittorrent", "TorrentManager", ini)
1415        {}
1416
1417        std::pair<torrentByName::iterator, bool> insert(const TorrentHolder& h)
1418        {
1419                return torrents_.get<byName>().insert(h);
1420        }
1421       
1422        std::pair<torrentByName::iterator, bool> insert(torrent_internal_ptr t)
1423        {
1424                return insert(TorrentHolder(t));
1425        }
1426
1427        torrent_internal_ptr getByFile(const wstring& filename)
1428        {
1429                torrentByFilename::iterator it = torrents_.get<byFilename>().find(filename);
1430               
1431                if (it != torrents_.get<byFilename>().end() && (*it).torrent)
1432                {
1433                        return (*it).torrent;
1434                }
1435               
1436                throw invalidTorrent(filename);
1437        }
1438       
1439        torrent_internal_ptr get(const wstring& name)
1440        {
1441                torrentByName::iterator it = torrents_.get<byName>().find(name);
1442               
1443                if (it != torrents_.get<byName>().end() && (*it).torrent)
1444                {
1445                        return (*it).torrent;
1446                }
1447               
1448                throw invalidTorrent(name);
1449        }
1450       
1451        torrentByName::iterator erase(torrentByName::iterator where)
1452        {
1453                return torrents_.get<byName>().erase(where);
1454        }
1455       
1456        size_t size()
1457        {
1458                return torrents_.size();
1459        }
1460       
1461        size_t erase(const wstring& name)
1462        {
1463                return torrents_.get<byName>().erase(name);
1464        }
1465       
1466        bool exists(const wstring& name)
1467        {
1468                torrentByName::iterator it = torrents_.get<byName>().find(name);
1469               
1470                if (it != torrents_.get<byName>().end())
1471                        return true;
1472                else
1473                        return false;
1474        }
1475       
1476        torrentByName::iterator begin() { return torrents_.get<byName>().begin(); }
1477        torrentByName::iterator end() { return torrents_.get<byName>().end(); }
1478       
1479        friend class boost::serialization::access;
1480        template<class Archive>
1481        void serialize(Archive& ar, const unsigned int version)
1482        {
1483                ar & boost::serialization::make_nvp("torrents", torrents_);
1484        }       
1485       
1486private:
1487        TorrentMultiIndex torrents_;
1488};
1489
1490} // namespace hal
1491
1492BOOST_CLASS_VERSION(hal::TorrentManager::TorrentHolder, 1)
Note: See TracBrowser for help on using the repository browser.