source: trunk/src/halTorrentInternal.hpp @ 574

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