Пример #1
0
def download(params, filefunc, statusfunc, finfunc, errorfunc, doneflag, cols,
             pathFunc=None, presets={}, exchandler=None, failed=_failfunc,
             paramfunc=None):

    try:
        config = parse_params(params, presets)
    except ValueError as e:
        failed('error: {}\nrun with no args for parameter explanations'.format(
            e))
        return
    if not config:
        errorfunc(get_usage())
        return

    myid = createPeerID()
    random.seed(myid)

    rawserver = RawServer(doneflag, config['timeout_check_interval'],
                          config['timeout'],
                          ipv6_enable=config['ipv6_enabled'],
                          failfunc=failed, errorfunc=exchandler)

    upnp_type = UPnP_test(config['upnp_nat_access'])
    try:
        listen_port = rawserver.find_and_bind(
            config['minport'], config['maxport'], config['bind'],
            ipv6_socket_style=config['ipv6_binds_v4'], upnp=upnp_type,
            randomizer=config['random_port'])
    except socket.error as e:
        failed("Couldn't listen - " + str(e))
        return

    response = get_response(config['responsefile'], config['url'], failed)
    if not response:
        return

    infohash = hashlib.sha1(bencode(response['info'])).digest()

    d = BT1Download(statusfunc, finfunc, errorfunc, exchandler, doneflag,
                    config, response, infohash, myid, rawserver, listen_port)

    if not d.saveAs(filefunc):
        return

    if pathFunc:
        pathFunc(d.getFilename())

    hashcheck = d.initFiles(old_style=True)
    if not hashcheck:
        return
    if not hashcheck():
        return
    if not d.startEngine():
        return
    d.startRerequester()
    d.autoStats()

    statusfunc(activity='connecting to peers')

    if paramfunc:
        paramfunc({
            # change_max_upload_rate(<int KiB/sec>)
            'max_upload_rate': d.setUploadRate,
            # change_max_uploads(<int max uploads>)
            'max_uploads': d.setConns,
            'listen_port': listen_port,     # int
            'peer_id': myid,                # string
            'info_hash': infohash,          # string
            # start_connection((<string ip>, <int port>), <peer id>)
            'start_connection': d._startConnection,
        })

    rawserver.listen_forever(d.getPortHandler())

    d.shutdown()
Пример #2
0
        return 0

def track(args):
    if len(args) == 0:
        print formatDefinitions(defaults, 80)
        return
    try:
        config, files = parseargs(args, defaults, 0, 0)
    except ValueError, e:
        print 'error: ' + str(e)
        print 'run with no arguments for parameter explanations'
        return
    r = RawServer(Event(), config['timeout_check_interval'], config['socket_timeout'])
    t = Tracker(config, r)
    r.bind(config['port'], config['bind'], True)
    r.listen_forever(HTTPHandler(t.get, config['min_time_between_log_flushes']))
    t.save_dfile()
    print '# Shutting down: ' + isotime()

def size_format(s):
    if (s < 1024):
        r = str(s) + 'B'
    elif (s < 1048576):
        r = str(int(s/1024)) + 'KiB'
    elif (s < 1073741824L):
        r = str(int(s/1048576)) + 'MiB'
    elif (s < 1099511627776L):
        r = str(int((s/1073741824.0)*100.0)/100.0) + 'GiB'
    else:
        r = str(int((s/1099511627776.0)*100.0)/100.0) + 'TiB'
    return(r)
Пример #3
0
        rawserver.add_task, storagewrapper.get_amount_left, 
        upmeasure.get_total, downmeasure.get_total, listen_port, 
        config['ip'], myid, infohash, config['http_timeout'], errorfunc, 
        config['max_initiate'], doneflag, upmeasure.get_rate, downmeasure.get_rate,
        encoder.ever_got_incoming)
    if config['spew']:
        spewflag.set()
    DownloaderFeedback(choker, rawserver.add_task, statusfunc, 
        upmeasure.get_rate, downmeasure.get_rate, 
        upmeasure.get_total, downmeasure.get_total, ratemeasure.get_time_left, 
        ratemeasure.get_size_left, file_length, finflag,
        config['display_interval'], spewflag)


    # useful info and functions for the UI
    if paramfunc:
        paramfunc({ 'max_upload_rate' : connecter.change_max_upload_rate,  # change_max_upload_rate(<int bytes/sec>)
                    'max_uploads': choker.change_max_uploads, # change_max_uploads(<int max uploads>)
                    'listen_port' : listen_port, # int
                    'peer_id' : myid, # string
                    'info_hash' : infohash, # string
                    'start_connection' : encoder._start_connection # start_connection((<string ip>, <int port>), <peer id>)
                    })
    
    statusfunc({"activity" : 'connecting to peers'})
    ann[0] = rerequest.announce
    rerequest.begin()
    rawserver.listen_forever(encoder)
    storage.close()
    rerequest.announce(2)
