]> git.decadent.org.uk Git - dak.git/blobdiff - katie
Map testing to unstable
[dak.git] / katie
diff --git a/katie b/katie
index f8053fc9f655c7220d0abb9ed5cac877a8563e5b..8b820902d144c7123cc82660b1dc41f8078f35fa 100755 (executable)
--- a/katie
+++ b/katie
@@ -2,7 +2,7 @@
 
 # Installs Debian packaes
 # Copyright (C) 2000  James Troup <james@nocrew.org>
-# $Id: katie,v 1.6 2000-11-30 04:38:34 troup Exp $
+# $Id: katie,v 1.16 2000-12-20 08:25:56 troup Exp $
 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -116,23 +116,26 @@ def check_signature (filename):
 
 #####################################################################################################################
 
-def read_override_file (filename, suite, component):
+def read_override_file (filename, suite, component, binary_type):
     global overrides;
-    
+
     file = utils.open_file(filename, 'r');
     for line in file.readlines():
         line = string.strip(utils.re_comments.sub('', line))
         override_package = re_override_package.sub(r'\1', line)
         if override_package != "":
-            overrides[suite][component][override_package] = 1
+            overrides[suite][component][binary_type][override_package] = 1
     file.close()
 
 
 # See if a given package is in the override file.  Caches and only loads override files on demand.
 
-def in_override_p (package, component, suite):
+def in_override_p (package, component, suite, binary_type):
     global overrides;
 
+    if binary_type == "" or binary_type == "deb":
+        binary_type = "-";
+
     # Avoid <undef> on unknown distributions
     if db_access.get_suite_id(suite) == -1:
         return None;
@@ -140,20 +143,26 @@ def in_override_p (package, component, suite):
     # FIXME: nasty non-US speficic hack
     if string.lower(component[:7]) == "non-us/":
         component = component[7:];
-    if not overrides.has_key(suite) or not overrides[suite].has_key(component):
+    if not overrides.has_key(suite) or not overrides[suite].has_key(component) or not overrides[suite][component].has_key(binary_type):
         if not overrides.has_key(suite):
             overrides[suite] = {}
         if not overrides[suite].has_key(component):
             overrides[suite][component] = {}
+        if not overrides[suite][component].has_key(binary_type):
+            overrides[suite][component][binary_type] = {}
         if Cnf.has_key("Suite::%s::SingleOverrideFile" % (suite)): # legacy mixed suite (i.e. experimental)
             override_filename = Cnf["Dir::OverrideDir"] + 'override.' + Cnf["Suite::%s::OverrideCodeName" % (suite)];
-            read_override_file (override_filename, suite, component);
+            read_override_file (override_filename, suite, component, binary_type);
         else: # all others.
-            for src in ("", ".src"):
-                override_filename = Cnf["Dir::OverrideDir"] + 'override.' + Cnf["Suite::%s::OverrideCodeName" % (suite)] + '.' + component + src;
-                read_override_file (override_filename, suite, component);
+            if binary_type == "udeb":
+                override_filename = Cnf["Dir::OverrideDir"] + 'override.' + Cnf["Suite::%s::OverrideCodeName" % (suite)] + '.debian-installer.' + component;
+                read_override_file (override_filename, suite, component, binary_type);
+            else:
+                for src in ("", ".src"):
+                    override_filename = Cnf["Dir::OverrideDir"] + 'override.' + Cnf["Suite::%s::OverrideCodeName" % (suite)] + '.' + component + src;
+                    read_override_file (override_filename, suite, component, binary_type);
 
-    return overrides[suite][component].get(package, None);
+    return overrides[suite][component][binary_type].get(package, None);
 
 #####################################################################################################################
 
@@ -208,6 +217,11 @@ def check_changes(filename):
         del changes["distribution"]["frozen"]
         reject_message = reject_message + "Mapping frozen to unstable.\n"
 
+    # Map testing to unstable
+    if changes["distribution"].has_key("testing"):
+        del changes["distribution"]["testing"]
+        reject_message = reject_message + "Mapping testing to unstable.\n"
+
     # Ensure target distributions exist
     for i in changes["distribution"].keys():
         if not Cnf.has_key("Suite::%s" % (i)):
@@ -229,12 +243,19 @@ def check_changes(filename):
 
     # Handle uploads to stable
     if changes["distribution"].has_key("stable"):
