Example #1
0
    def initFiles(self, old_style=False, statusfunc=None):
        if self.doneflag.isSet():
            return None
        if not statusfunc:
            statusfunc = self.statusfunc

        disabled_files = None
        if self.selector_enabled:
            self.priority = self.config['priority']
            if self.priority:
                try:
                    self.priority = self.priority.split(',')
                    assert len(self.priority) == len(self.files)
                    self.priority = [int(p) for p in self.priority]
                    for p in self.priority:
                        assert p >= -1
                        assert p <= 2
                except:
                    self.errorfunc('bad priority list given, ignored')
                    self.priority = None

            data = self.appdataobj.getTorrentData(self.infohash)
            try:
                d = data['resume data']['priority']
                assert len(d) == len(self.files)
                disabled_files = [x == -1 for x in d]
            except:
                try:
                    disabled_files = [x == -1 for x in self.priority]
                except:
                    pass

        try:
            try:
                self.storage = Storage(
                    self.files, self.info['piece length'], self.doneflag,
                    self.config, disabled_files)
            except IOError as e:
                self.errorfunc('trouble accessing files - ' + str(e))
                return None
            if self.doneflag.isSet():
                return None

            self.storagewrapper = StorageWrapper(
                self.storage, self.config['download_slice_size'],
                self.pieces, self.info['piece length'], self._finished,
                self._failed, statusfunc, self.doneflag,
                self.config['check_hashes'], self._data_flunked,
                self.rawserver.add_task, self.config, self.unpauseflag)

        except ValueError as e:
            self._failed('bad data - ' + str(e))
        except IOError as e:
            self._failed('IOError - ' + str(e))
        if self.doneflag.isSet():
            return None

        if self.selector_enabled:
            self.fileselector = FileSelector(
                self.files, self.info['piece length'],
                self.appdataobj.getPieceDir(self.infohash), self.storage,
                self.storagewrapper, self.rawserver.add_task, self._failed)
            if data:
                data = data.get('resume data')
                if data:
                    self.fileselector.unpickle(data)

        self.checking = True
        if old_style:
            return self.storagewrapper.old_style_init()
        return self.storagewrapper.initialize
Example #2
0
    def initFiles(self, old_style=False, statusfunc=None):
        if self.doneflag.isSet():
            return None
        if not statusfunc:
            statusfunc = self.statusfunc

        disabled_files = None
        if self.selector_enabled:
            self.priority = self.config['priority']
            if self.priority:
                try:
                    self.priority = self.priority.split(',')
                    assert len(self.priority) == len(self.files)
                    self.priority = [int(p) for p in self.priority]
                    for p in self.priority:
                        assert p >= -1
                        assert p <= 2
                except:
                    self.errorfunc('bad priority list given, ignored')
                    self.priority = None

            data = self.appdataobj.getTorrentData(self.infohash)
            try:
                d = data['resume data']['priority']
                assert len(d) == len(self.files)
                disabled_files = [x == -1 for x in d]
            except:
                try:
                    disabled_files = [x == -1 for x in self.priority]
                except:
                    pass

        try:
            try:
                self.storage = Storage(self.files, self.info['piece length'],
                                       self.doneflag, self.config,
                                       disabled_files)
            except IOError as e:
                self.errorfunc('trouble accessing files - ' + str(e))
                return None
            if self.doneflag.isSet():
                return None

            self.storagewrapper = StorageWrapper(
                self.storage, self.config['download_slice_size'], self.pieces,
                self.info['piece length'], self._finished, self._failed,
                statusfunc, self.doneflag, self.config['check_hashes'],
                self._data_flunked, self.rawserver.add_task, self.config,
                self.unpauseflag)

        except ValueError as e:
            self._failed('bad data - ' + str(e))
        except IOError as e:
            self._failed('IOError - ' + str(e))
        if self.doneflag.isSet():
            return None

        if self.selector_enabled:
            self.fileselector = FileSelector(
                self.files, self.info['piece length'],
                self.appdataobj.getPieceDir(self.infohash), self.storage,
                self.storagewrapper, self.rawserver.add_task, self._failed)
            if data:
                data = data.get('resume data')
                if data:
                    self.fileselector.unpickle(data)

        self.checking = True
        if old_style:
            return self.storagewrapper.old_style_init()
        return self.storagewrapper.initialize
