Esempio n. 1
0
    def main(self):
        """\
        Start the Mainline client and block forever listening for connectons
        """

        uiname = "bittorrent-console"
        defaults = get_defaults(uiname)
        config, args = configfile.parse_configuration_and_args(
            defaults, uiname)
        config = Preferences().initWithDict(config)
        data_dir = config['data_dir']
        self.core_doneflag = DeferredEvent()
        self.rawserver_doneflag = DeferredEvent()

        rawserver = RawServer(config)  #event and I/O scheduler
        self.multitorrent = MultiTorrent(
            config, rawserver,
            data_dir)  #class used to add, control and remove torrents

        self.tick()  #add periodic function call

        rawserver.add_task(0, self.core_doneflag.addCallback,
                           lambda r: rawserver.external_add_task(0, shutdown))
        rawserver.listen_forever(
            self.rawserver_doneflag)  # runs until the component terminates

        self.send(producerFinished(self), "signal")
        print "TorrentClient has shutdown"
Esempio n. 2
0
    def main(self):
        """\
        Start the Mainline client and block indefinitely, listening for connectons.
        """
      
        uiname = "bittorrent-console"
        defaults = get_defaults(uiname)
        config, args = configfile.parse_configuration_and_args(defaults, uiname)
        config = Preferences().initWithDict(config)
        data_dir = config['data_dir']
        self.core_doneflag = DeferredEvent()
        self.rawserver_doneflag = DeferredEvent()
        
        rawserver = RawServer(config) #event and I/O scheduler
        self.multitorrent = MultiTorrent(config, rawserver, data_dir) #class used to add, control and remove torrents

        self.tick() #add periodic function call
    
        rawserver.add_task(0, self.core_doneflag.addCallback, lambda r: rawserver.external_add_task(0, shutdown))
        rawserver.listen_forever(self.rawserver_doneflag) # runs until the component terminates

        self.send(producerFinished(self), "signal")
Esempio n. 3
0
class TorrentClient(threadedcomponent):
    """\
    TorrentClient([tickInterval]) -> component capable of downloading/sharing torrents.

    Initialises the Mainline client.
    Uses threadedcomponent so it doesn't have to worry about blocking I/O or making
    Mainline yield periodically.
   
    Keyword arguments:
    
    - tickInterval -- the interval in seconds at which TorrentClient checks inboxes (default=5)
    """
    
    Inboxes = {
        "inbox"   : "Torrent IPC - add a torrent, stop a torrent etc.",
        "control" : "Shut me down"
    }
    Outboxes = {
        "outbox"  : "Torrent IPC - status updates, completion, new torrent added etc.",
        "signal"  : "Say when I've shutdown"
    }

    def __init__(self, tickInterval = 5):
        super(TorrentClient, self).__init__()
        self.totaltorrents = 0
        self.torrents = {}
        self.torrentinfohashes = {}
        self.tickInterval = tickInterval #seconds
        
    def main(self):
        """\
        Start the Mainline client and block indefinitely, listening for connectons.
        """
      
        uiname = "bittorrent-console"
        defaults = get_defaults(uiname)
        config, args = configfile.parse_configuration_and_args(defaults, uiname)
        config = Preferences().initWithDict(config)
        data_dir = config['data_dir']
        self.core_doneflag = DeferredEvent()
        self.rawserver_doneflag = DeferredEvent()
        
        rawserver = RawServer(config) #event and I/O scheduler
        self.multitorrent = MultiTorrent(config, rawserver, data_dir) #class used to add, control and remove torrents

        self.tick() #add periodic function call
    
        rawserver.add_task(0, self.core_doneflag.addCallback, lambda r: rawserver.external_add_task(0, shutdown))
        rawserver.listen_forever(self.rawserver_doneflag) # runs until the component terminates

        self.send(producerFinished(self), "signal")
