queue.py 49.4 KB
Newer Older
J
New.  
James Troup 已提交
1
#!/usr/bin/env python
2
# vim:set et sw=4:
J
New.  
James Troup 已提交
3

J
Joerg Jaspert 已提交
4 5 6 7 8 9 10 11
"""
Queue utility functions for dak

@contact: Debian FTP Master <ftpmaster@debian.org>
@copyright: 2001 - 2006 James Troup <james@nocrew.org>
@copyright: 2009  Joerg Jaspert <joerg@debian.org>
@license: GNU General Public License version 2 or later
"""
J
New.  
James Troup 已提交
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

# 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
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

###############################################################################

J
Joerg Jaspert 已提交
29 30 31 32 33 34 35 36 37 38
import cPickle
import errno
import os
import pg
import stat
import sys
import time
import apt_inst
import apt_pkg
import utils
39
from types import *
40

J
Joerg Jaspert 已提交
41
from dak_exceptions import *
42
from changes import *
43
from regexes import re_default_answer, re_fdnic, re_bin_only_nmu, re_strip_srcver, re_valid_pkg_name, re_isanum, re_no_epoch, re_no_revision
44
from config import Config
45
from dbconn import *
46
from summarystats import SummaryStats
47 48
from utils import parse_changes
from textutils import fix_maintainer
J
New.  
James Troup 已提交
49 50

###############################################################################
51

52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
def get_type(f, session=None):
    """
    Get the file type of C{f}

    @type f: dict
    @param f: file entry from Changes object

    @rtype: string
    @return: filetype

    """
    if session is None:
        session = DBConn().session()

    # Determine the type
    if f.has_key("dbtype"):
        file_type = file["dbtype"]
    elif f["type"] in [ "orig.tar.gz", "orig.tar.bz2", "tar.gz", "tar.bz2", "diff.gz", "diff.bz2", "dsc" ]:
        file_type = "dsc"
    else:
        utils.fubar("invalid type (%s) for new.  Dazed, confused and sure as heck not continuing." % (file_type))

    # Validate the override type
    type_id = get_override_type(file_type, session)
    if type_id is None:
        utils.fubar("invalid type (%s) for new.  Say wha?" % (file_type))

    return file_type

################################################################################

83 84
# Determine what parts in a .changes are NEW

85
def determine_new(changes, files, warn=1):
J
Joerg Jaspert 已提交
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
    """
    Determine what parts in a C{changes} file are NEW.

    @type changes: Upload.Pkg.changes dict
    @param changes: Changes dictionary

    @type files: Upload.Pkg.files dict
    @param files: Files dictionary

    @type warn: bool
    @param warn: Warn if overrides are added for (old)stable

    @rtype: dict
    @return: dictionary of NEW components.

    """
102 103
    new = {}

104 105
    session = DBConn().session()

106
    # Build up a list of potentially new things
107
    for name, f in files.items():
108 109 110 111 112 113
        # Skip byhand elements
        if f["type"] == "byhand":
            continue
        pkg = f["package"]
        priority = f["priority"]
        section = f["section"]
J
Joerg Jaspert 已提交
114
        file_type = get_type(f)
115 116
        component = f["component"]

J
Joerg Jaspert 已提交
117
        if file_type == "dsc":
118
            priority = "source"
119

120 121 122 123
        if not new.has_key(pkg):
            new[pkg] = {}
            new[pkg]["priority"] = priority
            new[pkg]["section"] = section
J
Joerg Jaspert 已提交
124
            new[pkg]["type"] = file_type
125 126 127 128
            new[pkg]["component"] = component
            new[pkg]["files"] = []
        else:
            old_type = new[pkg]["type"]
J
Joerg Jaspert 已提交
129
            if old_type != file_type:
130 131 132 133
                # source gets trumped by deb or udeb
                if old_type == "dsc":
                    new[pkg]["priority"] = priority
                    new[pkg]["section"] = section
J
Joerg Jaspert 已提交
134
                    new[pkg]["type"] = file_type
135
                    new[pkg]["component"] = component
136 137 138

        new[pkg]["files"].append(name)

139 140 141 142 143
        if f.has_key("othercomponents"):
            new[pkg]["othercomponents"] = f["othercomponents"]

    for suite in changes["suite"].keys():
        for pkg in new.keys():
144 145
            ql = get_override(pkg, suite, new[pkg]["component"], new[pkg]["type"], session)
            if len(ql) > 0:
J
Joerg Jaspert 已提交
146 147 148
                for file_entry in new[pkg]["files"]:
                    if files[file_entry].has_key("new"):
                        del files[file_entry]["new"]
149 150 151
                del new[pkg]

    if warn:
152 153 154
        for s in ['stable', 'oldstable']:
            if changes["suite"].has_key(s):
                print "WARNING: overrides will be added for %s!" % s
155 156 157 158 159 160 161 162 163
        for pkg in new.keys():
            if new[pkg].has_key("othercomponents"):
                print "WARNING: %s already present in %s distribution." % (pkg, new[pkg]["othercomponents"])

    return new

################################################################################

def check_valid(new):
J
Joerg Jaspert 已提交
164 165 166 167 168 169 170 171 172 173 174
    """
    Check if section and priority for NEW packages exist in database.
    Additionally does sanity checks:
      - debian-installer packages have to be udeb (or source)
      - non debian-installer packages can not be udeb
      - source priority can only be assigned to dsc file types

    @type new: dict
    @param new: Dict of new packages with their section, priority and type.

    """
175
    for pkg in new.keys():
176 177
        section_name = new[pkg]["section"]
        priority_name = new[pkg]["priority"]
J
Joerg Jaspert 已提交
178
        file_type = new[pkg]["type"]
179 180 181 182 183 184 185 186 187 188 189 190 191

        section = get_section(section_name)
        if section is None:
            new[pkg]["section id"] = -1
        else:
            new[pkg]["section id"] = section.section_id

        priority = get_priority(priority_name)
        if priority is None:
            new[pkg]["priority id"] = -1
        else:
            new[pkg]["priority id"] = priority.priority_id

192
        # Sanity checks
193 194 195 196 197
        di = section_name.find("debian-installer") != -1

        # If d-i, we must be udeb and vice-versa
        if     (di and file_type not in ("udeb", "dsc")) or \
           (not di and file_type == "udeb"):
198
            new[pkg]["section id"] = -1
199 200

        # If dsc we need to be source and vice-versa
J
Joerg Jaspert 已提交
201 202
        if (priority == "source" and file_type != "dsc") or \
           (priority != "source" and file_type == "dsc"):
203 204
            new[pkg]["priority id"] = -1

J
New.  
James Troup 已提交
205 206
###############################################################################