Example #3
0
class BT1Download:
    def __init__(self, statusfunc, finfunc, errorfunc, excfunc, doneflag,
                 config, response, infohash, id, rawserver, port,
                 appdataobj=None):
        self.statusfunc = statusfunc
        self.finfunc = finfunc
        self.errorfunc = errorfunc
        self.excfunc = excfunc
        self.doneflag = doneflag
        self.config = config
        self.response = response
        self.infohash = infohash
        self.myid = id
        self.rawserver = rawserver
        self.port = port

        self.info = self.response['info']
        self.pieces = [self.info['pieces'][x:x + 20]
                       for x in xrange(0, len(self.info['pieces']), 20)]
        self.len_pieces = len(self.pieces)
        self.argslistheader = argslistheader
        self.unpauseflag = threading.Event()
        self.unpauseflag.set()
        self.downloader = None
        self.storagewrapper = None
        self.fileselector = None
        self.super_seeding_active = False
        self.filedatflag = threading.Event()
        self.spewflag = threading.Event()
        self.superseedflag = threading.Event()
        self.whenpaused = None
        self.finflag = threading.Event()
        self.rerequest = None
        self.tcp_ack_fudge = config['tcp_ack_fudge']

        self.selector_enabled = config['selector_enabled']
        if appdataobj:
            self.appdataobj = appdataobj
        elif self.selector_enabled:
            self.appdataobj = ConfigDir()
            self.appdataobj.deleteOldCacheData(config['expire_cache_data'],
                                               [self.infohash])

        self.excflag = self.rawserver.get_exception_flag()
        self.failed = False
        self.checking = False
        self.started = False

        self.picker = PiecePicker(self.len_pieces,
                                  config['rarest_first_cutoff'],
                                  config['rarest_first_priority_cutoff'])
        self.choker = Choker(config, rawserver.add_task,
                             self.picker, self.finflag.isSet)

    def checkSaveLocation(self, loc):
        if 'length' in self.info:
            return os.path.exists(loc)
        return any(os.path.exists(os.path.join(loc, x['path'][0]))
                   for x in self.info['files'])

    def saveAs(self, filefunc, pathfunc=None):
        try:
            def make(f, forcedir=False):
                if not forcedir:
                    f = os.path.split(f)[0]
                if f != '' and not os.path.exists(f):
                    os.makedirs(f)

            if 'length' in self.info:
                file_length = self.info['length']
                file = filefunc(self.info['name'], file_length,
                                self.config['saveas'], False)
                if file is None:
                    return None
                make(file)
                files = [(file, file_length)]
            else:
                file_length = sum(x['length'] for x in self.info['files'])
                file = filefunc(self.info['name'], file_length,
                                self.config['saveas'], True)
                if file is None:
                    return None

                # if this path exists, and no files from the info dict exist,
                # we assume it's a new download and the user wants to create a
                # new directory with the default name
                existing = 0
                if os.path.exists(file):
                    if not os.path.isdir(file):
                        self.errorfunc(file + 'is not a dir')
                        return None
                    if len(os.listdir(file)) > 0:  # if it's not empty
                        existing = any(
                            os.path.exists(os.path.join(file, x['path'][0]))
                            for x in self.info['files'])
                        if not existing:
                            file = os.path.join(file, self.info['name'])
                            if os.path.exists(file) and \
                                    not os.path.isdir(file):
                                if file[-8:] == '.torrent':
                                    file = file[:-8]
                                if os.path.exists(file) and \
                                        not os.path.isdir(file):
                                    self.errorfunc("Can't create dir - " +
                                                   self.info['name'])
                                    return None
                make(file, True)

                # alert the UI to any possible change in path
                if pathfunc is not None:
                    pathfunc(file)

                files = []
                for x in self.info['files']:
                    n = os.path.join(file, *x['path'])
                    files.append((n, x['length']))
                    make(n)
        except OSError as e:
            self.errorfunc("Couldn't allocate dir - " + str(e))
            return None

        self.filename = file
        self.files = files
        self.datalength = file_length

        return file

    def getFilename(self):
        return self.filename

    def _finished(self):
        self.finflag.set()
        try:
            self.storage.set_readonly()
        except (IOError, OSError) as e:
            self.errorfunc('trouble setting readonly at end - ' + str(e))
        if self.superseedflag.isSet():
            self._set_super_seed()
        self.choker.set_round_robin_period(
            max(self.config['round_robin_period'],
                self.config['round_robin_period'] *
                self.info['piece length'] / 200000))
        self.rerequest_complete()
        self.finfunc()

    def _data_flunked(self, amount, index):
        self.ratemeasure_datarejected(amount)
        if not self.doneflag.isSet():
            self.errorfunc('piece {:d} failed hash check, re-downloading it'
                           ''.format(index))

    def _failed(self, reason):
        self.failed = True
        self.doneflag.set()
        if reason is not None:
            self.errorfunc(reason)

    def initFiles(self, old_style=False, statusfunc=None):
        if self.doneflag.isSet():
            return None
        if not statusfunc:
            statusfunc = self.statusfunc

        disabled_files = None
        if self.selector_enabled:
            self.priority = self.config['priority']
            if self.priority:
                try:
                    self.priority = self.priority.split(',')
                    assert len(self.priority) == len(self.files)
                    self.priority = [int(p) for p in self.priority]
                    for p in self.priority:
                        assert p >= -1
                        assert p <= 2
                except:
                    self.errorfunc('bad priority list given, ignored')
                    self.priority = None

            data = self.appdataobj.getTorrentData(self.infohash)
            try:
                d = data['resume data']['priority']
                assert len(d) == len(self.files)
                disabled_files = [x == -1 for x in d]
            except:
                try:
                    disabled_files = [x == -1 for x in self.priority]
                except:
                    pass

        try:
            try:
                self.storage = Storage(
                    self.files, self.info['piece length'], self.doneflag,
                    self.config, disabled_files)
            except IOError as e:
                self.errorfunc('trouble accessing files - ' + str(e))
                return None
            if self.doneflag.isSet():
                return None

            self.storagewrapper = StorageWrapper(
                self.storage, self.config['download_slice_size'],
                self.pieces, self.info['piece length'], self._finished,
                self._failed, statusfunc, self.doneflag,
                self.config['check_hashes'], self._data_flunked,
                self.rawserver.add_task, self.config, self.unpauseflag)

        except ValueError as e:
            self._failed('bad data - ' + str(e))
        except IOError as e:
            self._failed('IOError - ' + str(e))
        if self.doneflag.isSet():
            return None

        if self.selector_enabled:
            self.fileselector = FileSelector(
                self.files, self.info['piece length'],
                self.appdataobj.getPieceDir(self.infohash), self.storage,
                self.storagewrapper, self.rawserver.add_task, self._failed)
            if data:
                data = data.get('resume data')
                if data:
                    self.fileselector.unpickle(data)

        self.checking = True
        if old_style:
            return self.storagewrapper.old_style_init()
        return self.storagewrapper.initialize

    def getCachedTorrentData(self):
        return self.appdataobj.getTorrentData(self.infohash)

    def _make_upload(self, connection, ratelimiter, totalup):
        return Upload(connection, ratelimiter, totalup,
                      self.choker, self.storagewrapper, self.picker,
                      self.config)

    def _kick_peer(self, connection):
        self.rawserver.add_task(connection.close, 0)

    def _ban_peer(self, ip):
        self.encoder_ban(ip)

    def _received_raw_data(self, x):
        if self.tcp_ack_fudge:
            x = int(x * self.tcp_ack_fudge)
            self.ratelimiter.adjust_sent(x)

    def _received_data(self, x):
        self.downmeasure.update_rate(x)
        self.ratemeasure.data_came_in(x)

    def _received_http_data(self, x):
        self.downmeasure.update_rate(x)
        self.ratemeasure.data_came_in(x)
        self.downloader.external_data_received(x)

    def _cancelfunc(self, pieces):
        self.downloader.cancel_piece_download(pieces)
        self.httpdownloader.cancel_piece_download(pieces)

    def _reqmorefunc(self, pieces):
        self.downloader.requeue_piece_download(pieces)

    def startEngine(self, ratelimiter=None, statusfunc=None):
        if self.doneflag.isSet():
            return False
        if not statusfunc:
            statusfunc = self.statusfunc

        self.checking = False

        if not CRYPTO_OK:
            if self.config['crypto_allowed']:
                self.errorfunc('warning - crypto library not installed')
            self.config['crypto_allowed'] = 0
            self.config['crypto_only'] = 0
            self.config['crypto_stealth'] = 0

        for i in xrange(self.len_pieces):
            if self.storagewrapper.do_I_have(i):
                self.picker.complete(i)
        self.upmeasure = Measure(self.config['max_rate_period'],
                                 self.config['upload_rate_fudge'])
        self.downmeasure = Measure(self.config['max_rate_period'])

        if ratelimiter:
            self.ratelimiter = ratelimiter
        else:
            self.ratelimiter = RateLimiter(self.rawserver.add_task,
                                           self.config['upload_unit_size'],
                                           self.setConns)
            self.ratelimiter.set_upload_rate(self.config['max_upload_rate'])

        self.ratemeasure = RateMeasure()
        self.ratemeasure_datarejected = self.ratemeasure.data_rejected

        self.downloader = Downloader(
            self.storagewrapper, self.picker, self.config['request_backlog'],
            self.config['max_rate_period'], self.len_pieces,
            self.config['download_slice_size'], self._received_data,
            self.config['snub_time'], self.config['auto_kick'],
            self._kick_peer, self._ban_peer)
        self.downloader.set_download_rate(self.config['max_download_rate'])
        self.connecter = Connecter(
            self._make_upload, self.downloader, self.choker, self.len_pieces,
            self.upmeasure, self.config, self.ratelimiter,
            self.rawserver.add_task)
        self.encoder = Encoder(
            self.connecter, self.rawserver, self.myid,
            self.config['max_message_length'], self.rawserver.add_task,
            self.config['keepalive_interval'], self.infohash,
            self._received_raw_data, self.config)
        self.encoder_ban = self.encoder.ban

        self.httpdownloader = HTTPDownloader(
            self.storagewrapper, self.picker, self.rawserver, self.finflag,
            self.errorfunc, self.downloader, self.config['max_rate_period'],
            self.infohash, self._received_http_data, self.connecter.got_piece)
        if 'httpseeds' in self.response and not self.finflag.isSet():
            for u in self.response['httpseeds']:
                self.httpdownloader.make_download(u)

        if self.selector_enabled:
            self.fileselector.tie_in(self.picker, self._cancelfunc,
                                     self._reqmorefunc,
                                     self.rerequest_ondownloadmore)
            if self.priority:
                self.fileselector.set_priorities_now(self.priority)

            # erase old data once you've started modifying it
            self.appdataobj.deleteTorrentData(self.infohash)

        if self.config['super_seeder']:
            self.set_super_seed()

        self.started = True
        return True

    def rerequest_complete(self):
        if self.rerequest:
            self.rerequest.announce(1)

    def rerequest_stopped(self):
        if self.rerequest:
            self.rerequest.announce(2)

    def rerequest_lastfailed(self):
        if self.rerequest:
            return self.rerequest.last_failed
        return False

    def rerequest_ondownloadmore(self):
        if self.rerequest:
            self.rerequest.hit()

    def startRerequester(self, seededfunc=None, force_rapid_update=False):
        trackerlist = self.response.get('announce-list',
                                        [[self.response['announce']]])

        self.rerequest = Rerequester(
            self.port, self.myid, self.infohash, trackerlist, self.config,
            self.rawserver.add_task, self.rawserver.add_task, self.errorfunc,
            self.excfunc, self.encoder.start_connections,
            self.connecter.how_many_connections,
            self.storagewrapper.get_amount_left, self.upmeasure.get_total,
            self.downmeasure.get_total, self.upmeasure.get_rate,
            self.downmeasure.get_rate, self.doneflag, self.unpauseflag,
            seededfunc, force_rapid_update)

        self.rerequest.start()

    def _init_stats(self):
        self.statistics = Statistics(
            self.upmeasure, self.downmeasure, self.connecter,
            self.httpdownloader, self.ratelimiter, self.rerequest_lastfailed,
            self.filedatflag)
        if 'files' in self.info:
            self.statistics.set_dirstats(self.files, self.info['piece length'])
        if self.config['spew']:
            self.spewflag.set()

    def autoStats(self, displayfunc=None):
        if not displayfunc:
            displayfunc = self.statusfunc

        self._init_stats()
        DownloaderFeedback(
            self.choker, self.httpdownloader, self.rawserver.add_task,
            self.upmeasure.get_rate, self.downmeasure.get_rate,
            self.ratemeasure, self.storagewrapper.get_stats, self.datalength,
            self.finflag, self.spewflag, self.statistics, displayfunc,
            self.config['display_interval'])

    def startStats(self):
        self._init_stats()
        d = DownloaderFeedback(
            self.choker, self.httpdownloader, self.rawserver.add_task,
            self.upmeasure.get_rate, self.downmeasure.get_rate,
            self.ratemeasure, self.storagewrapper.get_stats, self.datalength,
            self.finflag, self.spewflag, self.statistics)
        return d.gather

    def getPortHandler(self):
        return self.encoder

    def shutdown(self, torrentdata={}):
        if self.checking or self.started:
            self.storagewrapper.sync()
            self.storage.close()
            self.rerequest_stopped()
        if self.fileselector and self.started:
            if not self.failed:
                self.fileselector.finish()
                torrentdata['resume data'] = self.fileselector.pickle()
            try:
                self.appdataobj.writeTorrentData(self.infohash, torrentdata)
            except:
                self.appdataobj.deleteTorrentData(self.infohash)  # clear it
        return not self.failed and not self.excflag.isSet()
        # if returns false, you may wish to auto-restart the torrent

    def setUploadRate(self, rate):
        try:
            def s(self=self, rate=rate):
                self.config['max_upload_rate'] = rate
                self.ratelimiter.set_upload_rate(rate)
            self.rawserver.add_task(s)
        except AttributeError:
            pass

    def setConns(self, conns, conns2=None):
        if not conns2:
            conns2 = conns
        try:
            def s(self=self, conns=conns, conns2=conns2):
                self.config['min_uploads'] = conns
                self.config['max_uploads'] = conns2
                if (conns > 30):
                    self.config['max_initiate'] = conns + 10
            self.rawserver.add_task(s)
        except AttributeError:
            pass

    def setDownloadRate(self, rate):
        try:
            def s(self=self, rate=rate):
                self.config['max_download_rate'] = rate
                self.downloader.set_download_rate(rate)
            self.rawserver.add_task(s)
        except AttributeError:
            pass

    def startConnection(self, ip, port, id):
        self.encoder._start_connection((ip, port), id)

    def _startConnection(self, ipandport, id):
        self.encoder._start_connection(ipandport, id)

    def setInitiate(self, initiate):
        try:
            def s(self=self, initiate=initiate):
                self.config['max_initiate'] = initiate
            self.rawserver.add_task(s)
        except AttributeError:
            pass

    def getConfig(self):
        return self.config

    def getDefaults(self):
        return defaultargs(defaults)

    def getUsageText(self):
        return self.argslistheader

    def reannounce(self, special=None):
        try:
            def r(self=self, special=special):
                if special is None:
                    self.rerequest.announce()
                else:
                    self.rerequest.announce(specialurl=special)
            self.rawserver.add_task(r)
        except AttributeError:
            pass

    def getResponse(self):
        try:
            return self.response
        except:
            return None

    def Pause(self):
        if not self.storagewrapper:
            return False
        self.unpauseflag.clear()
        self.rawserver.add_task(self.onPause)
        return True

    def onPause(self):
        self.whenpaused = clock()
        if not self.downloader:
            return
        self.downloader.pause(True)
        self.encoder.pause(True)
        self.choker.pause(True)

    def Unpause(self):
        self.unpauseflag.set()
        self.rawserver.add_task(self.onUnpause)

    def onUnpause(self):
        if not self.downloader:
            return
        self.downloader.pause(False)
        self.encoder.pause(False)
        self.choker.pause(False)
        # rerequest automatically if paused for >60 seconds
        if self.rerequest and self.whenpaused and \
                clock() - self.whenpaused > 60:
            self.rerequest.announce(3)

    def set_super_seed(self):
        try:
            self.superseedflag.set()

            def s(self=self):
                if self.finflag.isSet():
                    self._set_super_seed()
            self.rawserver.add_task(s)
        except AttributeError:
            pass

    def _set_super_seed(self):
        if not self.super_seeding_active:
            self.super_seeding_active = True
            self.errorfunc('        ** SUPER-SEED OPERATION ACTIVE **\n  '
                           'please set Max uploads so each peer gets 6-8 kB/s')

            def s(self=self):
                self.downloader.set_super_seed()
                self.choker.set_super_seed()
            self.rawserver.add_task(s)
            # mode started when already finished
            if self.finflag.isSet():
                def r(self=self):
                    # so after kicking everyone off, reannounce
                    self.rerequest.announce(3)
                self.rawserver.add_task(r)

    def am_I_finished(self):
        return self.finflag.isSet()

    def get_transfer_stats(self):
        return self.upmeasure.get_total(), self.downmeasure.get_total()