-        # If running from within proposed-updates kill non-stable distributions
+        # If running from within proposed-updates; assume an install to stable
         if string.find(os.getcwd(), 'proposed-updates') != -1:
+            # FIXME: should probably remove anything that != stable
             for i in ("frozen", "unstable"):
-                if changes["distributions"].has_key(i):
+                if changes["distribution"].has_key(i):
                     reject_message = reject_message + "Removing %s from distribution list.\n"
                     del changes["distribution"][i]
+            changes["stable upload"] = 1;
+            # If we can't find a file from the .changes; assume it's a package already in the pool and move into the pool
+            file = files.keys()[0];
+            if os.access(file, os.R_OK) == 0:
+                pool_dir = Cnf["Dir::PoolDir"] + '/' + utils.poolify(changes["source"], files[file]["component"]);
+                os.chdir(pool_dir);
         # Otherwise (normal case) map stable to updates
         else:
             reject_message = reject_message + "Mapping stable to updates.\n";
@@ -358,7 +379,7 @@ def check_files():
                 continue
 
             # See if the package is NEW
-            if not in_override_p(files[file]["package"], files[file]["component"], suite):
+            if not in_override_p(files[file]["package"], files[file]["component"], suite, files[file].get("dbtype","")):
                 files[file]["new"] = 1
                 
             # Find any old binary packages
@@ -376,9 +397,10 @@ def check_files():
                             reject_message = reject_message + "Rejected"
                         reject_message = reject_message + ": %s Old version `%s' >= new version `%s'.\n" % (file, oldfile["version"], files[file]["version"])
                 # Check for existing copies of the file
-                q = projectB.query("SELECT b.id FROM binaries b, architecture a WHERE b.package = '%s' AND b.version = '%s' AND a.arch_string = '%s' AND a.id = b.architecture" % (files[file]["package"], files[file]["version"], files[file]["architecture"]))
-                if q.getresult() != []:
-                    reject_message = reject_message + "Rejected: can not overwrite existing copy of '%s' already in the archive.\n" % (file)
+                if not changes.has_key("stable upload"):
+                    q = projectB.query("SELECT b.id FROM binaries b, architecture a WHERE b.package = '%s' AND b.version = '%s' AND a.arch_string = '%s' AND a.id = b.architecture" % (files[file]["package"], files[file]["version"], files[file]["architecture"]))
+                    if q.getresult() != []:
+                        reject_message = reject_message + "Rejected: can not overwrite existing copy of '%s' already in the archive.\n" % (file)
 
             # Find any old .dsc files
             elif files[file]["type"] == "dsc":
@@ -398,7 +420,9 @@ def check_files():
             # Check the md5sum & size against existing files (if any)
             location = Cnf["Dir::PoolDir"];
             files[file]["location id"] = db_access.get_location_id (location, component, archive);
-            files_id = db_access.get_files_id(component + '/' + file, files[file]["size"], files[file]["md5sum"], files[file]["location id"]);
+            
+            files[file]["pool name"] = utils.poolify (changes["source"], files[file]["component"]);
+            files_id = db_access.get_files_id(files[file]["pool name"] + file, files[file]["size"], files[file]["md5sum"], files[file]["location id"]);
             if files_id == -1:
                 reject_message = reject_message + "Rejected: INTERNAL ERROR, get_files_id() returned multiple matches for %s.\n" % (file)
             elif files_id == -2:
@@ -419,7 +443,7 @@ def check_files():
 
 def check_dsc ():
     global dsc, dsc_files, reject_message, reprocess, orig_tar_id;
-    
+
     for file in files.keys():
         if files[file]["type"] == "dsc":
             try:
@@ -441,43 +465,51 @@ def check_dsc ():
             # locations of an .orig.tar.gz.
             for dsc_file in dsc_files.keys():
                 if files.has_key(dsc_file):
-                    actual_md5 = files[dsc_file]["md5sum"]
+                    actual_md5 = files[dsc_file]["md5sum"];
+                    actual_size = int(files[dsc_file]["size"]);
                     found = "%s in incoming" % (dsc_file)
                     # Check the file does not already exist in the archive
