summaryrefslogtreecommitdiff
path: root/lib/thandy/ServerCLI.py
blob: 4b6c1a6c60d04cd43288739322da3095bab545d2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# 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()