X-Git-Url: https://git.decadent.org.uk/gitweb/?a=blobdiff_plain;f=daklib%2Futils.py;h=75845244270fc04e554675f831100a9c7b0123e8;hb=f612e250a402ae5e6a86f711d84c6b641a3d2b22;hp=d05719fb458d9f75f367ec135e153077a80157f2;hpb=f86fb53a967d9fa0e92f3534b9a44a5ba0e6adb0;p=dak.git diff --git a/daklib/utils.py b/daklib/utils.py index d05719fb..75845244 100755 --- a/daklib/utils.py +++ b/daklib/utils.py @@ -22,7 +22,7 @@ ################################################################################ import codecs, commands, email.Header, os, pwd, re, select, socket, shutil, \ - sys, tempfile, traceback + sys, tempfile, traceback, stat import apt_pkg import database from dak_exceptions import * @@ -49,30 +49,23 @@ re_verwithext = re.compile(r"^(\d+)(?:\.(\d+))(?:\s+\((\S+)\))?$") re_srchasver = re.compile(r"^(\S+)\s+\((\S+)\)$") -changes_parse_error_exc = "Can't parse line in .changes file" -invalid_dsc_format_exc = "Invalid .dsc file" -nk_format_exc = "Unknown Format: in .changes file" -no_files_exc = "No Files: field in .dsc or .changes file." -cant_open_exc = "Can't open file" -unknown_hostname_exc = "Unknown hostname" -cant_overwrite_exc = "Permission denied; can't overwrite existent file." -file_exists_exc = "Destination file exists" -sendmail_failed_exc = "Sendmail invocation failed" -tried_too_hard_exc = "Tried too hard to find a free filename." - default_config = "/etc/dak/dak.conf" default_apt_config = "/etc/dak/apt.conf" alias_cache = None key_uid_email_cache = {} +# (hashname, function, earliest_changes_version) +known_hashes = [("sha1", apt_pkg.sha1sum, (1, 8)), + ("sha256", apt_pkg.sha256sum, (1, 8))] + ################################################################################ def open_file(filename, mode='r'): try: f = open(filename, mode) except IOError: - raise cant_open_exc, filename + raise CantOpenError, filename return f ################################################################################ @@ -135,7 +128,7 @@ The rules for (signing_rules == 1)-mode are: lines = changes_in.readlines() if not lines: - raise changes_parse_error_exc, "[Empty changes file]" + raise ParseChangesError, "[Empty changes file]" # Reindex by line number so we can easily verify the format of # .dsc files... @@ -157,10 +150,10 @@ The rules for (signing_rules == 1)-mode are: if signing_rules == 1: index += 1 if index > num_of_lines: - raise invalid_dsc_format_exc, index + raise InvalidDscError, index line = indexed_lines[index] if not line.startswith("-----BEGIN PGP SIGNATURE"): - raise invalid_dsc_format_exc, index + raise InvalidDscError, index inside_signature = 0 break else: @@ -189,7 +182,7 @@ The rules for (signing_rules == 1)-mode are: mlf = re_multi_line_field.match(line) if mlf: if first == -1: - raise changes_parse_error_exc, "'%s'\n [Multi-line field continuing on from nothing?]" % (line) + raise ParseChangesError, "'%s'\n [Multi-line field continuing on from nothing?]" % (line) if first == 1 and changes[field] != "": changes[field] += '\n' first = 0 @@ -198,7 +191,7 @@ The rules for (signing_rules == 1)-mode are: error += line if signing_rules == 1 and inside_signature: - raise invalid_dsc_format_exc, index + raise InvalidDscError, index changes_in.close() changes["filecontents"] = "".join(lines) @@ -212,12 +205,122 @@ The rules for (signing_rules == 1)-mode are: changes["source-version"] = srcver.group(2) if error: - raise changes_parse_error_exc, error + raise ParseChangesError, error return changes ################################################################################ +def create_hash (lfiles, key, testfn, basedict = None): + rejmsg = [] + for f in lfiles.keys(): + try: + file_handle = open_file(f) + except CantOpenError: + rejmsg.append("Could not open file %s for checksumming" % (f)) + + # Check hash + if basedict and basedict.has_key(f): + basedict[f]['%ssum' % key] = testfn(file_handle) + file_handle.close() + + return rejmsg + +################################################################################ + +def check_hash (where, lfiles, key, testfn, basedict = None): + rejmsg = [] + if basedict: + for f in basedict.keys(): + if f not in lfiles: + rejmsg.append("%s: no %s checksum" % (f, key)) + + for f in lfiles.keys(): + if basedict and f not in basedict: + rejmsg.append("%s: extraneous entry in %s checksums" % (f, key)) + + try: + file_handle = open_file(f) + except CantOpenError: + continue + + # Check hash + if testfn(file_handle) != lfiles[f][key]: + rejmsg.append("%s: %s check failed." % (f, key)) + file_handle.close() + # Store the hashes for later use + if basedict: + basedict[f]['%ssum' % key] = lfiles[f][key] + # Check size + actual_size = os.stat(f)[stat.ST_SIZE] + size = int(lfiles[f]["size"]) + if size != actual_size: + rejmsg.append("%s: actual file size (%s) does not match size (%s) in %s" + % (f, actual_size, size, where)) + + return rejmsg + +################################################################################ + +def ensure_hashes(changes, dsc, files, dsc_files): + # Make sure we recognise the format of the Files: field + format = changes.get("format", "0.0").split(".",1) + if len(format) == 2: + format = int(format[0]), int(format[1]) + else: + format = int(float(format[0])), 0 + + rejmsg = [] + for x in changes: + if x.startswith("checksum-"): + h = x.split("-",1)[1] + if h not in dict(known_hashes): + rejmsg.append("Unsupported checksum field in .changes" % (h)) + + for x in dsc: + if x.startswith("checksum-"): + h = x.split("-",1)[1] + if h not in dict(known_hashes): + rejmsg.append("Unsupported checksum field in .dsc" % (h)) + + # We have to calculate the hash if we have an earlier changes version than + # the hash appears in rather than require it exist in the changes file + # I hate backwards compatibility + for h,f,v in known_hashes: + try: + if format < v: + for m in create_hash(files, h, f, files): + rejmsg.append(m) + else: + for m in check_hash(".changes %s" % (h), files, h, f, files): + rejmsg.append(m) + except NoFilesFieldError: + rejmsg.append("No Checksums-%s: field in .changes" % (h)) + except UnknownFormatError, format: + rejmsg.append("%s: unknown format of .changes" % (format)) + except ParseChangesError, line: + rejmsg.append("parse error for Checksums-%s in .changes, can't grok: %s." % (h, line)) + + if "source" not in changes["architecture"]: continue + + try: + if format < v: + for m in create_hash(dsc_files, h, f, dsc_files): + rejmsg.append(m) + else: + for m in check_hash(".dsc %s" % (h), dsc_files, h, f, dsc_files): + rejmsg.append(m) + except UnknownFormatError, format: + rejmsg.append("%s: unknown format of .dsc" % (format)) + except NoFilesFieldError: + rejmsg.append("No Checksums-%s: field in .dsc" % (h)) + except ParseChangesError, line: + rejmsg.append("parse error for Checksums-%s in .dsc, can't grok: %s." % (h, line)) + + return rejmsg + +################################################################################ + # Dropped support for 1.4 and ``buggy dchanges 3.4'' (?!) compared to di.pl def build_file_list(changes, is_a_dsc=0, field="files", hashname="md5sum"): @@ -225,12 +328,12 @@ def build_file_list(changes, is_a_dsc=0, field="files", hashname="md5sum"): # Make sure we have a Files: field to parse... if not changes.has_key(field): - raise no_files_exc + raise NoFilesFieldError # Make sure we recognise the format of the Files: field format = re_verwithext.search(changes.get("format", "0.0")) if not format: - raise nk_format_exc, "%s" % (changes.get("format","0.0")) + raise UnknownFormatError, "%s" % (changes.get("format","0.0")) format = format.groups() if format[1] == None: @@ -242,12 +345,12 @@ def build_file_list(changes, is_a_dsc=0, field="files", hashname="md5sum"): if is_a_dsc: if format != (1,0): - raise nk_format_exc, "%s" % (changes.get("format","0.0")) + raise UnknownFormatError, "%s" % (changes.get("format","0.0")) else: if (format < (1,5) or format > (1,8)): - raise nk_format_exc, "%s" % (changes.get("format","0.0")) + raise UnknownFormatError, "%s" % (changes.get("format","0.0")) if field != "files" and format < (1,8): - raise nk_format_exc, "%s" % (changes.get("format","0.0")) + raise UnknownFormatError, "%s" % (changes.get("format","0.0")) includes_section = (not is_a_dsc) and field == "files" @@ -263,7 +366,7 @@ def build_file_list(changes, is_a_dsc=0, field="files", hashname="md5sum"): else: (md5, size, name) = s except ValueError: - raise changes_parse_error_exc, i + raise ParseChangesError, i if section == "": section = "-" @@ -371,7 +474,7 @@ def send_mail (message, filename=""): # Invoke sendmail (result, output) = commands.getstatusoutput("%s < %s" % (Cnf["Dinstall::SendmailCommand"], filename)) if (result != 0): - raise sendmail_failed_exc, output + raise SendmailFailedError, output # Clean up any temporary files if message: @@ -427,10 +530,10 @@ def copy (src, dest, overwrite = 0, perms = 0664): # Don't overwrite unless forced to if os.path.exists(dest): if not overwrite: - raise file_exists_exc + raise FileExistsError else: if not os.access(dest, os.W_OK): - raise cant_overwrite_exc + raise CantOverwriteError shutil.copy2(src, dest) os.chmod(dest, perms) @@ -574,7 +677,7 @@ def find_next_free (dest, too_many=100): dest = orig_dest + '.' + repr(extra) extra += 1 if extra >= too_many: - raise tried_too_hard_exc + raise NoFreeFilenameError return dest ################################################################################