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()
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)
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)
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
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())
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'
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