-                    q = projectB.query("SELECT f.id FROM files f, location l WHERE f.filename ~ '/%s' AND l.id = f.location" % (dsc_file));
-                    if q.getresult() != []:
-                        reject_message = reject_message + "Rejected: can not overwrite existing copy of '%s' already in the archive.\n" % (dsc_file)
+                    if not changes.has_key("stable upload"):
+                        q = projectB.query("SELECT f.id FROM files f, location l WHERE f.filename ~ '/%s' AND l.id = f.location" % (utils.regex_safe(dsc_file)));
+                        if q.getresult() != []:
+                            reject_message = reject_message + "Rejected: can not overwrite existing copy of '%s' already in the archive.\n" % (dsc_file)
                 elif dsc_file[-12:] == ".orig.tar.gz":
-                    # Check in Incoming
-                    # See comment above process_it() for explanation...
-                    if os.access(dsc_file, os.R_OK) != 0:
-                        files[dsc_file] = {};
-                        files[dsc_file]["size"] = os.stat(dsc_file)[stat.ST_SIZE];
-                        files[dsc_file]["md5sum"] = dsc_files[dsc_file]["md5sum"];
-                        files[dsc_file]["section"] = files[file]["section"];
-                        files[dsc_file]["priority"] = files[file]["priority"];
-                        files[dsc_file]["component"] = files[file]["component"];
-                        reprocess = 1;
-                        return 1;
                     # Check in the pool
-                    q = projectB.query("SELECT l.path, f.filename, l.type, f.id FROM files f, location l WHERE f.filename ~ '/%s' AND l.id = f.location" % (dsc_file));
+                    q = projectB.query("SELECT l.path, f.filename, l.type, f.id FROM files f, location l WHERE f.filename ~ '/%s' AND l.id = f.location" % (utils.regex_safe(dsc_file)));
                     ql = q.getresult();
                     if len(ql) > 0:
                         old_file = ql[0][0] + ql[0][1];
                         actual_md5 = apt_pkg.md5sum(utils.open_file(old_file,"r"));
+                        actual_size = os.stat(old_file)[stat.ST_SIZE];
                         found = old_file;
                         suite_type = ql[0][2];
+                        dsc_files[dsc_file]["files id"] = ql[0][3]; # need this for updating dsc_files in install()
                         # See install()...
                         if suite_type == "legacy" or suite_type == "legacy-mixed":
                             orig_tar_id = ql[0][3];
                     else:
-                        reject_message = reject_message + "Rejected: %s refers to %s, but I can't find it in Incoming or in the pool.\n" % (file, dsc_file);
-                        continue;
+                        # Not there? Check in Incoming...
+                        # [See comment above process_it() for explanation
+                        #  of why this is necessary...]
+                        if os.access(dsc_file, os.R_OK) != 0:
+                            files[dsc_file] = {};
+                            files[dsc_file]["size"] = os.stat(dsc_file)[stat.ST_SIZE];
+                            files[dsc_file]["md5sum"] = dsc_files[dsc_file]["md5sum"];
+                            files[dsc_file]["section"] = files[file]["section"];
+                            files[dsc_file]["priority"] = files[file]["priority"];
+                            files[dsc_file]["component"] = files[file]["component"];
+                            reprocess = 1;
+                            return 1;
+                        else:
+                            reject_message = reject_message + "Rejected: %s refers to %s, but I can't find it in Incoming or in the pool.\n" % (file, dsc_file);
+                            continue;
                 else:
                     reject_message = reject_message + "Rejected: %s refers to %s, but I can't find it in Incoming." % (file, dsc_file);
                     continue;
                 if actual_md5 != dsc_files[dsc_file]["md5sum"]:
-                    reject_message = reject_message + "Rejected: md5sum for %s doesn't match %s.\n" % (found, file)
+                    reject_message = reject_message + "Rejected: md5sum for %s doesn't match %s.\n" % (found, file);
+                if actual_size != int(dsc_files[dsc_file]["size"]):
+                    reject_message = reject_message + "Rejected: size for %s doesn't match %s.\n" % (found, file);
 
     if string.find(reject_message, "Rejected:") != -1:
         return 0
@@ -594,6 +626,11 @@ def action (changes_filename):
 
 def install (changes_filename, summary, short_summary):
     global install_count, install_bytes
+
+    # Stable uploads are a special case
+    if changes.has_key("stable upload"):
+        stable_install (changes_filename, summary, short_summary);
+        return;
     
     print "Installing."
 
