Back to index

enigmail  1.4.3
checksums.py
Go to the documentation of this file.
00001 #!/usr/bin/python
00002 # ***** BEGIN LICENSE BLOCK *****
00003 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004 #
00005 # The contents of this file are subject to the Mozilla Public License Version
00006 # 1.1 (the "License"); you may not use this file except in compliance with
00007 # the License. You may obtain a copy of the License at
00008 # http://www.mozilla.org/MPL/
00009 #
00010 # Software distributed under the License is distributed on an "AS IS" basis,
00011 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012 # for the specific language governing rights and limitations under the
00013 # License.
00014 #
00015 # The Original Code is a checksum generation utility.
00016 #
00017 # The Initial Developer of the Original Code is
00018 # Mozilla Foundation.
00019 # Portions created by the Initial Developer are Copyright (C) 2010
00020 # the Initial Developer. All Rights Reserved.
00021 #
00022 # Contributor(s):
00023 #    John Ford <jhford@mozilla.com>
00024 #
00025 # Alternatively, the contents of this file may be used under the terms of
00026 # either the GNU General Public License Version 2 or later (the "GPL"), or
00027 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028 # in which case the provisions of the GPL or the LGPL are applicable instead
00029 # of those above. If you wish to allow use of your version of this file only
00030 # under the terms of either the GPL or the LGPL, and not to allow others to
00031 # use your version of this file under the terms of the MPL, indicate your
00032 # decision by deleting the provisions above and replace them with the notice
00033 # and other provisions required by the GPL or the LGPL. If you do not delete
00034 # the provisions above, a recipient may use your version of this file under
00035 # the terms of any one of the MPL, the GPL or the LGPL.
00036 #
00037 # ***** END LICENSE BLOCK *****
00038 
00039 from __future__ import with_statement
00040 
00041 from optparse import OptionParser
00042 import logging
00043 import os
00044 try:
00045     import hashlib
00046 except:
00047     hashlib = None
00048 
00049 def digest_file(filename, digest, chunk_size=1024):
00050     '''Produce a checksum for the file specified by 'filename'.  'filename'
00051     is a string path to a file that is opened and read in this function.  The
00052     checksum algorithm is specified by 'digest' and is a valid OpenSSL
00053     algorithm.  If the digest used is not valid or Python's hashlib doesn't
00054     work, the None object will be returned instead.  The size of blocks
00055     that this function will read from the file object it opens based on
00056     'filename' can be specified by 'chunk_size', which defaults to 1K'''
00057     assert not os.path.isdir(filename), 'this function only works with files'
00058     logger = logging.getLogger('checksums.py')
00059     if hashlib is not None:
00060         logger.debug('Creating new %s object' % digest)
00061         h = hashlib.new(digest)
00062         with open(filename, 'rb') as f:
00063             while True:
00064                 data = f.read(chunk_size)
00065                 if not data:
00066                     logger.debug('Finished reading in file')
00067                     break
00068                 h.update(data)
00069         hash = h.hexdigest()
00070         logger.debug('Hash for %s is %s' % (filename, hash))
00071         return hash
00072     else:
00073         # In this case we could subprocess.Popen and .communicate with
00074         # sha1sum or md5sum
00075         logger.warn('The python module for hashlib is missing!')
00076         return None
00077 
00078 
00079 def process_files(files, output_filename, digests, strip):
00080     '''This function takes a list of file names, 'files'.  It will then
00081     compute the checksum for each of the files by opening the files.
00082     Once each file is read and its checksum is computed, this function
00083     will write the information to the file specified by 'output_filename'.
00084     The path written in the output file will have anything specified by 'strip'
00085     removed from the path.  The output file is closed before returning nothing
00086     The algorithm to compute checksums with can be specified by 'digests' 
00087     and needs to be a list of valid OpenSSL algorithms.
00088 
00089     The output file is written in the format:
00090         <hash> <algorithm> <filesize> <filepath>
00091     Example:
00092         d1fa09a<snip>e4220 sha1 14250744 firefox-4.0b6pre.en-US.mac64.dmg
00093     '''
00094 
00095     logger = logging.getLogger('checksums.py')
00096     if os.path.exists(output_filename):
00097         logger.debug('Overwriting existing checksums file "%s"' %
00098                      output_filename)
00099     else:
00100         logger.debug('Creating a new checksums file "%s"' % output_filename)
00101     with open(output_filename, 'w+') as output:
00102         for file in files:
00103             if os.path.isdir(file):
00104                 logger.warn('%s is a directory, skipping' % file)
00105             else:
00106                 for digest in digests:
00107                     hash = digest_file(file, digest)
00108                     if hash is None:
00109                         logger.warn('Unable to generate a hash for %s. ' +
00110                                     'Skipping.' % file)
00111                         continue
00112                     if file.startswith(strip):
00113                         short_file = file[len(strip):]
00114                         short_file = short_file.lstrip('/')
00115                     else:
00116                         short_file = file
00117                     print >>output, '%s %s %s %s' % (hash, digest,
00118                                                      os.path.getsize(file),
00119                                                      short_file)
00120 
00121 def setup_logging(level=logging.DEBUG):
00122     '''This function sets up the logging module using a speficiable logging
00123     module logging level.  The default log level is DEBUG.
00124 
00125     The output is in the format:
00126         <level> - <message>
00127     Example:
00128         DEBUG - Finished reading in file
00129 '''
00130 
00131     logger = logging.getLogger('checksums.py')
00132     logger.setLevel(logging.DEBUG)
00133     handler = logging.StreamHandler()
00134     handler.setLevel(level)
00135     formatter = logging.Formatter("%(levelname)s - %(message)s")
00136     handler.setFormatter(formatter)
00137     logger.addHandler(handler)
00138 
00139 def main():
00140     '''This is a main function that parses arguments, sets up logging
00141     and generates a checksum file'''
00142     # Parse command line arguments
00143     parser = OptionParser()
00144     parser.add_option('-d', '--digest', help='checksum algorithm to use',
00145                       action='append', dest='digests')
00146     parser.add_option('-o', '--output', help='output file to use',
00147                       action='store', dest='outfile', default='checksums')
00148     parser.add_option('-v', '--verbose',
00149                       help='Be noisy (takes precedence over quiet)',
00150                       action='store_true', dest='verbose', default=False)
00151     parser.add_option('-q', '--quiet', help='Be quiet', action='store_true',
00152                       dest='quiet', default=False)
00153     parser.add_option('-s', '--strip',
00154                       help='strip this path from the filenames',
00155                       dest='strip', default=os.getcwd())
00156     options, args = parser.parse_args()
00157 
00158     #Figure out which logging level to use
00159     if options.verbose:
00160         loglevel = logging.DEBUG
00161     elif options.quiet:
00162         loglevel = logging.ERROR
00163     else:
00164         loglevel = logging.INFO
00165 
00166     #Set up logging
00167     setup_logging(loglevel)
00168     logger = logging.getLogger('checksums.py')
00169 
00170     # Validate the digest type to use
00171     if not options.digests:
00172         options.digests = ['sha1']
00173     try:
00174         for digest in options.digests:
00175             hashlib.new(digest)
00176     except ValueError, ve:
00177         logger.error('Could not create a "%s" hash object (%s)' %
00178                      (digest, ve.args[0]))
00179         exit(1)
00180 
00181     # Validate the files to checksum
00182     files = []
00183     for i in args:
00184         if os.path.exists(i):
00185             files.append(i)
00186         else:
00187             logger.info('File "%s" was not found on the filesystem' % i)
00188     process_files(files, options.outfile, options.digests, options.strip)
00189 
00190 if __name__ == '__main__':
00191     main()