Пример #4
0
class ClientHandler:
    def __init__(self, co):
        self.co = co
        self.socket = {}
        self.rs = RawServer(Event(), 100, 1000)
        self.nh = NetworkHandler(self)

    # Functions called from lower level code
    def message_came_in(self, s, data):
        try:
            msg = bdecode(data)
        except ValueError:
            self.close(s)
            raise NetworkError, 'garbage data'
        if msg.has_key('error'):
            raise ServerError, msg['error']
        socket = self.socket[s]
        srp = socket['srp']
        if socket['state'] == 1:
            K, m = self.auth.client_key(msg['s'], msg['B'], msg['u'],
                                        srp['keys'])
            socket['key'], socket['m_out'] = K, m
            self._send_msg(s, {'m': socket['m_out'].digest()})
            socket['state'] = 2
        elif socket['state'] == 2:
            socket['m_in'] = SRP.host_authenticator(socket['key'], srp['keys'][0], socket['m_out'].digest())
            if socket['m_in'].digest() != msg['auth']:
                raise ServerError, 'Bad host authentication'
                return
            self.nh.set_hmac(s, socket['m_in'], socket['m_out'])
            self.rs.doneflag.set()
        elif socket['state'] == 3:
            self.socket[s]['hash'] = msg['hash']
            self.rs.doneflag.set()
        elif socket['state'] == 4:
            self.close(s)
            secret = crypt(msg['secret'], socket['key'])[0]
            self.auth.save_secret(secret)
            self.rs.doneflag.set()
        elif socket['state'] == 5:
            self.close(s)
            self.rs.doneflag.set()
        elif socket['state'] == 6:
            if len(msg['salt']) < 20:
                self._send_error(s, None, 'Bad salt length')
                self.close(s)
                raise NetworkError, 'Bad salt from server'

            salt = random_string(20)

            key = self.auth.session_key(salt, msg['salt'])
            socket['m_in'] = hmac.new(key, '', sha)
            key = self.auth.session_key(msg['salt'], salt)
            socket['m_out'] = hmac.new(key, '', sha)

            self._send_msg(s, {'auth': socket['m_in'].digest(),
                               'salt': salt})
            socket['state'] = 7
        elif socket['state'] == 7:
            if msg['auth'] != socket['m_out'].digest():
                self._send_error(s, None, 'Bad auth')
                self.close(s)
                raise NetworkError, 'Bad server auth'
            self._req_mode(s, 1)
            self.nh.set_hmac(s, socket['m_in'], socket['m_out'])
            self.socket[s] = [{}, {}, {}, [], 1]
            self.rs.doneflag.set()
        else:
            self.close(s)

    def connection_flushed(self, s):
        queue = self.socket[s][Queue]
        socket = self.socket[s]
        socket[Flushed] = 1
        while len(queue) and socket[Flushed] == 1:
            mid, msg = queue.pop(0)
            diff = read_diff(self.co, msg['handle'], msg['changenum'], None)
            socket[Flushed] = self._send_response(s, mid, {'diff': diff})

    def connection_lost(self, s, msg):
        del self.socket[s]
        raise NetworkError, msg

    def request_came_in(self, s, mid, data):
        try:
            msg = bdecode(data)
        except ValueError:
            self.close(s)
            raise NetworkError, 'garbage request'
        self.request_handlers[msg['request']](self, s, mid, msg)

    def response_came_in(self, s, mid, data):
        try:
            msg = bdecode(data)
        except ValueError:
            self.close(s)
            raise NetworkError, 'bad data from server'

        rstate = self.socket[s][Request][mid]
        if msg.has_key('error'):
            # XXX: grody hack, diffs which don't belong in history
            if rstate['request'] == 'get diff':
                msg['diff'] = zlib.compress(bencode(''))
            else:
                raise ServerError, msg['error']
        if self.response_handlers[rstate['request']](self, s, mid, msg, rstate):
            del self.socket[s][Request][mid]

    # request handlers
    def _request_get_change(self, s, mid, msg):
        resp = {'changeset': self.co.lcrepo.get(msg['changenum'], txn=self.txn)}
        self._send_response(s, mid, resp)

    def _request_get_diff(self, s, mid, msg):
        if self.socket[s][Flushed] == 1:
            diff = read_diff(self.co, msg['handle'], msg['changenum'], self.txn)
            self.socket[s][Flushed] = self._send_response(s, mid, {'diff': diff})
        else:
            self.socket[s][Queue].append((mid, msg))

    request_handlers = {'get change':  _request_get_change,
                        'get diff':    _request_get_diff}

    # response handlers
    def _response_get_head(self, s, mid, msg, rstate):
        return self._merge_change(s, mid, msg['head'])

    def _merge_change(self, s, mid, head):
        lstate = self.socket[s][Request][mid]
        lstate['head'] = head

        self.socket[s][UpdateInfo]['head'] = head

        if self.co.lcrepo.has_key(head):
            named, modified, added, deleted = \
                   handles_in_branch(self.co, lstate['heads'], [head], None)
            self._update_checks(s, mid, named, modified)
            self._update_handle_list(s, lstate, named, modified, added, deleted)
            self._update_finish(s, lstate)
            self.socket[s][UpdateInfo]['head'] = head
            self.rs.doneflag.set()
            return 1

        rid = self._get_change(s, head)
        self.socket[s][Request][rid] = {'request': 'get change',
                                        'changenum': head,
                                        'ref': mid}
        lstate['requests'][head] = 1
        lstate['count'] = 1
        return 0

    def _response_get_change(self, s, mid, msg, rstate):
        lstate = self.socket[s][Request][rstate['ref']]
        if sha.new(msg['changeset']).digest() != rstate['changenum']:
            raise ServerError, 'bad changeset'

        # write it out, decode and eject from memory
        write_changeset(self.co, rstate['changenum'], msg['changeset'],
                        lstate['txn'])
        changeset = bdecode(msg['changeset'])
        lstate['changes'][rstate['changenum']] = changeset
        del msg['changeset']

        # get any precursors we don't have and haven't yet requested
        for change in changeset['precursors']:
            if self.co.lcrepo.has_key(change):
                continue
            if lstate['changes'].has_key(change):
                continue
            if lstate['requests'].has_key(change):
                continue
            rid = self._get_change(s, change)
            self.socket[s][Request][rid] = {'request': 'get change',
                                            'changenum': change,
                                            'ref': rstate['ref']}
            lstate['requests'][change] = 1
            lstate['count'] += 1

        # record all the diffs we'll need to request
        diffs = lstate['diffs']
        for handle, hinfo in changeset['handles'].items():
            if not hinfo.has_key('hash'):
                continue
            if not diffs.has_key(handle):
                diffs[handle] = {}
                lstate['counts'][handle] = 0
            diffs[handle][rstate['changenum']] = 1
            lstate['counts'][handle] += 1

        # clean up state
        del lstate['requests'][rstate['changenum']]
        lstate['count'] -= 1
        if lstate['count'] == 0:
            sync_history(self.co, lstate['head'], lstate['txn'],
                                    cache=lstate['changes'])
            named, modified, added, deleted = \
                   handles_in_branch(self.co,
                                     lstate['heads'], [lstate['head']],
                                     lstate['txn'], cache=lstate['changes'])
            del lstate['changes']
            self._update_checks(s, rstate['ref'], named, modified)
            self._update_handle_list(s, lstate, named, modified, added, deleted)

            handle_list = lstate['handle list']
            # get all the related file diffs
            for i in xrange(len(handle_list)-1, -1, -1):
                handle = handle_list[i][1]
                if not diffs.has_key(handle):
                    continue
                changes = diffs[handle]

                requested = 0
                for change in changes.keys():
                    requested = 1
                    self._queue_diff(s, change, handle, rstate['ref'])
                lstate['count'] += requested
            self._get_diff(s, rstate['ref'])

        if lstate['count'] == 0:
            self._update_finish(s, lstate)
            del self.socket[s][Request][rstate['ref']]
            self.rs.doneflag.set()
        return 1

    def _response_get_diff(self, s, mid, msg, rstate):
        lstate = self.socket[s][Request][rstate['ref']]

        # send out the next one
        lstate['req-outstanding'] -= 1
        self._get_diff(s, rstate['ref'])

        diffs = lstate['diffs']
        diffs[rstate['handle']][rstate['change']] = msg['diff']
        lstate['counts'][rstate['handle']] -= 1
        if lstate['counts'][rstate['handle']] == 0:
            lstate['count'] -= 1

            # XXX: do better ordering
            WD = WriteDiff(self.co, rstate['handle'], lstate['txn'])
            for change, diff in diffs[rstate['handle']].items():
                WD.write(diff, change)
            WD.close()

            if lstate['modified'].has_key(rstate['handle']):
                updates(self.co, self.socket[s][UpdateInfo], lstate,
                        rstate['handle'])
            #del diffs[rstate['handle']]

        if lstate['count'] == 0:
            self._update_finish(s, lstate)
            self.socket[s][UpdateInfo]['head'] = lstate['head']
            del self.socket[s][Request][rstate['ref']]
            self.rs.doneflag.set()
        return 1

    def _response_return(self, s, mid, msg, rstate):
        self.rs.doneflag.set()
        return 1

    def _response_list_repositories(self, s, mid, msg, rstate):
        self.socket[s][UpdateInfo] = msg['list']
        self.rs.doneflag.set()
        return 1

    response_handlers = {'get head':            _response_get_head,
                         'get change':          _response_get_change,
                         'get diff':            _response_get_diff,
                         'commit':              _response_return,
                         'create repository':   _response_return,
                         'destroy repository':  _response_return,
                         'list repositories':   _response_list_repositories}

    # Internal helper functions
    def _update_checks(self, s, mid, named, modified):
        lstate = self.socket[s][Request][mid]
        check_modified(self.co, modified)
        lstate['modified'] = {}
        for handle in modified:
            lstate['modified'][handle] = 1
        self.socket[s][UpdateInfo]['modified'] = []
        self.socket[s][UpdateInfo]['modified conflicts'] = []

    def _update_handle_list(self, s, lstate, named, modified, added, deleted):
        uinfo = self.socket[s][UpdateInfo]
        func = lstate['update function']

        handles, nconflicts = func(self.co, uinfo,
                                   named, modified, added, deleted,
                                   lstate['txn'])

        handle_list = lstate['handle list']
        for handle, show in handles:
            letters = (' ', ' ')
            if uinfo['newfiles'].has_key(handle):
                letters = ('A', 'A')
            elif uinfo['deletes'].has_key(handle):
                letters = ('D', 'D')
            elif uinfo['names'].has_key(handle):
                letters = (' ', 'N')
            if nconflicts.has_key(handle):
                letters = (letters[0], 'C')
            handle_list.append((handle_to_filename(self.co, handle, lstate['txn']), handle, show, letters))
        handle_list.sort()
        handle_list.reverse()

        uinfo['name conflicts'] = nconflicts.keys()

    def _update_finish(self, s, lstate):
        updates(self.co, self.socket[s][UpdateInfo], lstate, None)

    def _req_mode(self, s, mode):
        self.nh.req_mode(s, mode)

    def _commit(self, s, repo, head):
        rid = self._send_request(s, {'request': 'commit',
                                     'repository': repo,
                                     'changenum': head})
        self.socket[s][Request][rid] = {'request': 'commit'}

    def _update(self, s, repo, func, txn):
        heads = bdecode(self.co.linforepo.get('heads'))
        lstate = {'request':           'get head',
                  'txn':               txn,
                  'changes':           {},
                  'requests':          {},
                  'diffs':             {},
                  'count':             0,
                  'counts':            {},
                  'handle list':       [],
                  'heads':             heads,
                  'reqq':              [],
                  'req-outstanding':   0,
                  'update function':   func}

        if repo.startswith('{') and repo.endswith('}'):
            head = binascii.unhexlify(repo[1:-1])
            rid = self.nh.next_id(s)
            self.socket[s][Request][rid] = lstate
            if self._merge_change(s, rid, head):
                del self.socket[s][Request][rid]
        else:
            rid = self._send_request(s, {'request': 'get head',
                                         'repository': repo})
            self.socket[s][Request][rid] = lstate

        return

    def _create_repo(self, s, repo):
        rid = self._send_request(s, {'request': 'create repository',
                                     'repository': repo})
        self.socket[s][Request][rid] = {'request': 'create repository',
                                        'repository': repo}

    def _remove_repo(self, s, repo):
        rid = self._send_request(s, {'request': 'destroy repository',
                                     'repository': repo})
        self.socket[s][Request][rid] = {'request': 'destroy repository',
                                        'repository': repo}

    def _list_repos(self, s):
        rid = self._send_request(s, {'request': 'list repositories'})
        self.socket[s][Request][rid] = {'request': 'list repositories'}

    def _get_change(self, s, change):
        req = {'request': 'get change', 'changenum': change}
        return self._send_request(s, req)

    def _queue_diff(self, s, change, handle, mid):
        rstate = self.socket[s][Request][mid]
        rstate['reqq'].append((change, handle))

    def _get_diff(self, s, mid):
        rstate = self.socket[s][Request][mid]
        while len(rstate['reqq']) and rstate['req-outstanding'] <= 40:
            change, handle = rstate['reqq'].pop(0)
            req = {'request': 'get diff', 'changenum': change,
                   'handle': handle}
            state = {'request': 'get diff', 'ref': mid, 'change': change,
                     'handle': handle}
            rid = self._send_request(s, req)
            self.socket[s][Request][rid] = state
            rstate['req-outstanding'] += 1

    def _send_msg(self, s, data):
        self.nh.send_msg(s, bencode(data))

    def _send_error(self, s, mid, msg):
        self._send_response(s, mid, {'error': msg})

    def _send_request(self, s, data):
        return self.nh.send_request(s, bencode(data))

    def _send_response(self, s, mid, data):
        return self.nh.send_response(s, mid, bencode(data))

    # Functions to be called from higher level code
    def start_connection(self, dns):
        s = self.nh.start_connection(dns)
        self.socket[s] = {'state': 0, 'srp': {}}
        return s

    def close(self, s):
        self.nh.close(s)
        del self.socket[s]

    def get_hash(self, s):
        self._send_msg(s, {'op': 'get hash', 'user': self.co.user})
        self.socket[s]['state'] = 3
        self.rs.listen_forever(self.nh)
        self.rs.doneflag.clear()
        secret_hash = self.socket[s]['hash']
        del self.socket[s]['hash']
        return secret_hash

    def srp_auth(self, s):
        socket = self.socket[s]
        assert socket['state'] == 0 or socket['state'] == 3
        srp = socket['srp'] = {}
        srp['keys'] = SRP.client_begin()
        self._send_msg(s, {'op': 'srp auth', 'user': self.co.user, 'A': srp['keys'][0]})
        socket['state'] = 1
        self.rs.listen_forever(self.nh)
        self.rs.doneflag.clear()

    def get_secret(self, s):
        self._send_msg(s, {'op': 'get secret'})
        self.socket[s]['state'] = 4
        self.rs.listen_forever(self.nh)
        self.rs.doneflag.clear()
        return

    def set_password(self, s, password):
        socket = self.socket[s]
        salt, v = SRP.new_passwd(self.co.user, password)
        cypherv = crypt(long_to_string(v), socket['key'])[0]
        self._send_msg(s, {'op': 'set password', 's': salt, 'v': cypherv})
        socket['state'] = 5
        self.rs.listen_forever(self.nh)
        self.rs.doneflag.clear()

    def secret_auth(self, s):
        socket = self.socket[s]
        self._send_msg(s, {'op': 'secret auth', 'user': self.co.user})
        socket['state'] = 6
        self.rs.listen_forever(self.nh)
        self.rs.doneflag.clear()

    def commit(self, s, repo, changenum, txn):
        self.txn = txn
        self._commit(s, repo, changenum)
        self.rs.listen_forever(self.nh)
        self.rs.doneflag.clear()

    def update(self, s, repo, func, txn):
        self._update(s, repo, func, txn)
        self.rs.listen_forever(self.nh)
        self.rs.doneflag.clear()
        updateinfo = self.socket[s][UpdateInfo]
        del self.socket[s][UpdateInfo]
        return updateinfo

    def create_repo(self, s, repo):
        self._create_repo(s, repo)
        self.rs.listen_forever(self.nh)
        self.rs.doneflag.clear()

    def remove_repo(self, s, repo):
        self._remove_repo(s, repo)
        self.rs.listen_forever(self.nh)
        self.rs.doneflag.clear()

    def list_repos(self, s):
        self._list_repos(s)
        self.rs.listen_forever(self.nh)
        self.rs.doneflag.clear()
        rlist = self.socket[s][UpdateInfo]
        del self.socket[s][UpdateInfo]
        return rlist
