source: trunk/src/halTorrentInternal.hpp @ 575

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