@@ -614,7 +651,6 @@ def install (changes_filename, summary, short_summary):
             dsc_location_id = files[file]["location id"];
             if not files[file]["files id"]:
                 files[file]["files id"] = db_access.set_files_id (filename, files[file]["size"], files[file]["md5sum"], dsc_location_id)
-            dsc_file_id = files[file]["files id"]
             projectB.query("INSERT INTO source (source, version, maintainer, file) VALUES ('%s', '%s', %d, %d)"
                            % (package, version, maintainer_id, files[file]["files id"]))
             
@@ -622,15 +658,20 @@ def install (changes_filename, summary, short_summary):
                 suite_id = db_access.get_suite_id(suite);
                 projectB.query("INSERT INTO src_associations (suite, source) VALUES (%d, currval('source_id_seq'))" % (suite_id))
 
-
-            # Add the .diff.gz and {.orig,}.tar.gz files to the DB (files and dsc_files)
-            for file in files.keys():
-                if files[file]["type"] == "diff.gz" or files[file]["type"] == "orig.tar.gz" or files[file]["type"] == "tar.gz":
-                    if not files[file]["files id"]:
-                        filename = files[file]["pool name"] + file;
-                        files[file]["files id"] = db_access.set_files_id (filename, files[file]["size"], files[file]["md5sum"], files[file]["location id"])
-                    projectB.query("INSERT INTO dsc_files (source, file) VALUES (currval('source_id_seq'), %d)" % (files[file]["files id"]));
-                
+            # Add the source files to the DB (files and dsc_files)
+            projectB.query("INSERT INTO dsc_files (source, file) VALUES (currval('source_id_seq'), %d)" % (files[file]["files id"]));
+            for dsc_file in dsc_files.keys():
+                filename = files[file]["pool name"] + dsc_file;
+                # If the .orig.tar.gz is already in the pool, it's
+                # files id is stored in dsc_files by check_dsc().
+                files_id = dsc_files[dsc_file].get("files id", None);
+                if files_id == None:
+                    files_id = db_access.get_files_id(filename, dsc_files[dsc_file]["size"], dsc_files[dsc_file]["md5sum"], dsc_location_id);
+                # FIXME: needs to check for -1/-2 and or handle exception
+                if files_id == None:
+                    files_id = db_access.set_files_id (filename, dsc_files[dsc_file]["size"], dsc_files[dsc_file]["md5sum"], dsc_location_id);
+                projectB.query("INSERT INTO dsc_files (source, file) VALUES (currval('source_id_seq'), %d)" % (files_id));
+            
     # Add the .deb files to the DB
     for file in files.keys():
         if files[file]["type"] == "deb":
@@ -665,6 +706,22 @@ def install (changes_filename, summary, short_summary):
                 suite_id = db_access.get_suite_id(suite);
                 projectB.query("INSERT INTO bin_associations (suite, bin) VALUES (%d, currval('binaries_id_seq'))" % (suite_id));
 
+    # If the .orig.tar.gz is in a legacy directory we need to poolify
+    # it, so that apt-get source (and anything else that goes by the
+    # "Directory:" field in the Sources.gz file) works.
+    if orig_tar_id != None:
+        q = projectB.query("SELECT DISTINCT ON (f.id) l.path, f.filename, f.id as files_id, df.source, df.id as dsc_files_id, f.size, f.md5sum FROM files f, dsc_files df, location l WHERE df.source IN (SELECT source FROM dsc_files WHERE file = %s) AND f.id = df.file AND l.id = f.location AND (l.type = 'legacy' OR l.type = 'legacy-mixed')" % (orig_tar_id));
+        qd = q.dictresult();
+        for qid in qd:
+            # First move the files to the new location
+            legacy_filename = qid["path"]+qid["filename"];
+            pool_location = utils.poolify (changes["source"], files[file]["component"]);
+            pool_filename = pool_location + os.path.basename(qid["filename"]);
+            destination = Cnf["Dir::PoolDir"] + pool_location
+            utils.move(legacy_filename, destination);
+            # Then Update the DB's files table
+            q = projectB.query("UPDATE files SET filename = '%s', location = '%s' WHERE id = '%s'" % (pool_filename, dsc_location_id, qid["files_id"]));
+
     # Install the files into the pool
     for file in files.keys():
         if files[file].has_key("byhand"):