Example #4
0
class BT1Download:
    def __init__(self,
                 statusfunc,
                 finfunc,
                 errorfunc,
                 excfunc,
                 doneflag,
                 config,
                 response,
                 infohash,
                 id,
                 rawserver,
                 port,
                 appdataobj=None):
        self.statusfunc = statusfunc
        self.finfunc = finfunc
        self.errorfunc = errorfunc
        self.excfunc = excfunc
        self.doneflag = doneflag
        self.config = config
        self.response = response
        self.infohash = infohash
        self.myid = id
        self.rawserver = rawserver
        self.port = port

        self.info = self.response['info']
        self.pieces = [
            self.info['pieces'][x:x + 20]
            for x in xrange(0, len(self.info['pieces']), 20)
        ]
        self.len_pieces = len(self.pieces)
        self.argslistheader = argslistheader
        self.unpauseflag = threading.Event()
        self.unpauseflag.set()
        self.downloader = None
        self.storagewrapper = None
        self.fileselector = None
        self.super_seeding_active = False
        self.filedatflag = threading.Event()
        self.spewflag = threading.Event()
        self.superseedflag = threading.Event()
        self.whenpaused = None
        self.finflag = threading.Event()
        self.rerequest = None
        self.tcp_ack_fudge = config['tcp_ack_fudge']

        self.selector_enabled = config['selector_enabled']
        if appdataobj:
            self.appdataobj = appdataobj
        elif self.selector_enabled:
            self.appdataobj = ConfigDir()
            self.appdataobj.deleteOldCacheData(config['expire_cache_data'],
                                               [self.infohash])

        self.excflag = self.rawserver.get_exception_flag()
        self.failed = False
        self.checking = False
        self.started = False

        self.picker = PiecePicker(self.len_pieces,
                                  config['rarest_first_cutoff'],
                                  config['rarest_first_priority_cutoff'])
        self.choker = Choker(config, rawserver.add_task, self.picker,
                             self.finflag.isSet)

    def checkSaveLocation(self, loc):
        if 'length' in self.info:
            return os.path.exists(loc)
        return any(
            os.path.exists(os.path.join(loc, x['path'][0]))
            for x in self.info['files'])

    def saveAs(self, filefunc, pathfunc=None):
        try:

            def make(f, forcedir=False):
                if not forcedir:
                    f = os.path.split(f)[0]
                if f != '' and not os.path.exists(f):
                    os.makedirs(f)

            if 'length' in self.info:
                file_length = self.info['length']
                file = filefunc(self.info['name'], file_length,
                                self.config['saveas'], False)
                if file is None:
                    return None
                make(file)
                files = [(file, file_length)]
            else:
                file_length = sum(x['length'] for x in self.info['files'])
                file = filefunc(self.info['name'], file_length,
                                self.config['saveas'], True)
                if file is None:
                    return None

                # if this path exists, and no files from the info dict exist,
                # we assume it's a new download and the user wants to create a
                # new directory with the default name
                existing = 0
                if os.path.exists(file):
                    if not os.path.isdir(file):
                        self.errorfunc(file + 'is not a dir')
                        return None
                    if len(os.listdir(file)) > 0:  # if it's not empty
                        existing = any(
                            os.path.exists(os.path.join(file, x['path'][0]))
                            for x in self.info['files'])
                        if not existing:
                            file = os.path.join(file, self.info['name'])
                            if os.path.exists(file) and \
                                    not os.path.isdir(file):
                                if file[-8:] == '.torrent':
                                    file = file[:-8]
                                if os.path.exists(file) and \
                                        not os.path.isdir(file):
                                    self.errorfunc("Can't create dir - " +
                                                   self.info['name'])
                                    return None
                make(file, True)

                # alert the UI to any possible change in path
                if pathfunc is not None:
                    pathfunc(file)

                files = []
                for x in self.info['files']:
                    n = os.path.join(file, *x['path'])
                    files.append((n, x['length']))
                    make(n)
        except OSError as e:
            self.errorfunc("Couldn't allocate dir - " + str(e))
            return None

        self.filename = file
        self.files = files
        self.datalength = file_length

        return file

    def getFilename(self):
        return self.filename

    def _finished(self):
        self.finflag.set()
        try:
            self.storage.set_readonly()
        except (IOError, OSError) as e:
            self.errorfunc('trouble setting readonly at end - ' + str(e))
        if self.superseedflag.isSet():
            self._set_super_seed()
        self.choker.set_round_robin_period(
            max(
                self.config['round_robin_period'],
                self.config['round_robin_period'] * self.info['piece length'] /
                200000))
        self.rerequest_complete()
        self.finfunc()

    def _data_flunked(self, amount, index):
        self.ratemeasure_datarejected(amount)
        if not self.doneflag.isSet():
            self.errorfunc('piece {:d} failed hash check, re-downloading it'
                           ''.format(index))

    def _failed(self, reason):
        self.failed = True
        self.doneflag.set()
        if reason is not None:
            self.errorfunc(reason)

    def initFiles(self, old_style=False, statusfunc=None):
        if self.doneflag.isSet():
            return None
        if not statusfunc:
            statusfunc = self.statusfunc

        disabled_files = None
        if self.selector_enabled:
            self.priority = self.config['priority']
            if self.priority:
                try:
                    self.priority = self.priority.split(',')
                    assert len(self.priority) == len(self.files)
                    self.priority = [int(p) for p in self.priority]
                    for p in self.priority:
                        assert p >= -1
                        assert p <= 2
                except:
                    self.errorfunc('bad priority list given, ignored')
                    self.priority = None

            data = self.appdataobj.getTorrentData(self.infohash)
            try:
                d = data['resume data']['priority']
                assert len(d) == len(self.files)
                disabled_files = [x == -1 for x in d]
            except:
                try:
                    disabled_files = [x == -1 for x in self.priority]
                except:
                    pass

        try:
            try:
                self.storage = Storage(self.files, self.info['piece length'],
                                       self.doneflag, self.config,
                                       disabled_files)
            except IOError as e:
                self.errorfunc('trouble accessing files - ' + str(e))
                return None
            if self.doneflag.isSet():
                return None

            self.storagewrapper = StorageWrapper(
                self.storage, self.config['download_slice_size'], self.pieces,
                self.info['piece length'], self._finished, self._failed,
                statusfunc, self.doneflag, self.config['check_hashes'],
                self._data_flunked, self.rawserver.add_task, self.config,
                self.unpauseflag)

        except ValueError as e:
            self._failed('bad data - ' + str(e))
        except IOError as e:
            self._failed('IOError - ' + str(e))
        if self.doneflag.isSet():
            return None

        if self.selector_enabled:
            self.fileselector = FileSelector(
                self.files, self.info['piece length'],
                self.appdataobj.getPieceDir(self.infohash), self.storage,
                self.storagewrapper, self.rawserver.add_task, self._failed)
            if data:
                data = data.get('resume data')
                if data:
                    self.fileselector.unpickle(data)

        self.checking = True
        if old_style:
            return self.storagewrapper.old_style_init()
        return self.storagewrapper.initialize

    def getCachedTorrentData(self):
        return self.appdataobj.getTorrentData(self.infohash)

    def _make_upload(self, connection, ratelimiter, totalup):
        return Upload(connection, ratelimiter, totalup, self.choker,
                      self.storagewrapper, self.picker, self.config)

    def _kick_peer(self, connection):
        self.rawserver.add_task(connection.close, 0)

    def _ban_peer(self, ip):
        self.encoder_ban(ip)

    def _received_raw_data(self, x):
        if self.tcp_ack_fudge:
            x = int(x * self.tcp_ack_fudge)
            self.ratelimiter.adjust_sent(x)

    def _received_data(self, x):
        self.downmeasure.update_rate(x)
        self.ratemeasure.data_came_in(x)

    def _received_http_data(self, x):
        self.downmeasure.update_rate(x)
        self.ratemeasure.data_came_in(x)
        self.downloader.external_data_received(x)

    def _cancelfunc(self, pieces):
        self.downloader.cancel_piece_download(pieces)
        self.httpdownloader.cancel_piece_download(pieces)

    def _reqmorefunc(self, pieces):
        self.downloader.requeue_piece_download(pieces)

    def startEngine(self, ratelimiter=None, statusfunc=None):
        if self.doneflag.isSet():
            return False
        if not statusfunc:
            statusfunc = self.statusfunc

        self.checking = False

        if not CRYPTO_OK:
            if self.config['crypto_allowed']:
                self.errorfunc('warning - crypto library not installed')
            self.config['crypto_allowed'] = 0
            self.config['crypto_only'] = 0
            self.config['crypto_stealth'] = 0

        for i in xrange(self.len_pieces):
            if self.storagewrapper.do_I_have(i):
                self.picker.complete(i)
        self.upmeasure = Measure(self.config['max_rate_period'],
                                 self.config['upload_rate_fudge'])
        self.downmeasure = Measure(self.config['max_rate_period'])

        if ratelimiter:
            self.ratelimiter = ratelimiter
        else:
            self.ratelimiter = RateLimiter(self.rawserver.add_task,
                                           self.config['upload_unit_size'],
                                           self.setConns)
            self.ratelimiter.set_upload_rate(self.config['max_upload_rate'])

        self.ratemeasure = RateMeasure()
        self.ratemeasure_datarejected = self.ratemeasure.data_rejected

        self.downloader = Downloader(
            self.storagewrapper, self.picker, self.config['request_backlog'],
            self.config['max_rate_period'], self.len_pieces,
            self.config['download_slice_size'], self._received_data,
            self.config['snub_time'], self.config['auto_kick'],
            self._kick_peer, self._ban_peer)
        self.downloader.set_download_rate(self.config['max_download_rate'])
        self.connecter = Connecter(self._make_upload, self.downloader,
                                   self.choker, self.len_pieces,
                                   self.upmeasure, self.config,
                                   self.ratelimiter, self.rawserver.add_task)
        self.encoder = Encoder(self.connecter, self.rawserver, self.myid,
                               self.config['max_message_length'],
                               self.rawserver.add_task,
                               self.config['keepalive_interval'],
                               self.infohash, self._received_raw_data,
                               self.config)
        self.encoder_ban = self.encoder.ban

        self.httpdownloader = HTTPDownloader(
            self.storagewrapper, self.picker, self.rawserver, self.finflag,
            self.errorfunc, self.downloader, self.config['max_rate_period'],
            self.infohash, self._received_http_data, self.connecter.got_piece)
        if 'httpseeds' in self.response and not self.finflag.isSet():
            for u in self.response['httpseeds']:
                self.httpdownloader.make_download(u)

        if self.selector_enabled:
            self.fileselector.tie_in(self.picker, self._cancelfunc,
                                     self._reqmorefunc,
                                     self.rerequest_ondownloadmore)
            if self.priority:
                self.fileselector.set_priorities_now(self.priority)

            # erase old data once you've started modifying it
            self.appdataobj.deleteTorrentData(self.infohash)

        if self.config['super_seeder']:
            self.set_super_seed()

        self.started = True
        return True

    def rerequest_complete(self):
        if self.rerequest:
            self.rerequest.announce(1)

    def rerequest_stopped(self):
        if self.rerequest:
            self.rerequest.announce(2)

    def rerequest_lastfailed(self):
        if self.rerequest:
            return self.rerequest.last_failed
        return False

    def rerequest_ondownloadmore(self):
        if self.rerequest:
            self.rerequest.hit()

    def startRerequester(self, seededfunc=None, force_rapid_update=False):
        trackerlist = self.response.get('announce-list',
                                        [[self.response['announce']]])

        self.rerequest = Rerequester(
            self.port, self.myid, self.infohash, trackerlist, self.config,
            self.rawserver.add_task, self.rawserver.add_task, self.errorfunc,
            self.excfunc, self.encoder.start_connections,
            self.connecter.how_many_connections,
            self.storagewrapper.get_amount_left, self.upmeasure.get_total,
            self.downmeasure.get_total, self.upmeasure.get_rate,
            self.downmeasure.get_rate, self.doneflag, self.unpauseflag,
            seededfunc, force_rapid_update)

        self.rerequest.start()

    def _init_stats(self):
        self.statistics = Statistics(self.upmeasure, self.downmeasure,
                                     self.connecter, self.httpdownloader,
                                     self.ratelimiter,
                                     self.rerequest_lastfailed,
                                     self.filedatflag)
        if 'files' in self.info:
            self.statistics.set_dirstats(self.files, self.info['piece length'])
        if self.config['spew']:
            self.spewflag.set()

    def autoStats(self, displayfunc=None):
        if not displayfunc:
            displayfunc = self.statusfunc

        self._init_stats()
        DownloaderFeedback(self.choker, self.httpdownloader,
                           self.rawserver.add_task, self.upmeasure.get_rate,
                           self.downmeasure.get_rate, self.ratemeasure,
                           self.storagewrapper.get_stats, self.datalength,
                           self.finflag, self.spewflag, self.statistics,
                           displayfunc, self.config['display_interval'])

    def startStats(self):
        self._init_stats()
        d = DownloaderFeedback(self.choker, self.httpdownloader,
                               self.rawserver.add_task,
                               self.upmeasure.get_rate,
                               self.downmeasure.get_rate, self.ratemeasure,
                               self.storagewrapper.get_stats, self.datalength,
                               self.finflag, self.spewflag, self.statistics)
        return d.gather

    def getPortHandler(self):
        return self.encoder

    def shutdown(self, torrentdata={}):
        if self.checking or self.started:
            self.storagewrapper.sync()
            self.storage.close()
            self.rerequest_stopped()
        if self.fileselector and self.started:
            if not self.failed:
                self.fileselector.finish()
                torrentdata['resume data'] = self.fileselector.pickle()
            try:
                self.appdataobj.writeTorrentData(self.infohash, torrentdata)
            except:
                self.appdataobj.deleteTorrentData(self.infohash)  # clear it
        return not self.failed and not self.excflag.isSet()
        # if returns false, you may wish to auto-restart the torrent

    def setUploadRate(self, rate):
        try:

            def s(self=self, rate=rate):
                self.config['max_upload_rate'] = rate
                self.ratelimiter.set_upload_rate(rate)

            self.rawserver.add_task(s)
        except AttributeError:
            pass

    def setConns(self, conns, conns2=None):
        if not conns2:
            conns2 = conns
        try:

            def s(self=self, conns=conns, conns2=conns2):
                self.config['min_uploads'] = conns
                self.config['max_uploads'] = conns2
                if (conns > 30):
                    self.config['max_initiate'] = conns + 10

            self.rawserver.add_task(s)
        except AttributeError:
            pass

    def setDownloadRate(self, rate):
        try:

            def s(self=self, rate=rate):
                self.config['max_download_rate'] = rate
                self.downloader.set_download_rate(rate)

            self.rawserver.add_task(s)
        except AttributeError:
            pass

    def startConnection(self, ip, port, id):
        self.encoder._start_connection((ip, port), id)

    def _startConnection(self, ipandport, id):
        self.encoder._start_connection(ipandport, id)

    def setInitiate(self, initiate):
        try:

            def s(self=self, initiate=initiate):
                self.config['max_initiate'] = initiate

            self.rawserver.add_task(s)
        except AttributeError:
            pass

    def getConfig(self):
        return self.config

    def getDefaults(self):
        return defaultargs(defaults)

    def getUsageText(self):
        return self.argslistheader

    def reannounce(self, special=None):
        try:

            def r(self=self, special=special):
                if special is None:
                    self.rerequest.announce()
                else:
                    self.rerequest.announce(specialurl=special)

            self.rawserver.add_task(r)
        except AttributeError:
            pass

    def getResponse(self):
        try:
            return self.response
        except:
            return None

    def Pause(self):
        if not self.storagewrapper:
            return False
        self.unpauseflag.clear()
        self.rawserver.add_task(self.onPause)
        return True

    def onPause(self):
        self.whenpaused = clock()
        if not self.downloader:
            return
        self.downloader.pause(True)
        self.encoder.pause(True)
        self.choker.pause(True)

    def Unpause(self):
        self.unpauseflag.set()
        self.rawserver.add_task(self.onUnpause)

    def onUnpause(self):
        if not self.downloader:
            return
        self.downloader.pause(False)
        self.encoder.pause(False)
        self.choker.pause(False)
        # rerequest automatically if paused for >60 seconds
        if self.rerequest and self.whenpaused and \
                clock() - self.whenpaused > 60:
            self.rerequest.announce(3)

    def set_super_seed(self):
        try:
            self.superseedflag.set()

            def s(self=self):
                if self.finflag.isSet():
                    self._set_super_seed()

            self.rawserver.add_task(s)
        except AttributeError:
            pass

    def _set_super_seed(self):
        if not self.super_seeding_active:
            self.super_seeding_active = True
            self.errorfunc('        ** SUPER-SEED OPERATION ACTIVE **\n  '
                           'please set Max uploads so each peer gets 6-8 kB/s')

            def s(self=self):
                self.downloader.set_super_seed()
                self.choker.set_super_seed()

            self.rawserver.add_task(s)
            # mode started when already finished
            if self.finflag.isSet():

                def r(self=self):
                    # so after kicking everyone off, reannounce
                    self.rerequest.announce(3)

                self.rawserver.add_task(r)

    def am_I_finished(self):
        return self.finflag.isSet()

    def get_transfer_stats(self):
        return self.upmeasure.get_total(), self.downmeasure.get_total()