Пример #5
0
def download(params,
             filefunc,
             statusfunc,
             finfunc,
             errorfunc,
             doneflag,
             cols,
             pathFunc=None,
             presets={},
             exchandler=None,
             failed=_failfunc,
             paramfunc=None):

    try:
        config = parse_params(params, presets)
    except ValueError as e:
        failed(
            'error: {}\nrun with no args for parameter explanations'.format(e))
        return
    if not config:
        errorfunc(get_usage())
        return

    myid = createPeerID()
    random.seed(myid)

    rawserver = RawServer(doneflag,
                          config['timeout_check_interval'],
                          config['timeout'],
                          ipv6_enable=config['ipv6_enabled'],
                          failfunc=failed,
                          errorfunc=exchandler)

    upnp_type = UPnP_test(config['upnp_nat_access'])
    try:
        listen_port = rawserver.find_and_bind(
            config['minport'],
            config['maxport'],
            config['bind'],
            ipv6_socket_style=config['ipv6_binds_v4'],
            upnp=upnp_type,
            randomizer=config['random_port'])
    except socket.error as e:
        failed("Couldn't listen - " + str(e))
        return

    response = get_response(config['responsefile'], config['url'], failed)
    if not response:
        return

    infohash = hashlib.sha1(bencode(response['info'])).digest()

    d = BT1Download(statusfunc, finfunc, errorfunc, exchandler, doneflag,
                    config, response, infohash, myid, rawserver, listen_port)

    if not d.saveAs(filefunc):
        return

    if pathFunc:
        pathFunc(d.getFilename())

    hashcheck = d.initFiles(old_style=True)
    if not hashcheck:
        return
    if not hashcheck():
        return
    if not d.startEngine():
        return
    d.startRerequester()
    d.autoStats()

    statusfunc(activity='connecting to peers')

    if paramfunc:
        paramfunc({
            # change_max_upload_rate(<int KiB/sec>)
            'max_upload_rate': d.setUploadRate,
            # change_max_uploads(<int max uploads>)
            'max_uploads': d.setConns,
            'listen_port': listen_port,  # int
            'peer_id': myid,  # string
            'info_hash': infohash,  # string
            # start_connection((<string ip>, <int port>), <peer id>)
            'start_connection': d._startConnection,
        })

    rawserver.listen_forever(d.getPortHandler())

    d.shutdown()
