| #!/usr/bin/env python |
| # |
| # |
| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License. |
| # |
| # |
| # Check MD5 and SHA1 signatures of files, using md5sums and/or |
| # sha1sums as manifests. Replaces the 'md5sum' and 'sha1sum' commands |
| # on systems that do not have them, such as Mac OS X or Windows. |
| # |
| # Usage: checksums.py [manifest] |
| # where "os.path.basename(manifest)" is either "md5sums" or "sha1sums" |
| # |
| # Tested with the following Python versions: |
| # 2.4 2.5 2.6 2.7 3.2 |
| |
| |
| import os |
| import shutil |
| import sys |
| |
| try: |
| from hashlib import md5 |
| from hashlib import sha1 |
| except ImportError: |
| from md5 import md5 |
| from sha import sha as sha1 |
| |
| |
| class Digester(object): |
| BUFFER_SIZE = 1024*1024 |
| |
| def __init__(self, factory): |
| self.factory = factory |
| self.digest_size = factory().digest_size |
| self.hashfunc = None |
| |
| def reset(self): |
| self.hashfunc = self.factory() |
| |
| def write(self, data): |
| return self.hashfunc.update(data) |
| |
| def hexdigest(self): |
| return self.hashfunc.hexdigest() |
| |
| |
| def main(manipath): |
| basedir, manifest = os.path.split(manipath) |
| |
| if manifest == 'md5sums': |
| sink = Digester(md5) |
| elif manifest == 'sha1sums': |
| sink = Digester(sha1) |
| else: |
| raise ValueError('The name of the digest manifest must be ' |
| "'md5sums' or 'sha1sums', not '%s'" % manifest) |
| |
| # No 'with' statement in Python 2.4 ... |
| stream = None |
| try: |
| stream = open(manipath, 'r') |
| for line in stream: |
| sink.reset() |
| parse_digest(basedir, line.rstrip(), sink) |
| finally: |
| if stream is not None: |
| stream.close() |
| |
| |
| def parse_digest(basedir, entry, sink): |
| length = 2 * sink.digest_size |
| expected = entry[:length].lower() |
| filename = entry[length + 2:] |
| |
| # Still no 'with' statement in Python 2.4 ... |
| source = None |
| try: |
| source = open(os.path.join(basedir, filename), 'rb') |
| shutil.copyfileobj(source, sink, sink.BUFFER_SIZE) |
| actual = sink.hexdigest().lower() |
| finally: |
| if source is not None: |
| source.close() |
| |
| if expected != actual: |
| raise ValueError('Mismatch: expected %s, actual %s: %s' |
| % (expected, actual, filename)) |
| print('ok: %s %s' % (actual, filename)) |
| |
| |
| if __name__ == '__main__': |
| main(sys.argv[1]) |