@@ -679,37 +736,101 @@ def install (changes_filename, summary, short_summary):
         if Cnf.has_key("Suite::%s::CopyChanges" % (suite)):
             utils.copy (changes_filename, Cnf["Dir::RootDir"] + Cnf["Suite::%s::CopyChanges" % (suite)]);
 
-    # If the .orig.tar.gz is in a legacy directory we need to poolify
-    # it, so that apt-get source (and anything else that goes by the
-    # "Directory:" field in the Sources.gz file) works.
-    if orig_tar_id != None:
-        q = projectB.query("SELECT l.path, f.filename, f.id as files_id, df.source, df.id as dsc_files_id, f.size, f.md5sum FROM files f, dsc_files df, location l WHERE df.source IN (SELECT source FROM dsc_files WHERE file = %s) AND f.id = df.file AND l.id = f.location" % (orig_tar_id));
-        qd = q.dictresult();
-        for qid in qd:
-            # First move the files to the new location
-            legacy_filename = qid["path"]+qid["filename"];
-            pool_location = utils.poolify (files[file]["source"], files[file]["component"]);
-            pool_filename = pool_location + os.path.basename(qid["filename"]);
-            destination = Cnf["Dir::PoolDir"] + pool_location
-            utils.move(legacy_filename, destination);
-            # Update the DB: files table
-            new_files_id = db_access.set_files_id(pool_filename, qid["size"], qid["md5sum"], dsc_location_id);
-            # Update the DB: dsc_files table
-            projectB.query("INSERT INTO dsc_files (source, file) VALUES (%s, %s)" % (qid["source"], new_files_id));
-            # Update the DB: source table
-            if legacy_filename[-4:] == ".dsc":
-                projectB.query("UPDATE source SET file = %s WHERE id = %d" % (new_files_id, qid["source"]));
-
-        for qid in qd:
-            # Remove old data from the DB: dsc_files table
-            projectB.query("DELETE FROM dsc_files WHERE id = %s" % (qid["dsc_files_id"]));
-            # Remove old data from the DB: files table
-            projectB.query("DELETE FROM files WHERE id = %s" % (qid["files_id"]));
+    projectB.query("COMMIT WORK");
 
     utils.move (changes_filename, Cnf["Dir::IncomingDir"] + 'DONE/' + os.path.basename(changes_filename))
 
+    install_count = install_count + 1;
+
+    if not Cnf["Dinstall::Options::No-Mail"]:
+        mail_message = """Return-Path: %s
+From: %s
+To: %s
+Bcc: troup@auric.debian.org
+Subject: %s INSTALLED
+
+%s
+Installing:
+%s
+
+%s""" % (Cnf["Dinstall::MyEmailAddress"], Cnf["Dinstall::MyEmailAddress"], changes["maintainer822"], os.path.basename(changes_filename), reject_message, summary, installed_footer)
+        utils.send_mail (mail_message, "")
+        announce (short_summary, 1)
+
+#####################################################################################################################
+
+def stable_install (changes_filename, summary, short_summary):
+    global install_count, install_bytes
+    
+    print "Installing to stable."
+
+    archive = utils.where_am_i();
+
+    # Begin a transaction; if we bomb out anywhere between here and the COMMIT WORK below, the DB will not be changed.
+    projectB.query("BEGIN WORK");
+
+    # Add the .dsc file to the DB
+    for file in files.keys():
+        if files[file]["type"] == "dsc":
+            package = dsc["source"]
+            version = dsc["version"]  # NB: not files[file]["version"], that has no epoch
+            q = projectB.query("SELECT id FROM source WHERE source = '%s' AND version = '%s'" % (package, version))
+            ql = q.getresult()
+            if ql == []:
+                sys.stderr.write("INTERNAL ERROR: couldn't find '%s' (%s) in source table.\n" % (package, version));
+                sys.exit(1);
+            source_id = ql[0][0];
+            suite_id = db_access.get_suite_id('proposed-updates');
+            projectB.query("DELETE FROM src_associations WHERE suite = '%s' AND source = '%s'" % (suite_id, source_id));
+            suite_id = db_access.get_suite_id('stable');
+            projectB.query("INSERT INTO src_associations (suite, source) VALUES ('%s', '%s')" % (suite_id, source_id));
+                
+    # Add the .deb files to the DB
+    for file in files.keys():
+        if files[file]["type"] == "deb":
+            package = files[file]["package"]
+            version = files[file]["version"]
+            architecture = files[file]["architecture"]
+            q = projectB.query("SELECT b.id FROM binaries b, architecture a WHERE b.package = '%s' AND b.version = '%s' AND (a.arch_string = '%s' OR a.arch_string = 'all') AND b.architecture = a.id" % (package, version, architecture))
+            ql = q.getresult()
+            if ql == []:
+                sys.stderr.write("INTERNAL ERROR: couldn't find '%s' (%s for %s architecture) in binaries table.\n" % (package, version, architecture));
+                sys.exit(1);
+            binary_id = ql[0][0];
+            suite_id = db_access.get_suite_id('proposed-updates');
+            projectB.query("DELETE FROM bin_associations WHERE suite = '%s' AND bin = '%s'" % (suite_id, binary_id));
+            suite_id = db_access.get_suite_id('stable');
+            projectB.query("INSERT INTO bin_associations (suite, bin) VALUES ('%s', '%s')" % (suite_id, binary_id));
+
     projectB.query("COMMIT WORK");
 
