# Written by Pawel Garbacki, Arno Bakker # see LICENSE.txt for license information """ SecureOverlay message handler for a Helper""" from sha import sha import sys, os from random import randint from Swapper.Overlay.SecureOverlay import SecureOverlay from Swapper.utilities import show_permid from Swapper.CacheDB.CacheDBHandler import FriendDBHandler from BitTornado.bencode import bencode, bdecode from BitTornado.BT1.MessageID import * DEBUG = False def get_random_filename(dir): while True: name = str(randint(1, sys.maxint - 1)) p = os.path.join(dir, name) if not os.path.exists(p): return name class HelperMessageHandler: def __init__(self,launchmany): self.metadata_queue = {} self.launchmany = launchmany self.helpdir = launchmany.torrent_dir def register(self, metadata_handler): self.metadata_handler = metadata_handler def handleMessage(self,permid,message): t = message[0] #if DEBUG: # print >> sys.stderr,"helper: Got",getMessageName(t) # Access control friends = FriendDBHandler().getFriends() flag = 0 for peer in friends: if peer['permid'] == permid: if DEBUG: print >> sys.stderr,"helper: Got",getMessageName(t),"from friend",peer['name'] flag = 1 break if flag == 0: if DEBUG: print >> sys.stderr,"helper: Got",getMessageName(t),"from unknown peer",show_permid(permid) return False if t == DOWNLOAD_HELP: return self.got_dlhelp_request(permid, message) elif t == STOP_DOWNLOAD_HELP: return self.got_stop_dlhelp_request(permid, message) elif t == PIECES_RESERVED: return self.got_pieces_reserved(permid, message) def got_dlhelp_request(self, permid, message): try: torrent_hash = message[1:] except: errorfunc("warning: bad data in dlhelp_request") return False # TODO: add smarter concurrency control, see SecureOverlay. Currently has 1 big lock if not self.can_help(torrent_hash): return False torrent_path = self.find_torrent(torrent_hash) if torrent_path: self.do_help(torrent_hash, torrent_path, permid) else: self.get_metadata(permid, torrent_hash) return True # It is very important here that we create safe filenames, i.e., it should # not be possible for a coordinator to send a METADATA message that causes # important files to be overwritten # def do_help(self, torrent_hash, torrent_data, permid): d = bdecode(torrent_data) data = {} data['file'] = get_random_filename(self.helpdir) data['type'] = 'torrent' i = d['info'] h = sha(bencode(d['info'])).digest() assert(h == torrent_hash) l = 0 nf = 0 if i.has_key('length'): l = i.get('length', 0) nf = 1 elif i.has_key('files'): for li in i['files']: nf += 1 if li.has_key('length'): l += li['length'] data['numfiles'] = nf data['length'] = l data['name'] = i.get('name', data['file']) dest = os.path.join(self.helpdir, data['file'] ) data['dest'] = dest # These values are used by abcengine.py to create BT1Download data['coordinator_permid'] = permid tfile = os.path.join(self.helpdir, data['file'] + '.torrent') data['path'] = tfile def setkey(k, d = d, data = data): if d.has_key(k): data[k] = d[k] setkey('failure reason') setkey('warning message') setkey('announce-list') data['metainfo'] = d friendname = None friends = FriendDBHandler().getFriends() for peer in friends: if peer['permid'] == permid: friendname = peer['name'] break data['friendname'] = friendname if DEBUG: print >> sys.stderr,"helpmsg: Got metadata required for helping",friendname print >> sys.stderr,"helpmsg: name: ", data['name'] print >> sys.stderr,"helpmsg: torrent: ", data['path'] print >> sys.stderr,"helpmsg: saveas: ", data['file'] # TODO: instead of writing .torrent to the disk keep it only in the memory torrent_file = open(data['path'], "wb") torrent_file.write(torrent_data) torrent_file.close() self.launchmany.torrent_cache[torrent_hash] = data self.launchmany.file_cache[data['path']] = \ [(os.path.getmtime(data['path']), os.path.getsize(data['path'])), torrent_hash] # These values are used by launchmanycore??? in text mode???? self.launchmany.config['role'] = 'helper' self.launchmany.config['coordinator_permid'] = permid # Start new download self.launchmany.add(torrent_hash, data) def get_metadata(self, permid, torrent_hash): if not self.metadata_queue.has_key(torrent_hash): self.metadata_queue[torrent_hash] = [] self.metadata_queue[torrent_hash].append(permid) self.metadata_handler.send_metadata_request(permid, torrent_hash) def call_dlhelp_task(self, torrent_hash, torrent_data): if DEBUG: print >> sys.stderr,"helpmsg: Metadata handler reports torrent is in." if not self.metadata_queue.has_key(torrent_hash) or not self.metadata_queue[torrent_hash]: if DEBUG: print >> sys.stderr,"helpmsg: Metadata handler reported a torrent we are not waiting for." return for permid in self.metadata_queue[torrent_hash]: # only ask for metadata once self.do_help(torrent_hash, torrent_data, permid) del self.metadata_queue[torrent_hash] def can_help(self, torrent_hash): #TODO: test if I can help the cordinator to download this file return True #Future support: make the decision based on my preference def find_torrent(self, torrent_hash): return None def got_stop_dlhelp_request(self, permid, message): try: torrent_hash = message[1:] except: errorfunc("warning: bad data in STOP_DOWNLOAD_HELP") return False h = self.launchmany.get_helper(torrent_hash) if h is None: return False if not h.is_coordinator(permid): return False self.launchmany.remove(torrent_hash) return True def got_pieces_reserved(self,permid, message): try: torrent_hash = message[1:21] pieces = bdecode(message[21:]) except: errorfunc("warning: bad data in PIECES_RESERVED") return False # TODO: add smarter concurrency control, see SecureOverlay. Currently has 1 big lock h = self.launchmany.get_helper(torrent_hash) if h is None: return False if not h.is_coordinator(permid): return False h.got_pieces_reserved(permid, pieces) # Wake up download thread h.notify() return True def errorfunc(self,msg): if DEBUG: print msg