# To connect, run: nc localhost 8080

from threading import Event
from RawServer import RawServer

done_flag = Event()
class Handler:
  def external_connection_made(self, connection):
    connection.write("hello there stranger!\n")
  def connection_flushed(self, connection):
    # Wrote string, so close all connections and quit.
    done_flag.set()

timeout_check_interval = 60 * 60
timeout = timeout_check_interval
server = RawServer(done_flag, timeout_check_interval, timeout)
server.bind(8080)
server.listen_forever(Handler())

Пример #7
0
def track(args):
    if len(args) == 0:
        print formatDefinitions(defaults, 80)
        return
    try:
        config, files = parseargs(args, defaults, 0, 0)
    except ValueError, e:
        print 'error: ' + str(e)
        print 'run with no arguments for parameter explanations'
        return
    r = RawServer(Event(), config['timeout_check_interval'],
                  config['socket_timeout'])
    t = Tracker(config, r)
    r.bind(config['port'], config['bind'], True)
    r.listen_forever(HTTPHandler(t.get,
                                 config['min_time_between_log_flushes']))
    t.save_dfile()
    print '# Shutting down: ' + isotime()


def size_format(s):
    if (s < 1024):
        r = str(s) + 'B'
    elif (s < 1048576):
        r = str(int(s / 1024)) + 'KiB'
    elif (s < 1073741824l):
        r = str(int(s / 1048576)) + 'MiB'
    elif (s < 1099511627776l):
        r = str(int((s / 1073741824.0) * 100.0) / 100.0) + 'GiB'
    else:
        r = str(int((s / 1099511627776.0) * 100.0) / 100.0) + 'TiB'