+    utils.move (changes_filename, Cnf["Rhona::Morgue"] + os.path.basename(changes_filename));
+
+    # Update the Stable ChangeLog file
+
+    new_changelog_filename = Cnf["Dir::RootDir"] + Cnf["Suite::Stable::ChangeLogBase"] + ".ChangeLog";
+    changelog_filename = Cnf["Dir::RootDir"] + Cnf["Suite::Stable::ChangeLogBase"] + "ChangeLog";
+    if os.path.exists(new_changelog_filename):
+        os.unlink (new_changelog_filename);
+    
+    new_changelog = utils.open_file(new_changelog_filename, 'w');
+    for file in files.keys():
+        if files[file]["type"] == "deb":
+            new_changelog.write("stable/%s/binary-%s/%s\n" % (files[file]["component"], files[file]["architecture"], file));
+        elif re_issource.match(file) != None:
+            new_changelog.write("stable/%s/source/%s\n" % (files[file]["component"], file));
+        else:
+            new_changelog.write("%s\n" % (file));
+    chop_changes = re_fdnic.sub("\n", changes["changes"]);
+    new_changelog.write(chop_changes + '\n\n');
+    if os.access(changelog_filename, os.R_OK) != 0:
+        changelog = utils.open_file(changelog_filename, 'r');
+        new_changelog.write(changelog.read());
+    new_changelog.close();
+    if os.access(changelog_filename, os.R_OK) != 0:
+        os.unlink(changelog_filename);
+    utils.move(new_changelog_filename, changelog_filename);
+
     install_count = install_count + 1;
 
     if not Cnf["Dinstall::Options::No-Mail"]:
@@ -717,13 +838,13 @@ def install (changes_filename, summary, short_summary):
 From: %s
 To: %s
 Bcc: troup@auric.debian.org
-Subject: %s INSTALLED
+Subject: %s INSTALLED into stable
 
 %s
 Installing:
 %s
 
-%s""" % (Cnf["Dinstall::MyEmailAddress"], Cnf["Dinstall::MyEmailAddress"], changes["maintainer822"], changes_filename, reject_message, summary, installed_footer)
+%s""" % (Cnf["Dinstall::MyEmailAddress"], Cnf["Dinstall::MyEmailAddress"], changes["maintainer822"], os.path.basename(changes_filename), reject_message, summary, installed_footer)
         utils.send_mail (mail_message, "")
         announce (short_summary, 1)
 
@@ -736,11 +857,19 @@ def reject (changes_filename, manual_reject_mail_filename):
     reason_filename = re_changes.sub("reason", base_changes_filename);
     reject_filename = "%s/REJECT/%s" % (Cnf["Dir::IncomingDir"], reason_filename);
 
-    # Move the .changes files and it's contents into REJECT/
-    utils.move (changes_filename, "%s/REJECT/%s" % (Cnf["Dir::IncomingDir"], base_changes_filename));
+    # Move the .changes files and it's contents into REJECT/ (if we can; errors are ignored)
+    try:
+        utils.move (changes_filename, "%s/REJECT/%s" % (Cnf["Dir::IncomingDir"], base_changes_filename));
+    except utils.cant_overwrite_exc:
+        sys.stderr.write("W: couldn't overwrite existing file '%s/REJECT/%s" % (Cnf["Dir::IncomingDir"], base_changes_filename));
+        pass;
     for file in files.keys():