207
class Upload(object):
J
Joerg Jaspert 已提交
208 209
    """
    Everything that has to do with an upload processed.
J
New.  
James Troup 已提交
210

J
Joerg Jaspert 已提交
211
    """
212 213 214
    def __init__(self):
        self.pkg = Changes()
        self.reset()
J
New.  
James Troup 已提交
215 216 217

    ###########################################################################

218 219
    def reset (self):
        """ Reset a number of internal variables."""
220

221
        # Initialize the substitution template map
222 223 224 225 226 227
        cnf = Config()
        self.Subst = {}
        self.Subst["__ADMIN_ADDRESS__"] = cnf["Dinstall::MyAdminAddress"]
        self.Subst["__BUG_SERVER__"] = cnf["Dinstall::BugServer"]
        self.Subst["__DISTRO__"] = cnf["Dinstall::MyDistribution"]
        self.Subst["__DAK_ADDRESS__"] = cnf["Dinstall::MyEmailAddress"]
228

229 230 231 232
        self.rejects = []
        self.warnings = []
        self.notes = []

233
        self.pkg.reset()
J
New.  
James Troup 已提交
234

235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
    def package_info(self):
        msg = ''

        if len(self.rejects) > 0:
            msg += "Reject Reasons:\n"
            msg += "\n".join(self.rejects)

        if len(self.warnings) > 0:
            msg += "Warnings:\n"
            msg += "\n".join(self.warnings)

        if len(self.notes) > 0:
            msg += "Notes:\n"
            msg += "\n".join(self.notes)

        return msg

J
New.  
James Troup 已提交
252
    ###########################################################################
253
    def update_subst(self):
J
Joerg Jaspert 已提交
254 255
        """ Set up the per-package template substitution mappings """

256 257
        cnf = Config()

258
        # If 'dak process-unchecked' crashed out in the right place, architecture may still be a string.
259 260 261 262
        if not self.pkg.changes.has_key("architecture") or not \
           isinstance(changes["architecture"], DictType):
            self.pkg.changes["architecture"] = { "Unknown" : "" }

263
        # and maintainer2047 may not exist.
264 265
        if not self.pkg.changes.has_key("maintainer2047"):
            self.pkg.changes["maintainer2047"] = cnf["Dinstall::MyEmailAddress"]
J
New.  
James Troup 已提交
266

267 268 269
        self.Subst["__ARCHITECTURE__"] = " ".join(self.pkg.changes["architecture"].keys())
        self.Subst["__CHANGES_FILENAME__"] = os.path.basename(self.pkg.changes_file)
        self.Subst["__FILE_CONTENTS__"] = self.pkg.changes.get("filecontents", "")
J
New.  
James Troup 已提交
270 271

        # For source uploads the Changed-By field wins; otherwise Maintainer wins.
272 273 274 275 276 277 278
        if self.pkg.changes["architecture"].has_key("source") and \
           self.pkg.changes["changedby822"] != "" and \
           (self.pkg.changes["changedby822"] != self.pkg.changes["maintainer822"]):

            self.Subst["__MAINTAINER_FROM__"] = self.pkg.changes["changedby2047"]
            self.Subst["__MAINTAINER_TO__"] = "%s, %s" % (self.pkg.changes["changedby2047"], changes["maintainer2047"])
            self.Subst["__MAINTAINER__"] = self.pkg.changes.get("changed-by", "Unknown")
J
New.  
James Troup 已提交
279
        else:
280 281 282
            self.Subst["__MAINTAINER_FROM__"] = self.pkg.changes["maintainer2047"]
            self.Subst["__MAINTAINER_TO__"] = self.pkg.changes["maintainer2047"]
            self.Subst["__MAINTAINER__"] = self.pkg.changes.get("maintainer", "Unknown")
J
Joerg Jaspert 已提交
283

284 285
        if "sponsoremail" in self.pkg.changes:
            self.Subst["__MAINTAINER_TO__"] += ", %s" % self.pkg.changes["sponsoremail"]
J
Joerg Jaspert 已提交
286

287 288
        if cnf.has_key("Dinstall::TrackingServer") and self.pkg.changes.has_key("source"):
            self.Subst["__MAINTAINER_TO__"] += "\nBcc: %s@%s" % (self.pkg.changes["source"], cnf["Dinstall::TrackingServer"])
J
New.  
James Troup 已提交
289

290
        # Apply any global override of the Maintainer field
291 292 293
        if cnf.get("Dinstall::OverrideMaintainer"):
            self.Subst["__MAINTAINER_TO__"] = cnf["Dinstall::OverrideMaintainer"]
            self.Subst["__MAINTAINER_FROM__"] = cnf["Dinstall::OverrideMaintainer"]
294

295
        self.Subst["__REJECT_MESSAGE__"] = self.package_info()
296 297
        self.Subst["__SOURCE__"] = self.pkg.changes.get("source", "Unknown")
        self.Subst["__VERSION__"] = self.pkg.changes.get("version", "Unknown")
J
New.  
James Troup 已提交
298