#        print ("TorrentClient has shutdown")
                        
    def startTorrent(self, metainfo, save_incomplete_as, save_as, torrentid):
        """startTorrent causes MultiTorrent to begin downloading a torrent eventually.
        Use it instead of _start_torrent as it retries repeatedly if Mainline is busy."""
        
        self._create_torrent(metainfo, save_incomplete_as, save_as)
        self.multitorrent.rawserver.add_task(1, self._start_torrent, metainfo, torrentid)
            
    def _create_torrent(self, metainfo, save_incomplete_as, save_as):
        if not self.multitorrent.torrent_known(metainfo.infohash):
            df = self.multitorrent.create_torrent(metainfo, save_incomplete_as, save_as)                
        #except Exception:
        #    e = sys.exc_info()[1]
        #    print (e)
        #    return False
                
    def _start_torrent(self, metainfo, torrentid):
        #try:
        t = None
        if self.multitorrent.torrent_known( metainfo.infohash ):
            t = self.multitorrent.get_torrent(metainfo.infohash)
    
        # HACK!! Rewrite when INITIALIZING state is available.
        if t is None or not t.is_initialized():
            #self.logger.debug( "Waiting for torrent to initialize." )
            self.multitorrent.rawserver.add_task(3, self._start_torrent, metainfo, torrentid)
            return

        if not self.multitorrent.torrent_running(metainfo.infohash):
            df = self.multitorrent.start_torrent(metainfo.infohash)
            self.torrents[torrentid] = self.multitorrent.get_torrent(metainfo.infohash)
            
            #yield df
            #df.getResult()  # raises exception if one occurred in yield.
        
        #    print (e)
        #    print ("Failed to start torrent")

    def decodeTorrent(self, data):
        """\
        Converts bencoded raw metadata (as one would find in a .torrent file) into
        a metainfo object (which one can then get the torrent's properties from).
        """
            
        from BitTorrent.bencode import bdecode, bencode
        metainfo = None
        try:
            b = bdecode(data)
            metainfo = ConvertedMetainfo(b)
        except Exception:
            e = sys.exc_info()[1]
            pass
        return metainfo
    
    def handleMessages(self):
        while self.dataReady("inbox"):
            temp = self.recv("inbox")
            if isinstance(temp, TIPCCreateNewTorrent) or isinstance(temp, str):
                if isinstance(temp, str):
                    metainfo = self.decodeTorrent(temp)
                else:
                    metainfo = self.decodeTorrent(temp.rawmetainfo)
                if metainfo != None:
                    savefolder = os.path.join("./",metainfo.name_fs)
                    
                    existingTorrentId = self.torrentinfohashes.get(metainfo.infohash, 0)
                    if existingTorrentId != 0:
                        self.send(TIPCTorrentAlreadyDownloading(torrentid=existingTorrentId), "outbox")
                    else:
                        self.totaltorrents += 1
                        self.torrentinfohashes[metainfo.infohash] = self.totaltorrents
                        self.torrents[self.totaltorrents] = MakeshiftTorrent(metainfo)                    
                        self.startTorrent(metainfo, savefolder, savefolder, self.totaltorrents)
                        self.send(TIPCNewTorrentCreated(torrentid=self.totaltorrents, savefolder=savefolder), "outbox")
            elif isinstance(temp, TIPCCloseTorrent):
                try:
                    torrent = self.torrents.get(temp.torrentid, None)
                    self.multitorrent.remove_torrent(torrent.metainfo.infohash)
                    self.torrentinfohashes.pop(torrent.metainfo.infohash)
                    self.torrents.pop(temp.torrentid)
                except KeyError:
                    pass
                    
        while self.dataReady("control"):
            temp = self.recv("control")
            if isinstance(temp, shutdown):
