source: trunk/src/halTorrentInternal.hpp @ 583

Revision 583, 35.7 KB checked in by Eoin, 12 years ago (diff)

Checking file bug tracked down.

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