Пример #8
0
class ClientHandler:
    def __init__(self, co):
        self.co = co
        self.socket = {}
        self.rs = RawServer(Event(), 100, 1000)
        self.nh = NetworkHandler(self)

    # Functions called from lower level code
    def message_came_in(self, s, data):
        try:
            msg = bdecode(data)
        except ValueError:
            self.close(s)
            raise NetworkError, 'garbage data'
        if msg.has_key('error'):
            raise ServerError, msg['error']
        socket = self.socket[s]
        srp = socket['srp']
        if socket['state'] == 1:
            K, m = self.auth.client_key(msg['s'], msg['B'], msg['u'],
                                        srp['keys'])
            socket['key'], socket['m_out'] = K, m
            self._send_msg(s, {'m': socket['m_out'].digest()})
            socket['state'] = 2
        elif socket['state'] == 2:
            socket['m_in'] = SRP.host_authenticator(socket['key'],
                                                    srp['keys'][0],
                                                    socket['m_out'].digest())
            if socket['m_in'].digest() != msg['auth']:
                raise ServerError, 'Bad host authentication'
                return
            self.nh.set_hmac(s, socket['m_in'], socket['m_out'])
            self.rs.doneflag.set()
        elif socket['state'] == 3:
            self.socket[s]['hash'] = msg['hash']
            self.rs.doneflag.set()
        elif socket['state'] == 4:
            self.close(s)
            secret = crypt(msg['secret'], socket['key'])[0]
            self.auth.save_secret(secret)
            self.rs.doneflag.set()
        elif socket['state'] == 5:
            self.close(s)
            self.rs.doneflag.set()
        elif socket['state'] == 6:
            if len(msg['salt']) < 20:
                self._send_error(s, None, 'Bad salt length')
                self.close(s)
                raise NetworkError, 'Bad salt from server'

            salt = random_string(20)

            key = self.auth.session_key(salt, msg['salt'])
            socket['m_in'] = hmac.new(key, '', sha)
            key = self.auth.session_key(msg['salt'], salt)
            socket['m_out'] = hmac.new(key, '', sha)

            self._send_msg(s, {'auth': socket['m_in'].digest(), 'salt': salt})
            socket['state'] = 7
        elif socket['state'] == 7:
            if msg['auth'] != socket['m_out'].digest():
                self._send_error(s, None, 'Bad auth')
                self.close(s)
                raise NetworkError, 'Bad server auth'
            self._req_mode(s, 1)
            self.nh.set_hmac(s, socket['m_in'], socket['m_out'])
            self.socket[s] = [{}, {}, {}, [], 1]
            self.rs.doneflag.set()
        else:
            self.close(s)

    def connection_flushed(self, s):
        queue = self.socket[s][Queue]
        socket = self.socket[s]
        socket[Flushed] = 1
        while len(queue) and socket[Flushed] == 1:
            mid, msg = queue.pop(0)
            diff = read_diff(self.co, msg['handle'], msg['changenum'], None)
            socket[Flushed] = self._send_response(s, mid, {'diff': diff})

    def connection_lost(self, s, msg):
        del self.socket[s]
        raise NetworkError, msg

    def request_came_in(self, s, mid, data):
        try:
            msg = bdecode(data)
        except ValueError:
            self.close(s)
            raise NetworkError, 'garbage request'
        self.request_handlers[msg['request']](self, s, mid, msg)

    def response_came_in(self, s, mid, data):
        try:
            msg = bdecode(data)
        except ValueError:
            self.close(s)
            raise NetworkError, 'bad data from server'

        rstate = self.socket[s][Request][mid]
        if msg.has_key('error'):
            # XXX: grody hack, diffs which don't belong in history
            if rstate['request'] == 'get diff':
                msg['diff'] = zlib.compress(bencode(''))
            else:
                raise ServerError, msg['error']
        if self.response_handlers[rstate['request']](self, s, mid, msg,
                                                     rstate):
            del self.socket[s][Request][mid]

    # request handlers
    def _request_get_change(self, s, mid, msg):
        resp = {
            'changeset': self.co.lcrepo.get(msg['changenum'], txn=self.txn)
        }
        self._send_response(s, mid, resp)

    def _request_get_diff(self, s, mid, msg):
        if self.socket[s][Flushed] == 1:
            diff = read_diff(self.co, msg['handle'], msg['changenum'],
                             self.txn)
            self.socket[s][Flushed] = self._send_response(
                s, mid, {'diff': diff})
        else:
            self.socket[s][Queue].append((mid, msg))

    request_handlers = {
        'get change': _request_get_change,
        'get diff': _request_get_diff
    }

    # response handlers
    def _response_get_head(self, s, mid, msg, rstate):
        return self._merge_change(s, mid, msg['head'])

    def _merge_change(self, s, mid, head):
        lstate = self.socket[s][Request][mid]
        lstate['head'] = head

        self.socket[s][UpdateInfo]['head'] = head

        if self.co.lcrepo.has_key(head):
            named, modified, added, deleted = \
                   handles_in_branch(self.co, lstate['heads'], [head], None)
            self._update_checks(s, mid, named, modified)
            self._update_handle_list(s, lstate, named, modified, added,
                                     deleted)
            self._update_finish(s, lstate)
            self.socket[s][UpdateInfo]['head'] = head
            self.rs.doneflag.set()
            return 1

        rid = self._get_change(s, head)
        self.socket[s][Request][rid] = {
            'request': 'get change',
            'changenum': head,
            'ref': mid
        }
        lstate['requests'][head] = 1
        lstate['count'] = 1
        return 0

    def _response_get_change(self, s, mid, msg, rstate):
        lstate = self.socket[s][Request][rstate['ref']]
        if sha.new(msg['changeset']).digest() != rstate['changenum']:
            raise ServerError, 'bad changeset'

        # write it out, decode and eject from memory
        write_changeset(self.co, rstate['changenum'], msg['changeset'],
                        lstate['txn'])
        changeset = bdecode(msg['changeset'])
        lstate['changes'][rstate['changenum']] = changeset
        del msg['changeset']

        # get any precursors we don't have and haven't yet requested
        for change in changeset['precursors']:
            if self.co.lcrepo.has_key(change):
                continue
            if lstate['changes'].has_key(change):
                continue
            if lstate['requests'].has_key(change):
                continue
            rid = self._get_change(s, change)
            self.socket[s][Request][rid] = {
                'request': 'get change',
                'changenum': change,
                'ref': rstate['ref']
            }
            lstate['requests'][change] = 1
            lstate['count'] += 1

        # record all the diffs we'll need to request
        diffs = lstate['diffs']
        for handle, hinfo in changeset['handles'].items():
            if not hinfo.has_key('hash'):
                continue
            if not diffs.has_key(handle):
                diffs[handle] = {}
                lstate['counts'][handle] = 0
            diffs[handle][rstate['changenum']] = 1
            lstate['counts'][handle] += 1

        # clean up state
        del lstate['requests'][rstate['changenum']]
        lstate['count'] -= 1
        if lstate['count'] == 0:
            sync_history(self.co,
                         lstate['head'],
                         lstate['txn'],
                         cache=lstate['changes'])
            named, modified, added, deleted = \
                   handles_in_branch(self.co,
                                     lstate['heads'], [lstate['head']],
                                     lstate['txn'], cache=lstate['changes'])
            del lstate['changes']
            self._update_checks(s, rstate['ref'], named, modified)
            self._update_handle_list(s, lstate, named, modified, added,
                                     deleted)

            handle_list = lstate['handle list']
            # get all the related file diffs
            for i in xrange(len(handle_list) - 1, -1, -1):
                handle = handle_list[i][1]
                if not diffs.has_key(handle):
                    continue
                changes = diffs[handle]

                requested = 0
                for change in changes.keys():
                    requested = 1
                    self._queue_diff(s, change, handle, rstate['ref'])
                lstate['count'] += requested
            self._get_diff(s, rstate['ref'])

        if lstate['count'] == 0:
            self._update_finish(s, lstate)
            del self.socket[s][Request][rstate['ref']]
            self.rs.doneflag.set()
        return 1

    def _response_get_diff(self, s, mid, msg, rstate):
        lstate = self.socket[s][Request][rstate['ref']]

        # send out the next one
        lstate['req-outstanding'] -= 1
        self._get_diff(s, rstate['ref'])

        diffs = lstate['diffs']
        diffs[rstate['handle']][rstate['change']] = msg['diff']
        lstate['counts'][rstate['handle']] -= 1
        if lstate['counts'][rstate['handle']] == 0:
            lstate['count'] -= 1

            # XXX: do better ordering
            WD = WriteDiff(self.co, rstate['handle'], lstate['txn'])
            for change, diff in diffs[rstate['handle']].items():
                WD.write(diff, change)
            WD.close()

            if lstate['modified'].has_key(rstate['handle']):
                updates(self.co, self.socket[s][UpdateInfo], lstate,
                        rstate['handle'])
            #del diffs[rstate['handle']]

        if lstate['count'] == 0:
            self._update_finish(s, lstate)
            self.socket[s][UpdateInfo]['head'] = lstate['head']
            del self.socket[s][Request][rstate['ref']]
            self.rs.doneflag.set()
        return 1

    def _response_return(self, s, mid, msg, rstate):
        self.rs.doneflag.set()
        return 1

    def _response_list_repositories(self, s, mid, msg, rstate):
        self.socket[s][UpdateInfo] = msg['list']
        self.rs.doneflag.set()
        return 1

    response_handlers = {
        'get head': _response_get_head,
        'get change': _response_get_change,
        'get diff': _response_get_diff,
        'commit': _response_return,
        'create repository': _response_return,
        'destroy repository': _response_return,
        'list repositories': _response_list_repositories
    }

    # Internal helper functions
    def _update_checks(self, s, mid, named, modified):
        lstate = self.socket[s][Request][mid]
        check_modified(self.co, modified)
        lstate['modified'] = {}
        for handle in modified:
            lstate['modified'][handle] = 1
        self.socket[s][UpdateInfo]['modified'] = []
        self.socket[s][UpdateInfo]['modified conflicts'] = []

    def _update_handle_list(self, s, lstate, named, modified, added, deleted):
        uinfo = self.socket[s][UpdateInfo]
        func = lstate['update function']

        handles, nconflicts = func(self.co, uinfo, named, modified, added,
                                   deleted, lstate['txn'])

        handle_list = lstate['handle list']
        for handle, show in handles:
            letters = (' ', ' ')
            if uinfo['newfiles'].has_key(handle):
                letters = ('A', 'A')
            elif uinfo['deletes'].has_key(handle):
                letters = ('D', 'D')
            elif uinfo['names'].has_key(handle):
                letters = (' ', 'N')
            if nconflicts.has_key(handle):
                letters = (letters[0], 'C')
            handle_list.append(
                (handle_to_filename(self.co, handle,
                                    lstate['txn']), handle, show, letters))
        handle_list.sort()
        handle_list.reverse()

        uinfo['name conflicts'] = nconflicts.keys()

    def _update_finish(self, s, lstate):
        updates(self.co, self.socket[s][UpdateInfo], lstate, None)

    def _req_mode(self, s, mode):
        self.nh.req_mode(s, mode)

    def _commit(self, s, repo, head):
        rid = self._send_request(s, {
            'request': 'commit',
            'repository': repo,
            'changenum': head
        })
        self.socket[s][Request][rid] = {'request': 'commit'}

    def _update(self, s, repo, func, txn):
        heads = bdecode(self.co.linforepo.get('heads'))
        lstate = {
            'request': 'get head',
            'txn': txn,
            'changes': {},
            'requests': {},
            'diffs': {},
            'count': 0,
            'counts': {},
            'handle list': [],
            'heads': heads,
            'reqq': [],
            'req-outstanding': 0,
            'update function': func
        }

        if repo.startswith('{') and repo.endswith('}'):
            head = binascii.unhexlify(repo[1:-1])
            rid = self.nh.next_id(s)
            self.socket[s][Request][rid] = lstate
            if self._merge_change(s, rid, head):
                del self.socket[s][Request][rid]
        else:
            rid = self._send_request(s, {
                'request': 'get head',
                'repository': repo
            })
            self.socket[s][Request][rid] = lstate

        return

    def _create_repo(self, s, repo):
        rid = self._send_request(s, {
            'request': 'create repository',
            'repository': repo
        })
        self.socket[s][Request][rid] = {
            'request': 'create repository',
            'repository': repo
        }

    def _remove_repo(self, s, repo):
        rid = self._send_request(s, {
            'request': 'destroy repository',
            'repository': repo
        })
        self.socket[s][Request][rid] = {
            'request': 'destroy repository',
            'repository': repo
        }

    def _list_repos(self, s):
        rid = self._send_request(s, {'request': 'list repositories'})
        self.socket[s][Request][rid] = {'request': 'list repositories'}

    def _get_change(self, s, change):
        req = {'request': 'get change', 'changenum': change}
        return self._send_request(s, req)

    def _queue_diff(self, s, change, handle, mid):
        rstate = self.socket[s][Request][mid]
        rstate['reqq'].append((change, handle))

    def _get_diff(self, s, mid):
        rstate = self.socket[s][Request][mid]
        while len(rstate['reqq']) and rstate['req-outstanding'] <= 40:
            change, handle = rstate['reqq'].pop(0)
            req = {
                'request': 'get diff',
                'changenum': change,
                'handle': handle
            }
            state = {
                'request': 'get diff',
                'ref': mid,
                'change': change,
                'handle': handle
            }
            rid = self._send_request(s, req)
            self.socket[s][Request][rid] = state
            rstate['req-outstanding'] += 1

    def _send_msg(self, s, data):
        self.nh.send_msg(s, bencode(data))

    def _send_error(self, s, mid, msg):
        self._send_response(s, mid, {'error': msg})

    def _send_request(self, s, data):
        return self.nh.send_request(s, bencode(data))

    def _send_response(self, s, mid, data):
        return self.nh.send_response(s, mid, bencode(data))

    # Functions to be called from higher level code
    def start_connection(self, dns):
        s = self.nh.start_connection(dns)
        self.socket[s] = {'state': 0, 'srp': {}}
        return s

    def close(self, s):
        self.nh.close(s)
        del self.socket[s]

    def get_hash(self, s):
        self._send_msg(s, {'op': 'get hash', 'user': self.co.user})
        self.socket[s]['state'] = 3
        self.rs.listen_forever(self.nh)
        self.rs.doneflag.clear()
        secret_hash = self.socket[s]['hash']
        del self.socket[s]['hash']
        return secret_hash

    def srp_auth(self, s):
        socket = self.socket[s]
        assert socket['state'] == 0 or socket['state'] == 3
        srp = socket['srp'] = {}
        srp['keys'] = SRP.client_begin()
        self._send_msg(s, {
            'op': 'srp auth',
            'user': self.co.user,
            'A': srp['keys'][0]
        })
        socket['state'] = 1
        self.rs.listen_forever(self.nh)
        self.rs.doneflag.clear()

    def get_secret(self, s):
        self._send_msg(s, {'op': 'get secret'})
        self.socket[s]['state'] = 4
        self.rs.listen_forever(self.nh)
        self.rs.doneflag.clear()
        return

    def set_password(self, s, password):
        socket = self.socket[s]
        salt, v = SRP.new_passwd(self.co.user, password)
        cypherv = crypt(long_to_string(v), socket['key'])[0]
        self._send_msg(s, {'op': 'set password', 's': salt, 'v': cypherv})
        socket['state'] = 5
        self.rs.listen_forever(self.nh)
        self.rs.doneflag.clear()

    def secret_auth(self, s):
        socket = self.socket[s]
        self._send_msg(s, {'op': 'secret auth', 'user': self.co.user})
        socket['state'] = 6
        self.rs.listen_forever(self.nh)
        self.rs.doneflag.clear()

    def commit(self, s, repo, changenum, txn):
        self.txn = txn
        self._commit(s, repo, changenum)
        self.rs.listen_forever(self.nh)
        self.rs.doneflag.clear()

    def update(self, s, repo, func, txn):
        self._update(s, repo, func, txn)
        self.rs.listen_forever(self.nh)
        self.rs.doneflag.clear()
        updateinfo = self.socket[s][UpdateInfo]
        del self.socket[s][UpdateInfo]
        return updateinfo

    def create_repo(self, s, repo):
        self._create_repo(s, repo)
        self.rs.listen_forever(self.nh)
        self.rs.doneflag.clear()

    def remove_repo(self, s, repo):
        self._remove_repo(s, repo)
        self.rs.listen_forever(self.nh)
        self.rs.doneflag.clear()

    def list_repos(self, s):
        self._list_repos(s)
        self.rs.listen_forever(self.nh)
        self.rs.doneflag.clear()
        rlist = self.socket[s][UpdateInfo]
        del self.socket[s][UpdateInfo]
        return rlist