-        if os.access(file,os.R_OK) == 0:
-            utils.move (file, "%s/REJECT/%s" % (Cnf["Dir::IncomingDir"], file));
+        if os.path.exists(file):
+            try:
+                utils.move (file, "%s/REJECT/%s" % (Cnf["Dir::IncomingDir"], file));
+            except utils.cant_overwrite_exc:
+                sys.stderr.write("W: couldn't overwrite existing file '%s/REJECT/%s" % (Cnf["Dir::IncomingDir"], file));
+                pass;
 
     # If this is not a manual rejection generate the .reason file and rejection mail message
     if manual_reject_mail_filename == "":
@@ -756,7 +885,7 @@ Subject: %s REJECTED
 
 %s
 ===
-%s""" % (Cnf["Dinstall::MyEmailAddress"], changes["maintainer822"], changes_filename, reject_message, reject_footer);
+%s""" % (Cnf["Dinstall::MyEmailAddress"], changes["maintainer822"], os.path.basename(changes_filename), reject_message, reject_footer);
     else: # Have a manual rejection file to use
         reject_mail_message = ""; # avoid <undef>'s
         
@@ -781,7 +910,7 @@ Subject: %s REJECTED
 %s
 %s
 ===
-%s""" % (user_email_address, Cnf["Dinstall::MyEmailAddress"], changes["maintainer822"], changes_filename, manual_reject_message, reject_message, reject_footer)
+%s""" % (user_email_address, Cnf["Dinstall::MyEmailAddress"], changes["maintainer822"], os.path.basename(changes_filename), manual_reject_message, reject_message, reject_footer)
     
     # Write the rejection email out as the <foo>.reason file
     reason_filename = re_changes.sub("reason", os.path.basename(changes_filename));
@@ -807,6 +936,8 @@ Subject: %s REJECTED
 def acknowledge_new (changes_filename, summary):
     global new_ack_new;
 
+    changes_filename = os.path.basename(changes_filename);
+
     new_ack_new[changes_filename] = 1;
 
     if new_ack_old.has_key(changes_filename):
@@ -939,11 +1070,25 @@ non-maintainer upload.  The .changes file follows.
 # into the .changes structure and reprocess the .changes file.
 
 def process_it (changes_file):
-    global reprocess, orig_tar_id;
+    global reprocess, orig_tar_id, changes, dsc, dsc_files, files;
 
     reprocess = 1;
     orig_tar_id = None;
+    # Reset some globals
+    changes = {};
+    dsc = {};
+    dsc_files = {};
+    files = {};
+    orig_tar_id = None;
 
+    # Absolutize the filename to avoid the requirement of being in the
+    # same directory as the .changes file.
+    changes_file = os.path.abspath(changes_file);
+
+    # And since handling of installs to stable munges with the CWD;
+    # save and restore it.
+    cwd = os.getcwd();
+    
     check_signature (changes_file);
     check_changes (changes_file);
     while reprocess:
@@ -954,6 +1099,9 @@ def process_it (changes_file):
         
     action(changes_file);
 
+    # Restore CWD
+    os.chdir(cwd);
+
 ###############################################################################
 
 def main():
@@ -1022,18 +1170,11 @@ def main():
         print "\n" + changes_file;
         process_it (changes_file);
 
-    install_mag = " b";
-    if install_bytes > 10000:
-        install_bytes = install_bytes / 1000;
-        install_mag = " Kb";
-    if install_bytes > 10000:
-        install_bytes = install_bytes / 1000;
-        install_mag = " Mb";
     if install_count:
         sets = "set"
         if install_count > 1:
             sets = "sets"
-        sys.stderr.write("Installed %d package %s, %d%s.\n" % (install_count, sets, int(install_bytes), install_mag))
+        sys.stderr.write("Installed %d package %s, %s.\n" % (install_count, sets, utils.size_type(int(install_bytes))));
 
     # Write out the list of already-acknowledged NEW packages
     if Cnf["Dinstall::Options::Ack-New"]: