################################################################################
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 *
################################################################################
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 = {}
-################################################################################
-
-class Error(Exception):
- """Base class for exceptions in this module."""
- pass
-
-class ParseMaintError(Error):
- """Exception raised for errors in parsing a maintainer field.
-
- Attributes:
- message -- explanation of the error
- """
-
- def __init__(self, message):
- self.args = message,
- self.message = message
+# (hashname, function, earliest_changes_version)
+known_hashes = [("sha1", apt_pkg.sha1sum, (1, 8)),
+ ("sha256", apt_pkg.sha256sum, (1, 8))]
################################################################################
try:
f = open(filename, mode)
except IOError:
- raise cant_open_exc, filename
+ raise CantOpenError, filename
return f
################################################################################
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...
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:
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
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)
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"):
# 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:
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"
else:
(md5, size, name) = s
except ValueError:
- raise changes_parse_error_exc, i
+ raise ParseChangesError, i
if section == "":
section = "-"
# 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:
# 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)
dest = orig_dest + '.' + repr(extra)
extra += 1
if extra >= too_many:
- raise tried_too_hard_exc
+ raise NoFreeFilenameError
return dest
################################################################################