299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
    ###########################################################################
    def load_changes(self, filename):
        """
        @rtype boolean
        @rvalue: whether the changes file was valid or not.  We may want to
                 reject even if this is True (see what gets put in self.rejects).
                 This is simply to prevent us even trying things later which will
                 fail because we couldn't properly parse the file.
        """
        self.pkg.changes_file = filename

        # Parse the .changes field into a dictionary
        try:
            self.pkg.changes.update(parse_changes(filename))
        except CantOpenError:
            self.rejects.append("%s: can't read file." % (filename))
            return False
        except ParseChangesError, line:
            self.rejects.append("%s: parse error, can't grok: %s." % (filename, line))
            return False
        except ChangesUnicodeError:
            self.rejects.append("%s: changes file not proper utf-8" % (filename))
            return False

        # Parse the Files field from the .changes into another dictionary
        try:
            self.pkg.files.update(build_file_list(self.pkg.changes))
        except ParseChangesError, line:
            self.rejects.append("%s: parse error, can't grok: %s." % (filename, line))
            return False
        except UnknownFormatError, format:
            self.rejects.append("%s: unknown format '%s'." % (filename, format))
            return False

        # Check for mandatory fields
        for i in ("distribution", "source", "binary", "architecture",
                  "version", "maintainer", "files", "changes", "description"):
            if not self.pkg.changes.has_key(i):
                # Avoid undefined errors later
                self.rejects.append("%s: Missing mandatory field `%s'." % (filename, i))
                return False

        # Strip a source version in brackets from the source field
        if re_strip_srcver.search(self.pkg.changes["source"]):
            self.pkg.changes["source"] = re_strip_srcver.sub('', self.pkg.changes["source"])

        # Ensure the source field is a valid package name.
        if not re_valid_pkg_name.match(self.pkg.changes["source"]):
            self.rejects.append("%s: invalid source name '%s'." % (filename, self.pkg.changes["source"]))

        # Split multi-value fields into a lower-level dictionary
        for i in ("architecture", "distribution", "binary", "closes"):
            o = self.pkg.changes.get(i, "")
            if o != "":
                del self.pkg.changes[i]

            self.pkg.changes[i] = {}

            for j in o.split():
                self.pkg.changes[i][j] = 1

        # Fix the Maintainer: field to be RFC822/2047 compatible
        try:
            (self.pkg.changes["maintainer822"],
             self.pkg.changes["maintainer2047"],
             self.pkg.changes["maintainername"],
             self.pkg.changes["maintaineremail"]) = \
                   fix_maintainer (self.pkg.changes["maintainer"])
        except ParseMaintError, msg:
            self.rejects.append("%s: Maintainer field ('%s') failed to parse: %s" \
                   % (filename, changes["maintainer"], msg))

        # ...likewise for the Changed-By: field if it exists.
        try:
            (self.pkg.changes["changedby822"],
             self.pkg.changes["changedby2047"],
             self.pkg.changes["changedbyname"],
             self.pkg.changes["changedbyemail"]) = \
                   fix_maintainer (self.pkg.changes.get("changed-by", ""))
        except ParseMaintError, msg:
            self.pkg.changes["changedby822"] = ""
            self.pkg.changes["changedby2047"] = ""
            self.pkg.changes["changedbyname"] = ""
            self.pkg.changes["changedbyemail"] = ""

            self.rejects.append("%s: Changed-By field ('%s') failed to parse: %s" \
                   % (filename, changes["changed-by"], msg))

        # Ensure all the values in Closes: are numbers
        if self.pkg.changes.has_key("closes"):
            for i in self.pkg.changes["closes"].keys():
                if re_isanum.match (i) == None:
                    self.rejects.append(("%s: `%s' from Closes field isn't a number." % (filename, i)))

        # chopversion = no epoch; chopversion2 = no epoch and no revision (e.g. for .orig.tar.gz comparison)
        self.pkg.changes["chopversion"] = re_no_epoch.sub('', self.pkg.changes["version"])
        self.pkg.changes["chopversion2"] = re_no_revision.sub('', self.pkg.changes["chopversion"])

        # Check there isn't already a changes file of the same name in one
        # of the queue directories.
        base_filename = os.path.basename(filename)
        for d in [ "Accepted", "Byhand", "Done", "New", "ProposedUpdates", "OldProposedUpdates" ]:
            if os.path.exists(os.path.join(Cnf["Dir::Queue::%s" % (d) ], base_filename):
                self.rejects.append("%s: a file with this name already exists in the %s directory." % (base_filename, d))

        # Check the .changes is non-empty
        if not self.pkg.files:
            self.rejects.append("%s: nothing to do (Files field is empty)." % (base_filename))
            return False

        # Changes was syntactically valid even if we'll reject
        return True

    ###########################################################################

    def check_distributions(self):
        "Check and map the Distribution field"

        Cnf = Config()

        # Handle suite mappings
        for m in Cnf.ValueList("SuiteMappings"):
            args = m.split()
            mtype = args[0]
            if mtype == "map" or mtype == "silent-map":
                (source, dest) = args[1:3]
                if self.pkg.changes["distribution"].has_key(source):
                    del self.pkg.changes["distribution"][source]
                    self.pkg.changes["distribution"][dest] = 1
                    if mtype != "silent-map":
                        self.notes.append("Mapping %s to %s." % (source, dest))
                if self.pkg.changes.has_key("distribution-version"):
                    if self.pkg.changes["distribution-version"].has_key(source):
                        self.pkg.changes["distribution-version"][source]=dest
            elif mtype == "map-unreleased":
                (source, dest) = args[1:3]
                if self.pkg.changes["distribution"].has_key(source):
                    for arch in self.pkg.changes["architecture"].keys():
                        if arch not in [ arch_string for a in get_suite_architectures(source) ]:
                            self.notes.append("Mapping %s to %s for unreleased architecture %s." % (source, dest, arch))
                            del self.pkg.changes["distribution"][source]
                            self.pkg.changes["distribution"][dest] = 1
                            break
            elif mtype == "ignore":
                suite = args[1]
                if self.pkg.changes["distribution"].has_key(suite):
                    del self.pkg.changes["distribution"][suite]
                    self.warnings.append("Ignoring %s as a target suite." % (suite))
            elif mtype == "reject":
                suite = args[1]
                if self.pkg.changes["distribution"].has_key(suite):
                    self.rejects.append("Uploads to %s are not accepted." % (suite))
            elif mtype == "propup-version":
                # give these as "uploaded-to(non-mapped) suites-to-add-when-upload-obsoletes"
                #
                # changes["distribution-version"] looks like: {'testing': 'testing-proposed-updates'}
                if self.pkg.changes["distribution"].has_key(args[1]):
                    self.pkg.changes.setdefault("distribution-version", {})
                    for suite in args[2:]:
                        self.pkg.changes["distribution-version"][suite] = suite

        # Ensure there is (still) a target distribution
        if len(self.pkg.changes["distribution"].keys()) < 1:
            self.rejects.append("No valid distribution remaining.")

        # Ensure target distributions exist
        for suite in self.pkg.changes["distribution"].keys():
            if not Cnf.has_key("Suite::%s" % (suite)):
                self.rejects.append("Unknown distribution `%s'." % (suite))

J
New.  
James Troup 已提交
469 470 471
    ###########################################################################

    def build_summaries(self):
J
Joerg Jaspert 已提交
472
        """ Build a summary of changes the upload introduces. """
473 474

        (byhand, new, summary, override_summary) = self.pkg.file_summary()
J
New.  
James Troup 已提交
475

476
        short_summary = summary
J
New.  
James Troup 已提交
477 478

        # This is for direport's benefit...
479
        f = re_fdnic.sub("\n .\n", self.pkg.changes.get("changes", ""))
J
New.  
James Troup 已提交
480 481

        if byhand or new:
482
            summary += "Changes: " + f
J
New.  
James Troup 已提交
483

484 485
        summary += "\n\nOverride entries for your package:\n" + override_summary + "\n"

486
        summary += self.announce(short_summary, 0)
J
New.  
James Troup 已提交
487

488
        return (summary, short_summary)
J
New.  
James Troup 已提交
489 490 491

    ###########################################################################

492
    def close_bugs(self, summary, action):
J
Joerg Jaspert 已提交
493 494 495 496 497 498 499 500 501 502 503 504 505 506
        """
        Send mail to close bugs as instructed by the closes field in the changes file.
        Also add a line to summary if any work was done.

        @type summary: string
        @param summary: summary text, as given by L{build_summaries}

        @type action: bool
        @param action: Set to false no real action will be done.

        @rtype: string
        @return: summary. If action was taken, extended by the list of closed bugs.

        """
J
New.  
James Troup 已提交
507

508 509 510
        template = os.path.join(Config()["Dir::Templates"], 'process-unchecked.bug-close')

        bugs = self.pkg.changes["closes"].keys()
J
New.  
James Troup 已提交
511

512
        if not bugs:
513
            return summary
J
New.  
James Troup 已提交
514

515
        bugs.sort()
516 517 518 519
        summary += "Closing bugs: "
        for bug in bugs:
            summary += "%s " % (bug)
            if action:
520 521 522
                self.Subst["__BUG_NUMBER__"] = bug
                if self.pkg.changes["distribution"].has_key("stable"):
                    self.Subst["__STABLE_WARNING__"] = """
523 524 525 526
Note that this package is not part of the released stable Debian
distribution.  It may have dependencies on other unreleased software,
or other instabilities.  Please take care if you wish to install it.
The update will eventually make its way into the next released Debian
527
distribution."""
528
                else:
529 530 531 532 533 534 535 536
                    self.Subst["__STABLE_WARNING__"] = ""
                    mail_message = utils.TemplateSubst(self.Subst, template)
                    utils.send_mail(mail_message)

                # Clear up after ourselves
                del self.Subst["__BUG_NUMBER__"]
                del self.Subst["__STABLE_WARNING__"]

537
        if action:
538 539
            self.Logger.log(["closing bugs"] + bugs)

540
        summary += "\n"
541

542
        return summary
543 544 545

    ###########################################################################

546
    def announce(self, short_summary, action):
J
Joerg Jaspert 已提交
547 548 549 550 551 552 553 554 555 556 557 558 559
        """
        Send an announce mail about a new upload.

        @type short_summary: string
        @param short_summary: Short summary text to include in the mail

        @type action: bool
        @param action: Set to false no real action will be done.

        @rtype: string
        @return: Textstring about action taken.

        """
560 561 562

        cnf = Config()
        announcetemplate = os.path.join(cnf["Dir::Templates"], 'process-unchecked.announce')
563 564

        # Only do announcements for source uploads with a recent dpkg-dev installed
565 566
        if float(self.pkg.changes.get("format", 0)) < 1.6 or not \
           self.pkg.changes["architecture"].has_key("source"):
567
            return ""
J
New.  
James Troup 已提交
568

569 570
        lists_done = {}
        summary = ""
571

572 573 574
        self.Subst["__SHORT_SUMMARY__"] = short_summary

        for dist in self.pkg.changes["distribution"].keys():
J
Joerg Jaspert 已提交
575 576
            announce_list = Cnf.Find("Suite::%s::Announce" % (dist))
            if announce_list == "" or lists_done.has_key(announce_list):
577
                continue
578

J
Joerg Jaspert 已提交
579 580
            lists_done[announce_list] = 1
            summary += "Announcing to %s\n" % (announce_list)
581 582

            if action:
583 584 585 586 587 588 589 590
                self.Subst["__ANNOUNCE_LIST_ADDRESS__"] = announce_list
                if cnf.get("Dinstall::TrackingServer") and \
                   self.pkg.changes["architecture"].has_key("source"):
                    trackingsendto = "Bcc: %s@%s" % (self.pkg.changes["source"], cnf["Dinstall::TrackingServer"])
                    self.Subst["__ANNOUNCE_LIST_ADDRESS__"] += "\n" + trackingsendto

                mail_message = utils.TemplateSubst(self.Subst, announcetemplate)
                utils.send_mail(mail_message)
591

592 593 594
                del self.Subst["__ANNOUNCE_LIST_ADDRESS__"]

        if cnf.FindB("Dinstall::CloseBugs"):
595
            summary = self.close_bugs(summary, action)
596

597 598
        del self.Subst["__SHORT_SUMMARY__"]

599
        return summary
J
New.  
James Troup 已提交
600 601 602

    ###########################################################################

J
NEW  
Joerg Jaspert 已提交
603
    def accept (self, summary, short_summary, targetdir=None):
J
Joerg Jaspert 已提交
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
        """
        Accept an upload.

        This moves all files referenced from the .changes into the I{accepted}
        queue, sends the accepted mail, announces to lists, closes bugs and
        also checks for override disparities. If enabled it will write out
        the version history for the BTS Version Tracking and will finally call
        L{queue_build}.

        @type summary: string
        @param summary: Summary text

        @type short_summary: string
        @param short_summary: Short summary

        """

621 622 623 624
        cnf = Config()
        stats = SummaryStats()

        accepttemplate = os.path.join(cnf["Dir::Templates"], 'process-unchecked.accepted')
J
New.  
James Troup 已提交
625

J
NEW  
Joerg Jaspert 已提交
626
        if targetdir is None:
627
            targetdir = cnf["Dir::Queue::Accepted"]
J
NEW  
Joerg Jaspert 已提交
628

J
New.  
James Troup 已提交
629
        print "Accepting."
630
        self.Logger.log(["Accepting changes", self.pkg.changes_file])
J
New.  
James Troup 已提交
631

632
        self.write_dot_dak(targetdir)
J
New.  
James Troup 已提交
633 634

        # Move all the files into the accepted directory
635 636 637 638 639 640 641
        utils.move(self.pkg.changes_file, targetdir)

        for name, entry in sorted(self.pkg.files.items()):
            utils.move(name, targetdir)
            stats.accept_bytes += float(entry["size"])

        stats.accept_count += 1
J
New.  
James Troup 已提交
642 643 644

        # Send accept mail, announce to lists, close bugs and check for
        # override disparities
645 646 647 648
        if not cnf["Dinstall::Options::No-Mail"]:
            self.Subst["__SUITE__"] = ""
            self.Subst["__SUMMARY__"] = summary
            mail_message = utils.TemplateSubst(self.Subst, accepttemplate)
649
            utils.send_mail(mail_message)
J
New.  
James Troup 已提交
650 651
            self.announce(short_summary, 1)

652
        ## Helper stuff for DebBugs Version Tracking
653
        if cnf.Find("Dir::Queue::BTSVersionTrack"):
654 655 656 657 658
            # ??? once queue/* is cleared on *.d.o and/or reprocessed
            # the conditionalization on dsc["bts changelog"] should be
            # dropped.

            # Write out the version history from the changelog
659 660
            if self.pkg.changes["architecture"].has_key("source") and \
               self.pkg.dsc.has_key("bts changelog"):
661

662
                (fd, temp_filename) = utils.temp_filename(cnf["Dir::Queue::BTSVersionTrack"], prefix=".")
J
fdopen  
Joerg Jaspert 已提交
663
                version_history = os.fdopen(fd, 'w')
664
                version_history.write(self.pkg.dsc["bts changelog"])
665
                version_history.close()
666 667
                filename = "%s/%s" % (cnf["Dir::Queue::BTSVersionTrack"],
                                      self.pkg.changes_file[:-8]+".versions")
668
                os.rename(temp_filename, filename)
J
Joerg Jaspert 已提交
669
                os.chmod(filename, 0644)
670 671

            # Write out the binary -> source mapping.
672
            (fd, temp_filename) = utils.temp_filename(cnf["Dir::Queue::BTSVersionTrack"], prefix=".")
J
fdopen  
Joerg Jaspert 已提交
673
            debinfo = os.fdopen(fd, 'w')
674 675 676 677 678
            for name, entry in sorted(self.pkg.files.items()):
                if entry["type"] == "deb":
                    line = " ".join([entry["package"], entry["version"],
                                     entry["architecture"], entry["source package"],
                                     entry["source version"]])
679 680
                    debinfo.write(line+"\n")
            debinfo.close()
681 682
            filename = "%s/%s" % (cnf["Dir::Queue::BTSVersionTrack"],
                                  self.pkg.changes_file[:-8]+".debinfo")
683
            os.rename(temp_filename, filename)
J
Joerg Jaspert 已提交
684
            os.chmod(filename, 0644)
685

J
NEW  
Joerg Jaspert 已提交
686 687 688 689 690 691 692 693 694 695 696 697
        # Its is Cnf["Dir::Queue::Accepted"] here, not targetdir!
        # <Ganneff> we do call queue_build too
        # <mhy> well yes, we'd have had to if we were inserting into accepted
        # <Ganneff> now. thats database only.
        # <mhy> urgh, that's going to get messy
        # <Ganneff> so i make the p-n call to it *also* using accepted/
        # <mhy> but then the packages will be in the queue_build table without the files being there
        # <Ganneff> as the buildd queue is only regenerated whenever unchecked runs
        # <mhy> ah, good point
        # <Ganneff> so it will work out, as unchecked move it over
        # <mhy> that's all completely sick
        # <Ganneff> yes
698

699 700 701 702
        # This routine returns None on success or an error on failure
        res = get_queue('accepted').autobuild_upload(self.pkg, cnf["Dir::Queue::Accepted"])
        if res:
            utils.fubar(res)
J
Joerg Jaspert 已提交
703

J
New.  
James Troup 已提交
704 705

    def check_override (self):
J
Joerg Jaspert 已提交
706 707 708 709 710 711 712 713
        """
        Checks override entries for validity. Mails "Override disparity" warnings,
        if that feature is enabled.

        Abandons the check if
          - override disparity checks are disabled
          - mail sending is disabled
        """
714 715

        cnf = Config()
J
New.  
James Troup 已提交
716

717
        # Abandon the check if:
718 719 720 721
        #  a) override disparity checks have been disabled
        #  b) we're not sending mail
        if not cnf.FindB("Dinstall::OverrideDisparityCheck") or \
           cnf["Dinstall::Options::No-Mail"]:
722
            return
J
New.  
James Troup 已提交
723

724
        summary = self.pkg.check_override()
J
New.  
James Troup 已提交
725 726

        if summary == "":
727
            return
J
New.  
James Troup 已提交
728

729 730 731 732
        overridetemplate = os.path.join(cnf["Dir::Templates"], 'process-unchecked.override-disparity')

        self.Subst["__SUMMARY__"] = summary
        mail_message = utils.TemplateSubst(self.Subst, overridetemplate)
733
        utils.send_mail(mail_message)
734
        del self.Subst["__SUMMARY__"]
J
New.  
James Troup 已提交
735 736

    ###########################################################################
737
    def force_reject(self, reject_files):
J
Joerg Jaspert 已提交
738 739 740 741 742 743 744 745 746 747
        """
        Forcefully move files from the current directory to the
        reject directory.  If any file already exists in the reject
        directory it will be moved to the morgue to make way for
        the new file.

        @type files: dict
        @param files: file dictionary

        """
J
New.  
James Troup 已提交
748

749
        cnf = Config()
J
New.  
James Troup 已提交
750

751
        for file_entry in reject_files:
J
New.  
James Troup 已提交
752
            # Skip any files which don't exist or which we don't have permission to copy.
753
            if os.access(file_entry, os.R_OK) == 0:
754
                continue
755 756 757

            dest_file = os.path.join(cnf["Dir::Queue::Reject"], file_entry)

J
New.  
James Troup 已提交
758
            try:
759
                dest_fd = os.open(dest_file, os.O_RDWR | os.O_CREAT | os.O_EXCL, 0644)
J
New.  
James Troup 已提交
760 761
            except OSError, e:
                # File exists?  Let's try and move it to the morgue
762 763
                if e.errno == errno.EEXIST:
                    morgue_file = os.path.join(cnf["Dir::Morgue"], cnf["Dir::MorgueReject"], file_entry)
J
New.  
James Troup 已提交
764
                    try:
765
                        morgue_file = utils.find_next_free(morgue_file)
J
Joerg Jaspert 已提交
766
                    except NoFreeFilenameError:
J
New.  
James Troup 已提交
767 768
                        # Something's either gone badly Pete Tong, or
                        # someone is trying to exploit us.
J
Joerg Jaspert 已提交
769
                        utils.warn("**WARNING** failed to move %s from the reject directory to the morgue." % (file_entry))
770 771
                        return
                    utils.move(dest_file, morgue_file, perms=0660)
J
New.  
James Troup 已提交
772
                    try:
773
                        dest_fd = os.open(dest_file, os.O_RDWR|os.O_CREAT|os.O_EXCL, 0644)
J
New.  
James Troup 已提交
774 775
                    except OSError, e:
                        # Likewise
J
Joerg Jaspert 已提交
776
                        utils.warn("**WARNING** failed to claim %s in the reject directory." % (file_entry))
777
                        return
J
New.  
James Troup 已提交
778
                else:
779
                    raise
J
New.  
James Troup 已提交
780 781
            # If we got here, we own the destination file, so we can
            # safely overwrite it.
J
Joerg Jaspert 已提交
782
            utils.move(file_entry, dest_file, 1, perms=0660)
783
            os.close(dest_fd)
784

J
New.  
James Troup 已提交
785
    ###########################################################################
786
    def do_reject (self, manual=0, reject_message="", note=""):
J
Joerg Jaspert 已提交
787 788 789 790 791 792 793 794 795 796 797 798 799
        """
        Reject an upload. If called without a reject message or C{manual} is
        true, spawn an editor so the user can write one.

        @type manual: bool
        @param manual: manual or automated rejection

        @type reject_message: string
        @param reject_message: A reject message

        @return: 0

        """
J
James Troup 已提交
800 801 802
        # If we weren't given a manual rejection message, spawn an
        # editor so the user can add one in...
        if manual and not reject_message:
J
various  
Joerg Jaspert 已提交
803
            (fd, temp_filename) = utils.temp_filename()
J
Joerg Jaspert 已提交
804 805 806 807 808
            temp_file = os.fdopen(fd, 'w')
            if len(note) > 0:
                for line in note:
                    temp_file.write(line)
            temp_file.close()
J
James Troup 已提交
809
            editor = os.environ.get("EDITOR","vi")
810
            answer = 'E'
J
James Troup 已提交
811 812
            while answer == 'E':
                os.system("%s %s" % (editor, temp_filename))
813 814 815 816 817
                temp_fh = utils.open_file(temp_filename)
                reject_message = "".join(temp_fh.readlines())
                temp_fh.close()
                print "Reject message:"
                print utils.prefix_multi_line_string(reject_message,"  ",include_blank_lines=1)
J
James Troup 已提交
818
                prompt = "[R]eject, Edit, Abandon, Quit ?"
819
                answer = "XXX"
820
                while prompt.find(answer) == -1:
821 822
                    answer = utils.our_raw_input(prompt)
                    m = re_default_answer.search(prompt)
J
James Troup 已提交
823
                    if answer == "":
824 825 826
                        answer = m.group(1)
                    answer = answer[:1].upper()
            os.unlink(temp_filename)
J
James Troup 已提交
827
            if answer == 'A':
828
                return 1
J
James Troup 已提交
829
            elif answer == 'Q':
830
                sys.exit(0)
J
James Troup 已提交
831

J
New.  
James Troup 已提交
832 833
        print "Rejecting.\n"

834
        cnf = Config()
J
New.  
James Troup 已提交
835

836 837
        reason_filename = self.pkg.changes_file[:-8] + ".reason"
        reason_filename = os.path.join(cnf["Dir::Queue::Reject"], reason_filename)
J
New.  
James Troup 已提交
838 839

        # Move all the files into the reject directory
840
        reject_files = self.pkg.files.keys() + [self.pkg.changes_file]
841
        self.force_reject(reject_files)
J
New.  
James Troup 已提交
842 843 844

        # If we fail here someone is probably trying to exploit the race
        # so let's just raise an exception ...
J
James Troup 已提交
845
        if os.path.exists(reason_filename):
846 847
            os.unlink(reason_filename)
        reason_fd = os.open(reason_filename, os.O_RDWR|os.O_CREAT|os.O_EXCL, 0644)
J
New.  
James Troup 已提交
848

849 850
        rej_template = os.path.join(cnf["Dir::Templates"], "queue.rejected")

J
New.  
James Troup 已提交
851
        if not manual:
852 853 854
            self.Subst["__REJECTOR_ADDRESS__"] = cnf["Dinstall::MyEmailAddress"]
            self.Subst["__MANUAL_REJECT_MESSAGE__"] = ""
            self.Subst["__CC__"] = "X-DAK-Rejection: automatic (moo)\nX-Katie-Rejection: automatic (moo)"
855
            os.write(reason_fd, reject_message)
856
            reject_mail_message = utils.TemplateSubst(self.Subst, rej_template)
J
New.  
James Troup 已提交
857 858
        else:
            # Build up the rejection email
859 860 861 862 863
            user_email_address = utils.whoami() + " <%s>" % (cnf["Dinstall::MyAdminAddress"])
            self.Subst["__REJECTOR_ADDRESS__"] = user_email_address
            self.Subst["__MANUAL_REJECT_MESSAGE__"] = reject_message
            self.Subst["__CC__"] = "Cc: " + Cnf["Dinstall::MyEmailAddress"]
            reject_mail_message = utils.TemplateSubst(self.Subst, rej_template)
J
New.  
James Troup 已提交
864
            # Write the rejection email out as the <foo>.reason file
865
            os.write(reason_fd, reject_mail_message)
J
James Troup 已提交
866

867 868 869 870
        del self.Subst["__REJECTOR_ADDRESS__"]
        del self.Subst["__MANUAL_REJECT_MESSAGE__"]
        del self.Subst["__CC__"]

871
        os.close(reason_fd)
J
New.  
James Troup 已提交
872 873

        # Send the rejection mail if appropriate
874
        if not cnf["Dinstall::Options::No-Mail"]:
875
            utils.send_mail(reject_mail_message)
J
New.  
James Troup 已提交
876

877
        self.Logger.log(["rejected", pkg.changes_file])
J
Joerg Jaspert 已提交
878

879
        return 0
J
New.  
James Troup 已提交
880 881

    ################################################################################
882
    def in_override_p(self, package, component, suite, binary_type, file, session=None):
J
Joerg Jaspert 已提交
883 884 885 886 887 888 889
        """
        Check if a package already has override entries in the DB

        @type package: string
        @param package: package name

        @type component: string
890
        @param component: database id of the component
J
Joerg Jaspert 已提交
891 892

        @type suite: int
893
        @param suite: database id of the suite
J
Joerg Jaspert 已提交
894 895 896 897 898 899 900 901 902 903

        @type binary_type: string
        @param binary_type: type of the package

        @type file: string
        @param file: filename we check

        @return: the database result. But noone cares anyway.

        """
904 905 906 907 908

        cnf = Config()

        if session is None:
            session = DBConn().session()
J
New.  
James Troup 已提交
909 910

        if binary_type == "": # must be source
J
Joerg Jaspert 已提交
911
            file_type = "dsc"
J
New.  
James Troup 已提交
912
        else:
J
Joerg Jaspert 已提交
913
            file_type = binary_type
J
New.  
James Troup 已提交
914 915

        # Override suite name; used for example with proposed-updates
916 917 918 919
        if cnf.Find("Suite::%s::OverrideSuite" % (suite)) != "":
            suite = cnf["Suite::%s::OverrideSuite" % (suite)]

        result = get_override(package, suite, component, file_type, session)
J
New.  
James Troup 已提交
920 921

        # If checking for a source package fall back on the binary override type
922 923
        if file_type == "dsc" and len(result) < 1:
            result = get_override(package, suite, component, ['deb', 'udeb'], session)
J
New.  
James Troup 已提交
924 925

        # Remember the section and priority so we can check them later if appropriate
926 927 928 929 930
        if len(result) > 0:
            result = result[0]
            self.pkg.files[file]["override section"] = result.section.section
            self.pkg.files[file]["override priority"] = result.priority.priority
            return result
J
New.  
James Troup 已提交
931

932
        return None
J
New.  
James Troup 已提交
933

934
    ################################################################################
935 936 937 938 939 940 941
    def get_anyversion(self, sv_list, suite):
        """
        @type sv_list: list
        @param sv_list: list of (suite, version) tuples to check

        @type suite: string
        @param suite: suite name
942

943 944 945
        Description: TODO
        """
        anyversion = None
946
        anysuite = [suite] + self.Cnf.ValueList("Suite::%s::VersionChecks::Enhances" % (suite))
947
        for (s, v) in sv_list:
948
            if s in [ x.lower() for x in anysuite ]:
949
                if not anyversion or apt_pkg.VersionCompare(anyversion, v) <= 0:
950 951
                    anyversion = v

952 953 954 955
        return anyversion

    ################################################################################

956
    def cross_suite_version_check(self, sv_list, file, new_version, sourceful=False):
J
Joerg Jaspert 已提交
957
        """
958 959 960 961 962 963 964 965 966
        @type sv_list: list
        @param sv_list: list of (suite, version) tuples to check

        @type file: string
        @param file: XXX

        @type new_version: string
        @param new_version: XXX

J
Joerg Jaspert 已提交
967
        Ensure versions are newer than existing packages in target
968
        suites and that cross-suite version checking rules as
J
Joerg Jaspert 已提交
969 970
        set out in the conf file are satisfied.
        """
971

972 973
        cnf = Config()

974 975
        # Check versions for each target suite
        for target_suite in self.pkg.changes["distribution"].keys():
976 977 978
            must_be_newer_than = [ i.lower() for i in cnf.ValueList("Suite::%s::VersionChecks::MustBeNewerThan" % (target_suite)) ]
            must_be_older_than = [ i.lower() for i in cnf.ValueList("Suite::%s::VersionChecks::MustBeOlderThan" % (target_suite)) ]

979 980
            # Enforce "must be newer than target suite" even if conffile omits it
            if target_suite not in must_be_newer_than:
981
                must_be_newer_than.append(target_suite)
982 983 984 985 986

            for (suite, existent_version) in sv_list:
                vercmp = apt_pkg.VersionCompare(new_version, existent_version)

                if suite in must_be_newer_than and sourceful and vercmp < 1:
987
                    self.rejects.append("%s: old version (%s) in %s >= new version (%s) targeted at %s." % (file, existent_version, suite, new_version, target_suite))
988 989

                if suite in must_be_older_than and vercmp > -1:
990
                    cansave = 0
991

992 993 994 995 996 997
                    if self.pkg.changes.get('distribution-version', {}).has_key(suite):
                        # we really use the other suite, ignoring the conflicting one ...
                        addsuite = self.pkg.changes["distribution-version"][suite]

                        add_version = self.get_anyversion(sv_list, addsuite)
                        target_version = self.get_anyversion(sv_list, target_suite)
998

999 1000 1001 1002 1003 1004 1005 1006 1007 1008
                        if not add_version:
                            # not add_version can only happen if we map to a suite
                            # that doesn't enhance the suite we're propup'ing from.
                            # so "propup-ver x a b c; map a d" is a problem only if
                            # d doesn't enhance a.
                            #
                            # i think we could always propagate in this case, rather
                            # than complaining. either way, this isn't a REJECT issue
                            #
                            # And - we really should complain to the dorks who configured dak
1009
                            self.warnings.append("%s is mapped to, but not enhanced by %s - adding anyways" % (suite, addsuite))
1010 1011
                            self.pkg.changes.setdefault("propdistribution", {})
                            self.pkg.changes["propdistribution"][addsuite] = 1
1012 1013 1014 1015 1016
                            cansave = 1
                        elif not target_version:
                            # not targets_version is true when the package is NEW
                            # we could just stick with the "...old version..." REJECT
                            # for this, I think.
1017
                            self.rejects.append("Won't propogate NEW packages.")
1018 1019
                        elif apt_pkg.VersionCompare(new_version, add_version) < 0:
                            # propogation would be redundant. no need to reject though.
1020
                            self.warnings.append("ignoring versionconflict: %s: old version (%s) in %s <= new version (%s) targeted at %s." % (file, existent_version, suite, new_version, target_suite))
1021 1022
                            cansave = 1
                        elif apt_pkg.VersionCompare(new_version, add_version) > 0 and \
1023
                             apt_pkg.VersionCompare(add_version, target_version) >= 0:
1024
                            # propogate!!
1025
                            self.warnings.append("Propogating upload to %s" % (addsuite))
1026 1027
                            self.pkg.changes.setdefault("propdistribution", {})
                            self.pkg.changes["propdistribution"][addsuite] = 1
1028
                            cansave = 1
1029

1030
                    if not cansave:
1031
                        self.reject.append("%s: old version (%s) in %s <= new version (%s) targeted at %s." % (file, existent_version, suite, new_version, target_suite))
1032 1033 1034

    ################################################################################

1035
    def check_binary_against_db(self, file, session=None):
J
Joerg Jaspert 已提交
1036 1037 1038
        """

        """
1039 1040 1041 1042

        if session is None:
            session = DBConn().session()

1043
        # Ensure version is sane
1044 1045 1046 1047 1048 1049
        q = session.query(BinAssociation)
        q = q.join(DBBinary).filter(DBBinary.package==self.pkg.files[file]["package"])
        q = q.join(Architecture).filter(Architecture.arch_string.in_([self.pkg.files[file]["architecture"], 'all']))

        self.cross_suite_version_check([ (x.suite.suite_name, x.binary.version) for x in q.all() ],
                                       file, files[file]["version"], sourceful=False)
1050

J
New.  
James Troup 已提交
1051
        # Check for any existing copies of the file
1052 1053 1054 1055 1056
        q = session.query(DBBinary).filter_by(files[file]["package"])
        q = q.filter_by(version=files[file]["version"])
        q = q.join(Architecture).filter_by(arch_string=files[file]["architecture"])

        if q.count() > 0:
1057
            self.rejects.append("%s: can not overwrite existing copy already in the archive." % (file))
J
New.  
James Troup 已提交
1058 1059 1060

    ################################################################################

1061
    def check_source_against_db(self, file, session=None):
J
Joerg Jaspert 已提交
1062 1063
        """
        """
1064 1065 1066 1067 1068
        if session is None:
            session = DBConn().session()

        source = self.pkg.dsc.get("source")
        version = self.pkg.dsc.get("version")
J
New.  
James Troup 已提交
1069

1070
        # Ensure version is sane
1071 1072 1073 1074 1075
        q = session.query(SrcAssociation)
        q = q.join(DBSource).filter(DBSource.source==source)

        self.cross_suite_version_check([ (x.suite.suite_name, x.source.version) for x in q.all() ],
                                       file, version, sourceful=True)
1076

J
New.  
James Troup 已提交
1077 1078
    ################################################################################
    def check_dsc_against_db(self, file):
J
Joerg Jaspert 已提交
1079 1080 1081 1082 1083 1084 1085 1086 1087
        """

        @warning: NB: this function can remove entries from the 'files' index [if
         the .orig.tar.gz is a duplicate of the one in the archive]; if
         you're iterating over 'files' and call this function as part of
         the loop, be sure to add a check to the top of the loop to
         ensure you haven't just tried to dereference the deleted entry.

        """
1088
        self.pkg.orig_tar_gz = None
J
New.  
James Troup 已提交
1089 1090 1091 1092

        # Try and find all files mentioned in the .dsc.  This has
        # to work harder to cope with the multiple possible
        # locations of an .orig.tar.gz.
1093 1094
        # The ordering on the select is needed to pick the newest orig
        # when it exists in multiple places.
1095
        for dsc_name, dsc_entry in self.pkg.dsc_files.items():
1096
            found = None
1097 1098 1099 1100 1101
            if self.pkg.files.has_key(dsc_name):
                actual_md5 = self.pkg.files[dsc_name]["md5sum"]
                actual_size = int(self.pkg.files[dsc_name]["size"])
                found = "%s in incoming" % (dsc_name)

J
New.  
James Troup 已提交
1102
                # Check the file does not already exist in the archive
1103 1104
                ql = get_poolfile_like_name(dsc_name)

1105 1106
                # Strip out anything that isn't '%s' or '/%s$'
                for i in ql:
1107
                    if not i.filename.endswith(dsc_name):
1108
                        ql.remove(i)
1109

1110
                # "[dak] has not broken them.  [dak] has fixed a
J
New.  
James Troup 已提交
1111 1112 1113 1114 1115 1116 1117 1118
                # brokenness.  Your crappy hack exploited a bug in
                # the old dinstall.
                #
                # "(Come on!  I thought it was always obvious that
                # one just doesn't release different files with
                # the same name and version.)"
                #                        -- ajk@ on d-devel@l.d.o

1119
                if len(ql) > 0:
1120
                    # Ignore exact matches for .orig.tar.gz
1121
                    match = 0
1122
                    if dsc_name.endswith(".orig.tar.gz"):
1123
                        for i in ql:
1124 1125 1126
                            if self.pkg.files.has_key(dsc_name) and \
                               int(self.pkg.files[dsc_name]["size"]) == int(i.filesize) and \
                               self.pkg.files[dsc_name]["md5sum"] == i.md5sum:
1127
                                self.warnings.append("ignoring %s, since it's already in the archive." % (dsc_name))
1128 1129 1130 1131 1132
                                # TODO: Don't delete the entry, just mark it as not needed
                                # This would fix the stupidity of changing something we often iterate over
                                # whilst we're doing it
                                del files[dsc_name]
                                self.pkg.orig_tar_gz = os.path.join(i.location.path, i.filename)
1133
                                match = 1
1134 1135

                    if not match:
1136
                        self.rejects.append("can not overwrite existing copy of '%s' already in the archive." % (dsc_name))
1137 1138

            elif dsc_name.endswith(".orig.tar.gz"):
J
New.  
James Troup 已提交
1139
                # Check in the pool
1140 1141
                ql = get_poolfile_like_name(dsc_name)

1142
                # Strip out anything that isn't '%s' or '/%s$'
1143
                # TODO: Shouldn't we just search for things which end with our string explicitly in the SQL?
1144
                for i in ql:
1145
                    if not i.filename.endswith(dsc_name):
1146
                        ql.remove(i)
J
New.  
James Troup 已提交
1147

1148
                if len(ql) > 0:
1149 1150 1151
                    # Unfortunately, we may get more than one match here if,
                    # for example, the package was in potato but had an -sa
                    # upload in woody.  So we need to choose the right one.
J
New.  
James Troup 已提交
1152

J
Various  
Joerg Jaspert 已提交
1153 1154
                    # default to something sane in case we don't match any or have only one
                    x = ql[0]
J
New.  
James Troup 已提交
1155 1156 1157

                    if len(ql) > 1:
                        for i in ql:
1158
                            old_file = os.path.join(i.location.path, i.filename)
1159
                            old_file_fh = utils.open_file(old_file)
1160
                            actual_md5 = apt_pkg.md5sum(old_file_fh)
1161
                            old_file_fh.close()
1162
                            actual_size = os.stat(old_file)[stat.ST_SIZE]
1163
                            if actual_md5 == dsc_entry["md5sum"] and actual_size == int(dsc_entry["size"]):
1164
                                x = i
J
New.  
James Troup 已提交
1165

1166
                    old_file = os.path.join(i.location.path, i.filename)
1167
                    old_file_fh = utils.open_file(old_file)
1168
                    actual_md5 = apt_pkg.md5sum(old_file_fh)
1169
                    old_file_fh.close()
1170 1171
                    actual_size = os.stat(old_file)[stat.ST_SIZE]
                    found = old_file
1172
                    suite_type = f.location.archive_type
J
Various  
Joerg Jaspert 已提交
1173
                    # need this for updating dsc_files in install()
1174
                    dsc_entry["files id"] = f.file_id
1175
                    # See install() in process-accepted...
1176
                    self.pkg.orig_tar_id = f.file_id
1177
                    self.pkg.orig_tar_gz = old_file
1178
                    self.pkg.orig_tar_location = f.location.location_id
J
New.  
James Troup 已提交
1179
                else:
1180
                    # TODO: Record the queues and info in the DB so we don't hardcode all this crap
1181
                    # Not there? Check the queue directories...
1182 1183 1184 1185 1186 1187 1188 1189 1190
                    for directory in [ "Accepted", "New", "Byhand", "ProposedUpdates", "OldProposedUpdates", "Embargoed", "Unembargoed" ]:
                        in_otherdir = os.path.join(self.Cnf["Dir::Queue::%s" % (directory)], dsc_name)
                        if os.path.exists(in_otherdir):
                            in_otherdir_fh = utils.open_file(in_otherdir)
                            actual_md5 = apt_pkg.md5sum(in_otherdir_fh)
                            in_otherdir_fh.close()
                            actual_size = os.stat(in_otherdir)[stat.ST_SIZE]
                            found = in_otherdir
                            self.pkg.orig_tar_gz = in_otherdir
1191 1192

                    if not found:
1193
                        self.rejects.append("%s refers to %s, but I can't find it in the queue or in the pool." % (file, dsc_name))
1194 1195
                        self.pkg.orig_tar_gz = -1
                        continue
J
New.  
James Troup 已提交
1196
            else:
1197
                self.rejects.append("%s refers to %s, but I can't find it in the queue." % (file, dsc_name))
1198
                continue
1199
            if actual_md5 != dsc_entry["md5sum"]:
1200
                self.rejects.append("md5sum for %s doesn't match %s." % (found, file))
1201
            if actual_size != int(dsc_entry["size"]):
1202
                self.rejects.append("size for %s doesn't match %s." % (found, file))
J
New.  
James Troup 已提交
1203