#                print ("TorrentClient trying to shutdown")
                #cause us to shutdown
                self.rawserver_doneflag.set()
                self.core_doneflag.set()
                
    def sendStatusUpdates(self):
        "Send a TIPCTorrentStatusUpdate for each running torrent."
        for torrentid, torrent in self.torrents.items():
            if not isinstance(torrent, MakeshiftTorrent):
                self.send(TIPCTorrentStatusUpdate(torrentid=torrentid, statsdictionary=torrent.get_status()), "outbox")
        
    def tick(self):
        """\
        Called periodically... by itself (gets rawserver to call it back after a delay of
        tickInterval seconds). Checks inboxes and sends a status-update message for every
        active torrent.
        """

        self.multitorrent.rawserver.add_task(self.tickInterval, self.tick)
        #print ("Tick")
        self.handleMessages()
        self.sendStatusUpdates()
    def __init__(self, config, display, configfile_key):
      """Starts torrents for all .torrent files in a directory tree.

         All errors are logged using Python logging to 'configfile_key' logger.

         @param config: Preferences object storing config.
         @param display: output function for stats.
      """

      # 4.4.x version of LaunchMany output exceptions to a displayer.
      # This version only outputs stats to the displayer.  We do not use
      # the logger to output stats so that a caller-provided object
      # can provide stats formatting as opposed to using the
      # logger Formatter, which is specific to exceptions, warnings, and
      # info messages.
      self.logger = logging.getLogger(configfile_key)
      try:
        self.multitorrent = None
        self.rawserver = None
        self.config = config
        self.configfile_key = configfile_key
        self.display = display

        self.torrent_dir = config['torrent_dir']

        # Ex: torrent_cache = infohash -> (path,metainfo)
        self.torrent_cache = {}

        # maps path -> [(modification time, size), infohash]
        self.file_cache = {}

        # used as set containing paths of files that do not have separate
        # entries in torrent_cache either because torrent_cache already
        # contains the torrent or because the torrent file is corrupt.
        self.blocked_files = {}

        #self.torrent_list = [] 
        #self.downloads = {}

        self.hashcheck_queue = []
        #self.hashcheck_store = {}
        self.hashcheck_current = None
                         
        self.core_doneflag = DeferredEvent()
        self.rawserver = RawServer(self.config)
        try:
   
            # set up shut-down procedure before we begin doing things that
            # can throw exceptions.
            def shutdown():
                self.logger.critical(_("shutting down"))
                for t in self.multitorrent.get_torrents():
                    self.logger.info(_('dropped "%s"') %
                                    self.torrent_cache[t.infohash][0])
                if self.multitorrent:
                    df = self.multitorrent.shutdown()
                    set_flag = lambda *a : self.rawserver.stop()
                    df.addCallbacks(set_flag, set_flag)
                else:
                    self.rawserver.stop()
                
            # It is safe to addCallback here, because there is only one thread,
            # but even if the code were multi-threaded, core_doneflag has not
            # been passed to anyone.  There is no chance of a race condition
            # between the DeferredEvent's callback and addCallback.
            self.core_doneflag.addCallback(
                lambda r: self.rawserver.external_add_task(0, shutdown))

            self.rawserver.install_sigint_handler(self.core_doneflag)
    
            data_dir = config['data_dir']
            self.multitorrent = MultiTorrent(config, self.rawserver, data_dir,
                                             resume_from_torrent_config=False)
    
            self.rawserver.add_task(0, self.scan)
            self.rawserver.add_task(0.5, self.periodic_check_hashcheck_queue)
            self.rawserver.add_task(self.config['display_interval'],
                                    self.periodic_stats)
            
            try:
                import signal
                def handler(signum, frame):
                    self.rawserver.external_add_task(0, self.read_config)
                signal.signal(signal.SIGHUP, handler)
            except Exception, e:
                self.logger.error(_("Could not set signal handler: ") +
                                    unicode(e.args[0]))
                self.rawserver.add_task(0,self.core_doneflag.set())
  
        except UserFailure, e:
            self.logger.error(unicode(e.args[0]))
            self.rawserver.add_task(0,self.core_doneflag.set())
        except:
            data = StringIO()
            print_exc(file = data)
            self.logger.error(data.getvalue())
            self.rawserver.add_task(0,self.core_doneflag.set())
           
        # always make sure events get processed even if only for
        # shutting down.
        self.rawserver.listen_forever()
Esempio n. 5
0
class TorrentClient(threadedcomponent):
    """\
    TorrentClient([tickInterval]) -> component capable of downloading/sharing torrents.

    Initialises the Mainline client.
    Uses threadedcomponent so it doesn't have to worry about blocking I/O or making
    Mainline yield periodically.
   
    Keyword arguments:
    
    - tickInterval -- the interval in seconds at which TorrentClient checks inboxes (default=5)
    """

    Inboxes = {
        "inbox": "Torrent IPC - add a torrent, stop a torrent etc.",
        "control": "Shut me down"
    }
    Outboxes = {
        "outbox":
        "Torrent IPC - status updates, completion, new torrent added etc.",
        "signal": "Say when I've shutdown"
    }

    def __init__(self, tickInterval=5):
        super(TorrentClient, self).__init__()
        self.totaltorrents = 0
        self.torrents = {}
        self.torrentinfohashes = {}
        self.tickInterval = tickInterval  #seconds

    def main(self):
        """\
        Start the Mainline client and block indefinitely, listening for connectons.
        """

        uiname = "bittorrent-console"
        defaults = get_defaults(uiname)
        config, args = configfile.parse_configuration_and_args(
            defaults, uiname)
        config = Preferences().initWithDict(config)
        data_dir = config['data_dir']
        self.core_doneflag = DeferredEvent()
        self.rawserver_doneflag = DeferredEvent()

        rawserver = RawServer(config)  #event and I/O scheduler
        self.multitorrent = MultiTorrent(
            config, rawserver,
            data_dir)  #class used to add, control and remove torrents

        self.tick()  #add periodic function call

        rawserver.add_task(0, self.core_doneflag.addCallback,
                           lambda r: rawserver.external_add_task(0, shutdown))
        rawserver.listen_forever(
            self.rawserver_doneflag)  # runs until the component terminates

        self.send(producerFinished(self), "signal")


