# Copyright 2008 The Tor Project, Inc. See LICENSE for licensing information. import os import sys import getopt import time import thandy.keys import thandy.formats import thandy.util json = thandy.util.importJSON() def tstamp(): return time.strftime("%Y%m%d_%H%M%S", time.localtime()) def snarf(fname): """Return a string containing the binary contents of the file named "fname." """ f = open(fname, 'rb') try: return f.read() finally: f.close() def snarfObj(fname): """Return a 2-tuple of (object, len), where the object stored in json format in the file 'fname', and len is that file's length. """ f = open(fname, 'r') try: length = os.fstat(f.fileno()).st_size return json.load(f), length finally: f.close() def insert(args): repo = os.environ.get("THANDY_MASTER_REPO") backupDir = thandy.util.userFilename("old_files") checkSigs = True options, args = getopt.getopt(args, "", ["repo=", "no-check"]) for o,v in options: if o == "--repo": repo = v elif o == "--no-check": checkSigs = False if not repo: print "No repository specified." usage() if not os.path.exists(repo): print "No such repository as %r"%repo usage() if not os.path.exists(backupDir): os.makedirs(backupDir, 0700) if checkSigs: keys = thandy.util.getKeylist(os.path.join(repo, "meta/keys.txt")) else: keys = None n_ok = 0 for fn in args: print "Loading %s..."%fn try: content = snarf(fn) except OSError, e: print "Couldn't open %s: %s"%(fn, e) continue try: obj = json.loads(content) except ValueError, e: print "Couldn't decode %s: %s"%(fn, e) continue try: ss, r, path = thandy.formats.checkSignedObj(obj, keys) except thandy.FormatException, e: print "Bad format on %s: %s"%(fn, e) continue if checkSigs and not ss.isValid(): print "Not enough valid signatures on %s"%fn continue print " Looks okay. It goes in %s"%path assert path.startswith("/") targetPath = os.path.join(repo, path[1:]) if os.path.exists(targetPath): oldContents = snarf(targetPath) if oldContents == content: print " File unchanged!" n_ok += 1 continue baseFname = "%s_%s" % (tstamp(), os.path.split(path)[1]) backupFname = os.path.join(backupDir, baseFname) print " Copying old file to %s"%backupFname thandy.util.replaceFile(backupFname, oldContents) parentDir = os.path.split(targetPath)[0] if not os.path.exists(parentDir): print " Making %s"%parentDir os.makedirs(parentDir, 0755) print " Replacing file..." thandy.util.replaceFile(targetPath, content) os.chmod(targetPath, 0644) print " Done." n_ok += 1 if n_ok != len(args): sys.exit(1) def timestamp(args): repo = os.environ.get("THANDY_MASTER_REPO") ts_keyfile = thandy.util.userFilename("timestamp_key") options, args = getopt.getopt(args, "", ["repo=", "ts-key="]) for o,v in options: if o == "--repo": repo = v elif o == "--ts-key": ts_keyfile = v if repo == None: print "No repository specified." usage() if not os.path.exists(repo): print "No such repository as %r"%repo usage() tsFname = os.path.join(repo, "meta/timestamp.txt") try: mObj, mLen = snarfObj(os.path.join(repo, "meta/mirrors.txt")) except OSError: print "No mirror list!" sys.exit(1) try: kObj, kLen = snarfObj(os.path.join(repo, "meta/keys.txt")) except OSError: print "No key list!" sys.exit(1) bundles = [] for dirpath, dirname, fns in os.walk(os.path.join(repo, "bundleinfo")): for fn in fns: fn = os.path.join(dirpath, fn) try: bObj, bLen = snarfObj(fn) except (ValueError, OSError, IOError), e: print "(Couldn't read bundle-like %s: %s)"%(fn, e) continue try: _, r, _ = thandy.formats.checkSignedObj(bObj) except thandy.FormatException, e: print "Problem reading object from %s"%fn continue if r != "bundle": print "%s was not a good bundle"%fn continue bundles.append((bObj['signed'], bLen)) timestamp = thandy.formats.makeTimestampObj( mObj['signed'], mLen, kObj['signed'], kLen, bundles) signable = thandy.formats.makeSignable(timestamp) keydb = thandy.formats.Keylist() #XXXX Still a roundabout way to do this. keylist = thandy.formats.makeKeylistObj(ts_keyfile, True) keydb.addFromKeylist(keylist) for k in keydb.iterkeys(): thandy.formats.sign(signable, k) content = json.dumps(signable, sort_keys=True) thandy.util.replaceFile(tsFname, content) os.chmod(tsFname, 0644) def usage(): print "Known commands:" print " insert [--no-check] [--repo=repository] file ..." print " timestamp [--repo=repository]" sys.exit(1) def main(): if len(sys.argv) < 2: usage() cmd = sys.argv[1] args = sys.argv[2:] if cmd in [ "insert", "timestamp" ]: globals()[cmd](args) else: usage() if __name__ == '__main__': main()