Blame | Last modification | View Log | Download
#written by John Hoffmanfrom inifile import ini_write, ini_readfrom bencode import bencode, bdecodefrom types import IntType, LongType, StringType, FloatTypefrom CreateIcons import GetIcons, CreateIconfrom parseargs import defaultargsfrom __init__ import product_name, version_shortimport sys,osfrom time import time, strftimetry:Trueexcept:True = 1False = 0try:realpath = os.path.realpathexcept:realpath = lambda x:xOLDICONPATH = os.path.abspath(os.path.dirname(realpath(sys.argv[0])))DIRNAME = '.'+product_namehexchars = '0123456789abcdef'hexmap = []revmap = {}for i in xrange(256):x = hexchars[(i&0xF0)/16]+hexchars[i&0x0F]hexmap.append(x)revmap[x] = chr(i)def tohex(s):r = []for c in s:r.append(hexmap[ord(c)])return ''.join(r)def unhex(s):r = [ revmap[s[x:x+2]] for x in xrange(0, len(s), 2) ]return ''.join(r)def copyfile(oldpath, newpath): # simple file copy, all in RAMtry:f = open(oldpath,'rb')r = f.read()success = Trueexcept:success = Falsetry:f.close()except:passif not success:return Falsetry:f = open(newpath,'wb')f.write(r)except:success = Falsetry:f.close()except:passreturn successclass ConfigDir:###### INITIALIZATION TASKS ######def __init__(self, config_type = None):self.config_type = config_typeif config_type:config_ext = '.'+config_typeelse:config_ext = ''def check_sysvars(x):y = os.path.expandvars(x)if y != x and os.path.isdir(y):return yreturn Nonefor d in ['${APPDATA}', '${HOME}', '${HOMEPATH}', '${USERPROFILE}']:dir_root = check_sysvars(d)if dir_root:breakelse:dir_root = os.path.expanduser('~')if not os.path.isdir(dir_root):dir_root = os.path.abspath(os.path.dirname(sys.argv[0]))dir_root = os.path.join(dir_root,DIRNAME)self.dir_root = dir_rootif not os.path.isdir(self.dir_root):os.mkdir(self.dir_root,0700) # exception if failedself.dir_icons = os.path.join(dir_root,'icons')if not os.path.isdir(self.dir_icons):os.mkdir(self.dir_icons)for icon in GetIcons():i = os.path.join(self.dir_icons,icon)if not os.path.exists(i):if not copyfile(os.path.join(OLDICONPATH,icon),i):CreateIcon(icon,self.dir_icons)self.dir_torrentcache = os.path.join(dir_root,'torrentcache')if not os.path.isdir(self.dir_torrentcache):os.mkdir(self.dir_torrentcache)self.dir_datacache = os.path.join(dir_root,'datacache')if not os.path.isdir(self.dir_datacache):os.mkdir(self.dir_datacache)self.dir_piececache = os.path.join(dir_root,'piececache')if not os.path.isdir(self.dir_piececache):os.mkdir(self.dir_piececache)self.configfile = os.path.join(dir_root,'config'+config_ext+'.ini')self.statefile = os.path.join(dir_root,'state'+config_ext)self.TorrentDataBuffer = {}###### CONFIG HANDLING ######def setDefaults(self, defaults, ignore=[]):self.config = defaultargs(defaults)for k in ignore:if self.config.has_key(k):del self.config[k]def checkConfig(self):return os.path.exists(self.configfile)def loadConfig(self):try:r = ini_read(self.configfile)['']except:return self.configl = self.config.keys()for k,v in r.items():if self.config.has_key(k):t = type(self.config[k])try:if t == StringType:self.config[k] = velif t == IntType or t == LongType:self.config[k] = long(v)elif t == FloatType:self.config[k] = float(v)l.remove(k)except:passif l: # new default values since last saveself.saveConfig()return self.configdef saveConfig(self, new_config = None):if new_config:for k,v in new_config.items():if self.config.has_key(k):self.config[k] = vtry:ini_write( self.configfile, self.config,'Generated by '+product_name+'/'+version_short+'\n'+ strftime('%x %X') )return Trueexcept:return Falsedef getConfig(self):return self.config###### STATE HANDLING ######def getState(self):try:f = open(self.statefile,'rb')r = f.read()except:r = Nonetry:f.close()except:passtry:r = bdecode(r)except:r = Nonereturn rdef saveState(self, state):try:f = open(self.statefile,'wb')f.write(bencode(state))success = Trueexcept:success = Falsetry:f.close()except:passreturn success###### TORRENT HANDLING ######def getTorrents(self):d = {}for f in os.listdir(self.dir_torrentcache):f = os.path.basename(f)try:f, garbage = f.split('.')except:passd[unhex(f)] = 1return d.keys()def getTorrentVariations(self, t):t = tohex(t)d = []for f in os.listdir(self.dir_torrentcache):f = os.path.basename(f)if f[:len(t)] == t:try:garbage, ver = f.split('.')except:ver = '0'd.append(int(ver))d.sort()return ddef getTorrent(self, t, v = -1):t = tohex(t)if v == -1:v = max(self.getTorrentVariations(t)) # potential exceptionif v:t += '.'+str(v)try:f = open(os.path.join(self.dir_torrentcache,t),'rb')r = bdecode(f.read())except:r = Nonetry:f.close()except:passreturn rdef writeTorrent(self, data, t, v = -1):t = tohex(t)if v == -1:try:v = max(self.getTorrentVariations(t))+1except:v = 0if v:t += '.'+str(v)try:f = open(os.path.join(self.dir_torrentcache,t),'wb')f.write(bencode(data))except:v = Nonetry:f.close()except:passreturn v###### TORRENT DATA HANDLING ######def getTorrentData(self, t):if self.TorrentDataBuffer.has_key(t):return self.TorrentDataBuffer[t]t = os.path.join(self.dir_datacache,tohex(t))if not os.path.exists(t):return Nonetry:f = open(t,'rb')r = bdecode(f.read())except:r = Nonetry:f.close()except:passself.TorrentDataBuffer[t] = rreturn rdef writeTorrentData(self, t, data):self.TorrentDataBuffer[t] = datatry:f = open(os.path.join(self.dir_datacache,tohex(t)),'wb')f.write(bencode(data))success = Trueexcept:success = Falsetry:f.close()except:passif not success:self.deleteTorrentData(t)return successdef deleteTorrentData(self, t):try:os.remove(os.path.join(self.dir_datacache,tohex(t)))except:passdef getPieceDir(self, t):return os.path.join(self.dir_piececache,tohex(t))###### EXPIRATION HANDLING ######def deleteOldCacheData(self, days, still_active = [], delete_torrents = False):if not days:returnexptime = time() - (days*24*3600)names = {}times = {}for f in os.listdir(self.dir_torrentcache):p = os.path.join(self.dir_torrentcache,f)f = os.path.basename(f)try:f, garbage = f.split('.')except:passtry:f = unhex(f)assert len(f) == 20except:continueif delete_torrents:names.setdefault(f,[]).append(p)try:t = os.path.getmtime(p)except:t = time()times.setdefault(f,[]).append(t)for f in os.listdir(self.dir_datacache):p = os.path.join(self.dir_datacache,f)try:f = unhex(os.path.basename(f))assert len(f) == 20except:continuenames.setdefault(f,[]).append(p)try:t = os.path.getmtime(p)except:t = time()times.setdefault(f,[]).append(t)for f in os.listdir(self.dir_piececache):p = os.path.join(self.dir_piececache,f)try:f = unhex(os.path.basename(f))assert len(f) == 20except:continuefor f2 in os.listdir(p):p2 = os.path.join(p,f2)names.setdefault(f,[]).append(p2)try:t = os.path.getmtime(p2)except:t = time()times.setdefault(f,[]).append(t)names.setdefault(f,[]).append(p)for k,v in times.items():if max(v) < exptime and not k in still_active:for f in names[k]:try:os.remove(f)except:try:os.removedirs(f)except:passdef deleteOldTorrents(self, days, still_active = []):self.deleteOldCacheData(days, still_active, True)###### OTHER ######def getIconDir(self):return self.dir_icons