#        print ("TorrentClient has shutdown")

    def startTorrent(self, metainfo, save_incomplete_as, save_as, torrentid):
        """startTorrent causes MultiTorrent to begin downloading a torrent eventually.
        Use it instead of _start_torrent as it retries repeatedly if Mainline is busy."""

        self._create_torrent(metainfo, save_incomplete_as, save_as)
        self.multitorrent.rawserver.add_task(1, self._start_torrent, metainfo,
                                             torrentid)

    def _create_torrent(self, metainfo, save_incomplete_as, save_as):
        if not self.multitorrent.torrent_known(metainfo.infohash):
            df = self.multitorrent.create_torrent(metainfo, save_incomplete_as,
                                                  save_as)
        #except Exception:
        #    e = sys.exc_info()[1]
        #    print (e)
        #    return False

    def _start_torrent(self, metainfo, torrentid):
        #try:
        t = None
        if self.multitorrent.torrent_known(metainfo.infohash):
            t = self.multitorrent.get_torrent(metainfo.infohash)

        # HACK!! Rewrite when INITIALIZING state is available.
        if t is None or not t.is_initialized():
            #self.logger.debug( "Waiting for torrent to initialize." )
            self.multitorrent.rawserver.add_task(3, self._start_torrent,
                                                 metainfo, torrentid)
            return

        if not self.multitorrent.torrent_running(metainfo.infohash):
            df = self.multitorrent.start_torrent(metainfo.infohash)
            self.torrents[torrentid] = self.multitorrent.get_torrent(
                metainfo.infohash)

            #yield df
            #df.getResult()  # raises exception if one occurred in yield.

        #    print (e)
        #    print ("Failed to start torrent")

    def decodeTorrent(self, data):
        """\
        Converts bencoded raw metadata (as one would find in a .torrent file) into
        a metainfo object (which one can then get the torrent's properties from).
        """

        from BitTorrent.bencode import bdecode, bencode
        metainfo = None
        try:
            b = bdecode(data)
            metainfo = ConvertedMetainfo(b)
        except Exception:
            e = sys.exc_info()[1]
            pass
        return metainfo

    def handleMessages(self):
        while self.dataReady("inbox"):
            temp = self.recv("inbox")
            if isinstance(temp, TIPCCreateNewTorrent) or isinstance(temp, str):
                if isinstance(temp, str):
                    metainfo = self.decodeTorrent(temp)
                else:
                    metainfo = self.decodeTorrent(temp.rawmetainfo)
                if metainfo != None:
                    savefolder = os.path.join("./", metainfo.name_fs)

                    existingTorrentId = self.torrentinfohashes.get(
                        metainfo.infohash, 0)
                    if existingTorrentId != 0:
                        self.send(
                            TIPCTorrentAlreadyDownloading(
                                torrentid=existingTorrentId), "outbox")
                    else:
                        self.totaltorrents += 1
                        self.torrentinfohashes[
                            metainfo.infohash] = self.totaltorrents
                        self.torrents[self.totaltorrents] = MakeshiftTorrent(
                            metainfo)
                        self.startTorrent(metainfo, savefolder, savefolder,
                                          self.totaltorrents)
                        self.send(
                            TIPCNewTorrentCreated(torrentid=self.totaltorrents,
                                                  savefolder=savefolder),
                            "outbox")
            elif isinstance(temp, TIPCCloseTorrent):
                try:
                    torrent = self.torrents.get(temp.torrentid, None)
                    self.multitorrent.remove_torrent(torrent.metainfo.infohash)
                    self.torrentinfohashes.pop(torrent.metainfo.infohash)
                    self.torrents.pop(temp.torrentid)
                except KeyError:
                    pass

        while self.dataReady("control"):
            temp = self.recv("control")
            if isinstance(temp, shutdown):
                #                print ("TorrentClient trying to shutdown")
                #cause us to shutdown
                self.rawserver_doneflag.set()
                self.core_doneflag.set()

    def sendStatusUpdates(self):
        "Send a TIPCTorrentStatusUpdate for each running torrent."
        for torrentid, torrent in self.torrents.items():
            if not isinstance(torrent, MakeshiftTorrent):
                self.send(
                    TIPCTorrentStatusUpdate(
                        torrentid=torrentid,
                        statsdictionary=torrent.get_status()), "outbox")

    def tick(self):
        """\
        Called periodically... by itself (gets rawserver to call it back after a delay of
        tickInterval seconds). Checks inboxes and sends a status-update message for every
        active torrent.
        """

        self.multitorrent.rawserver.add_task(self.tickInterval, self.tick)
        #print ("Tick")
        self.handleMessages()
        self.sendStatusUpdates()