# Copyright 2008 The Tor Project, Inc. See LICENSE for licensing information. import os import sys import tempfile import random try: import json except ImportError: import simplejson as json try: import _winreg except ImportError: _winreg = None import thandy.formats import thandy.keys import thandy.master_keys def moveFile(fromLocation, toLocation): """Move the file from fromLocation to toLocation, removing any file in toLocation. """ if sys.platform in ('cygwin', 'win32'): # Win32 doesn't let rename replace an existing file. try: os.unlink(toLocation) except OSError: pass os.rename(fromLocation, toLocation) def replaceFile(fname, contents, textMode=False): """overwrite the file in 'fname' atomically with the content of 'contents' """ dir, prefix = os.path.split(fname) fd, fname_tmp = tempfile.mkstemp(prefix=prefix, dir=dir, text=textMode) try: os.write(fd, contents) finally: os.close(fd) moveFile(fname_tmp, fname) def userFilename(name): """Return a path relative to $THANDY_HOME or ~/.thandy whose final path component is 'name', creating parent directories as needed.""" try: base = os.environ["THANDY_HOME"] except KeyError: base = "~/.thandy" result = os.path.normpath(os.path.join(base, name)) ensureParentDir(result) return result def ensureParentDir(name): """If the parent directory of 'name' does not exist, create it.""" directory = os.path.split(name)[0] if not os.path.exists(directory): os.makedirs(directory, 0700) def getKeylist(keys_fname, checkKeys=True): """Return a Keylist() containing all the keys in master_keys, plus all the keys in $THANDY_HOME.preload_keys, plus all the keys stored in keys_fname. If check_keys, exclude from keys_fname any keys not signed by enough master keys. Do not allow master keys to occur in keys_fname.""" keydb = thandy.formats.Keylist() for key in thandy.master_keys.MASTER_KEYS: keydb.addKey(thandy.keys.RSAKey.fromJSon(key)) user_keys = userFilename("preload_keys") if os.path.exists(user_keys): #XXXX somewhat roundabout. keylist = thandy.formats.makeKeylistObj(user_keys) keydb.addFromKeylist(keylist, allowMasterKeys=True) if keys_fname and os.path.exists(keys_fname): f = open(keys_fname, 'r') try: obj = json.load(f) finally: f.close() ss, role, path = thandy.formats.checkSignedObj(obj, keydb) if role != 'master': raise thandy.FormatException("%s wasn't a keylist."%keys_fname) if checkKeys and not ss.isValid(): raise thandy.FormatException("%s not signed by enough master keys"% keys_fname) keydb.addFromKeylist(obj['signed'], allowMasterKeys=False) return keydb def randChooseWeighted(lst): """Given a list of (weight,item) tuples, pick an item with probability proportional to its weight. """ totalweight = sum(w for w,i in lst) position = random.uniform(0, totalweight) soFar = 0 # We could use bisect here, but this is not going to be in the # critical path. If it is, oops. for w,i in lst: soFar += w if position < soFar: return i return lst[-1][1] class NoRegistry(thandy.Exception): """Exception raised when we try to access the registry on a non-win32 machine.""" pass def getRegistryValue(keyname): """Read the contents of a Windows registry key from a given base.""" if _winreg is None: raise NoRegistry() hkey, rest = keyname.split("\\", 1) key, value = rest.rsplit("\\", 1) if not hkey.startswith("HKEY_"): return None base = getattr(_winreg, hkey) settings = None try: try: settings = _winreg.OpenKey(base, key) return _winreg.QueryValueEx(settings, value)[0] except (WindowsError, ValueError, TypeError): return None finally: if settings is not None: settings.Close()