queue.py 48.6 KB
Newer Older
J
New.  
James Troup 已提交
1 2
#!/usr/bin/env python

3 4
# Queue utility functions for dak
# Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006  James Troup <james@nocrew.org>
J
New.  
James Troup 已提交
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

# 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

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

22
import cPickle, errno, os, pg, re, stat, sys, time
23
import apt_inst, apt_pkg
24
import utils, database
J
New.  
James Troup 已提交
25

26
from types import *
J
New.  
James Troup 已提交
27 28 29

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

30 31 32 33
re_isanum = re.compile (r"^\d+$")
re_default_answer = re.compile(r"\[(.*)\]")
re_fdnic = re.compile(r"\n\n")
re_bin_only_nmu = re.compile(r"\+b\d+$")
J
James Troup 已提交
34

35 36 37 38 39 40 41 42 43 44 45 46 47 48 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 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
################################################################################

# Determine what parts in a .changes are NEW

def determine_new(changes, files, projectB, warn=1):
    new = {}

    # Build up a list of potentially new things
    for file in files.keys():
        f = files[file]
        # Skip byhand elements
        if f["type"] == "byhand":
            continue
        pkg = f["package"]
        priority = f["priority"]
        section = f["section"]
        type = get_type(f)
        component = f["component"]

        if type == "dsc":
            priority = "source"
        if not new.has_key(pkg):
            new[pkg] = {}
            new[pkg]["priority"] = priority
            new[pkg]["section"] = section
            new[pkg]["type"] = type
            new[pkg]["component"] = component
            new[pkg]["files"] = []
        else:
            old_type = new[pkg]["type"]
            if old_type != type:
                # source gets trumped by deb or udeb
                if old_type == "dsc":
                    new[pkg]["priority"] = priority
                    new[pkg]["section"] = section
                    new[pkg]["type"] = type
                    new[pkg]["component"] = component
        new[pkg]["files"].append(file)
        if f.has_key("othercomponents"):
            new[pkg]["othercomponents"] = f["othercomponents"]

    for suite in changes["suite"].keys():
        suite_id = database.get_suite_id(suite)
        for pkg in new.keys():
            component_id = database.get_component_id(new[pkg]["component"])
            type_id = database.get_override_type_id(new[pkg]["type"])
            q = projectB.query("SELECT package FROM override WHERE package = '%s' AND suite = %s AND component = %s AND type = %s" % (pkg, suite_id, component_id, type_id))
            ql = q.getresult()
            if ql:
                for file in new[pkg]["files"]:
                    if files[file].has_key("new"):
                        del files[file]["new"]
                del new[pkg]

    if warn:
        if changes["suite"].has_key("stable"):
            print "WARNING: overrides will be added for stable!"
            if changes["suite"].has_key("oldstable"):
                print "WARNING: overrides will be added for OLDstable!"
        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 get_type(f):
    # Determine the type
    if f.has_key("dbtype"):
        type = f["dbtype"]
    elif f["type"] in [ "orig.tar.gz", "orig.tar.bz2", "tar.gz", "tar.bz2", "diff.gz", "diff.bz2", "dsc" ]:
        type = "dsc"
    else:
109
        utils.fubar("invalid type (%s) for new.  Dazed, confused and sure as heck not continuing." % (type))
110 111 112 113

    # Validate the override type
    type_id = database.get_override_type_id(type)
    if type_id == -1:
114
        utils.fubar("invalid type (%s) for new.  Say wha?" % (type))
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137

    return type

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

# check if section/priority values are valid

def check_valid(new):
    for pkg in new.keys():
        section = new[pkg]["section"]
        priority = new[pkg]["priority"]
        type = new[pkg]["type"]
        new[pkg]["section id"] = database.get_section_id(section)
        new[pkg]["priority id"] = database.get_priority_id(new[pkg]["priority"])
        # Sanity checks
        di = section.find("debian-installer") != -1
        if (di and type != "udeb") or (not di and type == "udeb"):
            new[pkg]["section id"] = -1
        if (priority == "source" and type != "dsc") or \
           (priority != "source" and type == "dsc"):
            new[pkg]["priority id"] = -1


J
New.  
James Troup 已提交
138 139 140 141 142 143
###############################################################################

# Convenience wrapper to carry around all the package information in

class Pkg:
    def __init__(self, **kwds):
144
        self.__dict__.update(kwds)
J
New.  
James Troup 已提交
145 146

    def update(self, **kwds):
147
        self.__dict__.update(kwds)
J
New.  
James Troup 已提交
148 149 150

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

151
class Upload:
J
New.  
James Troup 已提交
152 153

    def __init__(self, Cnf):
154 155 156
        self.Cnf = Cnf
        self.accept_count = 0
        self.accept_bytes = 0L
J
New.  
James Troup 已提交
157
        self.pkg = Pkg(changes = {}, dsc = {}, dsc_files = {}, files = {},
158
                       legacy_source_untouchable = {})
J
New.  
James Troup 已提交
159 160

        # Initialize the substitution template mapping global
161 162 163 164
        Subst = self.Subst = {}
        Subst["__ADMIN_ADDRESS__"] = Cnf["Dinstall::MyAdminAddress"]
        Subst["__BUG_SERVER__"] = Cnf["Dinstall::BugServer"]
        Subst["__DISTRO__"] = Cnf["Dinstall::MyDistribution"]
165
        Subst["__DAK_ADDRESS__"] = Cnf["Dinstall::MyEmailAddress"]
J
New.  
James Troup 已提交
166

167
        self.projectB = pg.connect(Cnf["DB::Name"], Cnf["DB::Host"], int(Cnf["DB::Port"]))
168
        database.init(Cnf, self.projectB)
J
New.  
James Troup 已提交
169 170 171 172 173

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

    def init_vars (self):
        for i in [ "changes", "dsc", "files", "dsc_files", "legacy_source_untouchable" ]:
174 175 176 177
            exec "self.pkg.%s.clear();" % (i)
        self.pkg.orig_tar_id = None
        self.pkg.orig_tar_location = ""
        self.pkg.orig_tar_gz = None
J
New.  
James Troup 已提交
178 179 180 181

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

    def update_vars (self):
182
        dump_filename = self.pkg.changes_file[:-8]+".dak"
183 184
        dump_file = utils.open_file(dump_filename)
        p = cPickle.Unpickler(dump_file)
J
New.  
James Troup 已提交
185
        for i in [ "changes", "dsc", "files", "dsc_files", "legacy_source_untouchable" ]:
186
            exec "self.pkg.%s.update(p.load());" % (i)
J
New.  
James Troup 已提交
187
        for i in [ "orig_tar_id", "orig_tar_location" ]:
188 189
            exec "self.pkg.%s = p.load();" % (i)
        dump_file.close()
J
New.  
James Troup 已提交
190 191 192

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

193 194 195
    # This could just dump the dictionaries as is, but I'd like to
    # avoid this so there's some idea of what process-accepted &
    # process-new use from process-unchecked
J
New.  
James Troup 已提交
196 197 198 199

    def dump_vars(self, dest_dir):
        for i in [ "changes", "dsc", "files", "dsc_files",
                   "legacy_source_untouchable", "orig_tar_id", "orig_tar_location" ]:
200
            exec "%s = self.pkg.%s;" % (i,i)
201
        dump_filename = os.path.join(dest_dir,self.pkg.changes_file[:-8] + ".dak")
202
        dump_file = utils.open_file(dump_filename, 'w')
J
James Troup 已提交
203
        try:
204
            os.chmod(dump_filename, 0660)
J
James Troup 已提交
205 206
        except OSError, e:
            if errno.errorcode[e.errno] == 'EPERM':
207
                perms = stat.S_IMODE(os.stat(dump_filename)[stat.ST_MODE])
J
James Troup 已提交
208
                if perms & stat.S_IROTH:
209
                    utils.fubar("%s is world readable and chmod failed." % (dump_filename))
J
James Troup 已提交
210
            else:
211
                raise
J
James Troup 已提交
212

213
        p = cPickle.Pickler(dump_file, 1)
J
New.  
James Troup 已提交
214
        for i in [ "d_changes", "d_dsc", "d_files", "d_dsc_files" ]:
215
            exec "%s = {}" % i
J
New.  
James Troup 已提交
216 217
        ## files
        for file in files.keys():
218
            d_files[file] = {}
J
New.  
James Troup 已提交
219 220 221
            for i in [ "package", "version", "architecture", "type", "size",
                       "md5sum", "component", "location id", "source package",
                       "source version", "maintainer", "dbtype", "files id",
222
                       "new", "section", "priority", "othercomponents",
223
                       "pool name", "original component" ]:
J
New.  
James Troup 已提交
224
                if files[file].has_key(i):
225
                    d_files[file][i] = files[file][i]
J
New.  
James Troup 已提交
226 227
        ## changes
        # Mandatory changes fields
228 229 230 231 232
        for i in [ "distribution", "source", "architecture", "version",
                   "maintainer", "urgency", "fingerprint", "changedby822",
                   "changedby2047", "changedbyname", "maintainer822",
                   "maintainer2047", "maintainername", "maintaineremail",
                   "closes", "changes" ]:
233
            d_changes[i] = changes[i]
J
New.  
James Troup 已提交
234
        # Optional changes fields
235 236
        for i in [ "changed-by", "filecontents", "format", "process-new note", "adv id", "distribution-version",
                   "sponsoremail" ]:
J
James Troup 已提交
237
            if changes.has_key(i):
238
                d_changes[i] = changes[i]
J
New.  
James Troup 已提交
239
        ## dsc
240
        for i in [ "source", "version", "maintainer", "fingerprint",
A
Anthony Towns 已提交
241
                   "uploaders", "bts changelog", "dm-upload-allowed" ]:
J
New.  
James Troup 已提交
242
            if dsc.has_key(i):
243
                d_dsc[i] = dsc[i]
J
New.  
James Troup 已提交
244 245
        ## dsc_files
        for file in dsc_files.keys():
246
            d_dsc_files[file] = {}
J
New.  
James Troup 已提交
247 248
            # Mandatory dsc_files fields
            for i in [ "size", "md5sum" ]:
249
                d_dsc_files[file][i] = dsc_files[file][i]
J
New.  
James Troup 已提交
250 251 252
            # Optional dsc_files fields
            for i in [ "files id" ]:
                if dsc_files[file].has_key(i):
253
                    d_dsc_files[file][i] = dsc_files[file][i]
J
New.  
James Troup 已提交
254 255 256

        for i in [ d_changes, d_dsc, d_files, d_dsc_files,
                   legacy_source_untouchable, orig_tar_id, orig_tar_location ]:
257 258
            p.dump(i)
        dump_file.close()
J
New.  
James Troup 已提交
259 260 261 262 263 264

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

    # Set up the per-package template substitution mappings

    def update_subst (self, reject_message = ""):
265 266
        Subst = self.Subst
        changes = self.pkg.changes
267
        # If 'dak process-unchecked' crashed out in the right place, architecture may still be a string.
J
New.  
James Troup 已提交
268
        if not changes.has_key("architecture") or not isinstance(changes["architecture"], DictType):
269
            changes["architecture"] = { "Unknown" : "" }
270 271
        # and maintainer2047 may not exist.
        if not changes.has_key("maintainer2047"):
272
            changes["maintainer2047"] = self.Cnf["Dinstall::MyEmailAddress"]
J
New.  
James Troup 已提交
273

274 275 276
        Subst["__ARCHITECTURE__"] = " ".join(changes["architecture"].keys())
        Subst["__CHANGES_FILENAME__"] = os.path.basename(self.pkg.changes_file)
        Subst["__FILE_CONTENTS__"] = changes.get("filecontents", "")
J
New.  
James Troup 已提交
277 278 279

        # For source uploads the Changed-By field wins; otherwise Maintainer wins.
        if changes["architecture"].has_key("source") and changes["changedby822"] != "" and (changes["changedby822"] != changes["maintainer822"]):
280
            Subst["__MAINTAINER_FROM__"] = changes["changedby2047"]
281
            Subst["__MAINTAINER_TO__"] = "%s, %s" % (changes["changedby2047"],
282 283
                                                     changes["maintainer2047"])
            Subst["__MAINTAINER__"] = changes.get("changed-by", "Unknown")
J
New.  
James Troup 已提交
284
        else:
285 286 287
            Subst["__MAINTAINER_FROM__"] = changes["maintainer2047"]
            Subst["__MAINTAINER_TO__"] = changes["maintainer2047"]
            Subst["__MAINTAINER__"] = changes.get("maintainer", "Unknown")
J
Joerg Jaspert 已提交
288 289 290 291

        if "sponsoremail" in changes:
            Subst["__MAINTAINER_TO__"] += ", %s"%changes["sponsoremail"]

J
New.  
James Troup 已提交
292
        if self.Cnf.has_key("Dinstall::TrackingServer") and changes.has_key("source"):
293
            Subst["__MAINTAINER_TO__"] += "\nBcc: %s@%s" % (changes["source"], self.Cnf["Dinstall::TrackingServer"])
J
New.  
James Troup 已提交
294

295 296
        # Apply any global override of the Maintainer field
        if self.Cnf.get("Dinstall::OverrideMaintainer"):
297 298
            Subst["__MAINTAINER_TO__"] = self.Cnf["Dinstall::OverrideMaintainer"]
            Subst["__MAINTAINER_FROM__"] = self.Cnf["Dinstall::OverrideMaintainer"]
299

300 301 302
        Subst["__REJECT_MESSAGE__"] = reject_message
        Subst["__SOURCE__"] = changes.get("source", "Unknown")
        Subst["__VERSION__"] = changes.get("version", "Unknown")
J
New.  
James Troup 已提交
303 304 305 306

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

    def build_summaries(self):
307 308
        changes = self.pkg.changes
        files = self.pkg.files
J
New.  
James Troup 已提交
309

310
        byhand = summary = new = ""
J
New.  
James Troup 已提交
311 312 313 314

        # changes["distribution"] may not exist in corner cases
        # (e.g. unreadable changes files)
        if not changes.has_key("distribution") or not isinstance(changes["distribution"], DictType):
315
            changes["distribution"] = {}
J
New.  
James Troup 已提交
316

317
        override_summary ="";
318 319
        file_keys = files.keys()
        file_keys.sort()
J
New.  
James Troup 已提交
320 321 322
        for file in file_keys:
            if files[file].has_key("byhand"):
                byhand = 1
323
                summary += file + " byhand\n"
J
New.  
James Troup 已提交
324 325
            elif files[file].has_key("new"):
                new = 1
326
                summary += "(new) %s %s %s\n" % (file, files[file]["priority"], files[file]["section"])
J
New.  
James Troup 已提交
327
                if files[file].has_key("othercomponents"):
328
                    summary += "WARNING: Already present in %s distribution.\n" % (files[file]["othercomponents"])
J
New.  
James Troup 已提交
329
                if files[file]["type"] == "deb":
330
                    deb_fh = utils.open_file(file)
331
                    summary += apt_pkg.ParseSection(apt_inst.debExtractControl(deb_fh))["Description"] + '\n'
332
                    deb_fh.close()
J
New.  
James Troup 已提交
333
            else:
334
                files[file]["pool name"] = utils.poolify (changes.get("source",""), files[file]["component"])
J
New.  
James Troup 已提交
335
                destination = self.Cnf["Dir::PoolRoot"] + files[file]["pool name"] + file
336
                summary += file + "\n  to " + destination + "\n"
337 338
		if not files[file].has_key("type"):
		    files[file]["type"] = "unknown"
339
                if files[file]["type"] in ["deb", "udeb", "dsc"]:
340 341 342 343 344
                    # (queue/unchecked), there we have override entries already, use them
                    # (process-new), there we dont have override entries, use the newly generated ones.
                    override_prio = files[file].get("override priority", files[file]["priority"])
                    override_sect = files[file].get("override section", files[file]["section"])
                    override_summary += "%s - %s %s\n" % (file, override_prio, override_sect)
J
New.  
James Troup 已提交
345

346
        short_summary = summary
J
New.  
James Troup 已提交
347 348

        # This is for direport's benefit...
349
        f = re_fdnic.sub("\n .\n", changes.get("changes",""))
J
New.  
James Troup 已提交
350 351

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

354 355
        summary += "\n\nOverride entries for your package:\n" + override_summary + "\n"

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

358
        return (summary, short_summary)
J
New.  
James Troup 已提交
359 360 361

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

362
    def close_bugs (self, summary, action):
363 364 365
        changes = self.pkg.changes
        Subst = self.Subst
        Cnf = self.Cnf
J
New.  
James Troup 已提交
366

367
        bugs = changes["closes"].keys()
J
New.  
James Troup 已提交
368

369
        if not bugs:
370
            return summary
J
New.  
James Troup 已提交
371

372
        bugs.sort()
373 374 375 376 377 378 379
        summary += "Closing bugs: "
        for bug in bugs:
            summary += "%s " % (bug)
            if action:
                Subst["__BUG_NUMBER__"] = bug
                if changes["distribution"].has_key("stable"):
                    Subst["__STABLE_WARNING__"] = """
380 381 382 383
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
384
distribution."""
385 386 387 388 389 390
                else:
                    Subst["__STABLE_WARNING__"] = ""
                    mail_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/process-unchecked.bug-close")
                    utils.send_mail (mail_message)
        if action:
            self.Logger.log(["closing bugs"]+bugs)
391
        summary += "\n"
392

393
        return summary
394 395 396 397

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

    def announce (self, short_summary, action):
398 399 400
        Subst = self.Subst
        Cnf = self.Cnf
        changes = self.pkg.changes
401 402 403

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

406 407 408
        lists_done = {}
        summary = ""
        Subst["__SHORT_SUMMARY__"] = short_summary
409 410

        for dist in changes["distribution"].keys():
411
            list = Cnf.Find("Suite::%s::Announce" % (dist))
412
            if list == "" or lists_done.has_key(list):
413 414 415
                continue
            lists_done[list] = 1
            summary += "Announcing to %s\n" % (list)
416 417

            if action:
418
                Subst["__ANNOUNCE_LIST_ADDRESS__"] = list
419
                if Cnf.get("Dinstall::TrackingServer") and changes["architecture"].has_key("source"):
420
                    Subst["__ANNOUNCE_LIST_ADDRESS__"] = Subst["__ANNOUNCE_LIST_ADDRESS__"] + "\nBcc: %s@%s" % (changes["source"], Cnf["Dinstall::TrackingServer"])
421
                mail_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/process-unchecked.announce")
422
                utils.send_mail (mail_message)
423

J
James Troup 已提交
424
        if Cnf.FindB("Dinstall::CloseBugs"):
425
            summary = self.close_bugs(summary, action)
426

427
        return summary
J
New.  
James Troup 已提交
428 429 430 431

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

    def accept (self, summary, short_summary):
432 433 434 435 436 437
        Cnf = self.Cnf
        Subst = self.Subst
        files = self.pkg.files
        changes = self.pkg.changes
        changes_file = self.pkg.changes_file
        dsc = self.pkg.dsc
J
New.  
James Troup 已提交
438 439

        print "Accepting."
440
        self.Logger.log(["Accepting changes",changes_file])
J
New.  
James Troup 已提交
441

442
        self.dump_vars(Cnf["Dir::Queue::Accepted"])
J
New.  
James Troup 已提交
443 444

        # Move all the files into the accepted directory
445 446
        utils.move(changes_file, Cnf["Dir::Queue::Accepted"])
        file_keys = files.keys()
J
New.  
James Troup 已提交
447
        for file in file_keys:
448
            utils.move(file, Cnf["Dir::Queue::Accepted"])
449
            self.accept_bytes += float(files[file]["size"])
450
        self.accept_count += 1
J
New.  
James Troup 已提交
451 452 453 454

        # Send accept mail, announce to lists, close bugs and check for
        # override disparities
        if not Cnf["Dinstall::Options::No-Mail"]:
455 456
            Subst["__SUITE__"] = ""
            Subst["__SUMMARY__"] = summary
457
            mail_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/process-unchecked.accepted")
458
            utils.send_mail(mail_message)
J
New.  
James Troup 已提交
459 460
            self.announce(short_summary, 1)

461 462 463 464 465 466 467 468 469 470 471 472

        ## Helper stuff for DebBugs Version Tracking
        if Cnf.Find("Dir::Queue::BTSVersionTrack"):
            # ??? 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
            if changes["architecture"].has_key("source") and \
               dsc.has_key("bts changelog"):

                temp_filename = utils.temp_filename(Cnf["Dir::Queue::BTSVersionTrack"],
473 474 475 476
                                                    dotprefix=1, perms=0644)
                version_history = utils.open_file(temp_filename, 'w')
                version_history.write(dsc["bts changelog"])
                version_history.close()
477
                filename = "%s/%s" % (Cnf["Dir::Queue::BTSVersionTrack"],
478 479
                                      changes_file[:-8]+".versions")
                os.rename(temp_filename, filename)
480 481 482

            # Write out the binary -> source mapping.
            temp_filename = utils.temp_filename(Cnf["Dir::Queue::BTSVersionTrack"],
483 484
                                                dotprefix=1, perms=0644)
            debinfo = utils.open_file(temp_filename, 'w')
485
            for file in file_keys:
486
                f = files[file]
487 488 489
                if f["type"] == "deb":
                    line = " ".join([f["package"], f["version"],
                                     f["architecture"], f["source package"],
490 491 492
                                     f["source version"]])
                    debinfo.write(line+"\n")
            debinfo.close()
493
            filename = "%s/%s" % (Cnf["Dir::Queue::BTSVersionTrack"],
494 495
                                  changes_file[:-8]+".debinfo")
            os.rename(temp_filename, filename)
496

497 498 499 500 501
        self.queue_build("accepted", Cnf["Dir::Queue::Accepted"])

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

    def queue_build (self, queue, path):
502 503 504 505 506 507 508 509
        Cnf = self.Cnf
        Subst = self.Subst
        files = self.pkg.files
        changes = self.pkg.changes
        changes_file = self.pkg.changes_file
        dsc = self.pkg.dsc
        file_keys = files.keys()

510
        ## Special support to enable clean auto-building of queued packages
511
        queue_id = database.get_or_set_queue_id(queue)
512

513
        self.projectB.query("BEGIN WORK")
514
        for suite in changes["distribution"].keys():
515
            if suite not in Cnf.ValueList("Dinstall::QueueBuildSuites"):
516
                continue
517
            suite_id = database.get_suite_id(suite)
518
            dest_dir = Cnf["Dir::QueueBuild"]
519
            if Cnf.FindB("Dinstall::SecurityQueueBuild"):
520
                dest_dir = os.path.join(dest_dir, suite)
521
            for file in file_keys:
522 523
                src = os.path.join(path, file)
                dest = os.path.join(dest_dir, file)
524
                if Cnf.FindB("Dinstall::SecurityQueueBuild"):
525
                    # Copy it since the original won't be readable by www-data
526
                    utils.copy(src, dest)
527 528
                else:
                    # Create a symlink to it
529
                    os.symlink(src, dest)
J
James Troup 已提交
530
                # Add it to the list of packages for later processing by apt-ftparchive
531
                self.projectB.query("INSERT INTO queue_build (suite, queue, filename, in_queue) VALUES (%s, %s, '%s', 't')" % (suite_id, queue_id, dest))
J
James Troup 已提交
532 533
            # If the .orig.tar.gz is in the pool, create a symlink to
            # it (if one doesn't already exist)
534 535 536
            if self.pkg.orig_tar_id:
                # Determine the .orig.tar.gz file name
                for dsc_file in self.pkg.dsc_files.keys():
537
                    if dsc_file.endswith(".orig.tar.gz"):
538 539
                        filename = dsc_file
                dest = os.path.join(dest_dir, filename)
540 541 542
                # If it doesn't exist, create a symlink
                if not os.path.exists(dest):
                    # Find the .orig.tar.gz in the pool
543 544
                    q = self.projectB.query("SELECT l.path, f.filename from location l, files f WHERE f.id = %s and f.location = l.id" % (self.pkg.orig_tar_id))
                    ql = q.getresult()
545
                    if not ql:
546 547 548
                        utils.fubar("[INTERNAL ERROR] Couldn't find id %s in files table." % (self.pkg.orig_tar_id))
                    src = os.path.join(ql[0][0], ql[0][1])
                    os.symlink(src, dest)
J
James Troup 已提交
549
                    # Add it to the list of packages for later processing by apt-ftparchive
550
                    self.projectB.query("INSERT INTO queue_build (suite, queue, filename, in_queue) VALUES (%s, %s, '%s', 't')" % (suite_id, queue_id, dest))
551 552
                # if it does, update things to ensure it's not removed prematurely
                else:
553
                    self.projectB.query("UPDATE queue_build SET in_queue = 't', last_used = NULL WHERE filename = '%s' AND suite = %s" % (dest, suite_id))
J
James Troup 已提交
554

555
        self.projectB.query("COMMIT WORK")
556

J
New.  
James Troup 已提交
557 558 559
    ###########################################################################

    def check_override (self):
560 561 562 563
        Subst = self.Subst
        changes = self.pkg.changes
        files = self.pkg.files
        Cnf = self.Cnf
J
New.  
James Troup 已提交
564

565 566 567 568 569 570 571
        # Abandon the check if:
        #  a) it's a non-sourceful upload
        #  b) override disparity checks have been disabled
        #  c) we're not sending mail
        if not changes["architecture"].has_key("source") or \
           not Cnf.FindB("Dinstall::OverrideDisparityCheck") or \
           Cnf["Dinstall::Options::No-Mail"]:
572
            return
J
New.  
James Troup 已提交
573

574 575 576
        summary = ""
        file_keys = files.keys()
        file_keys.sort()
J
James Troup 已提交
577
        for file in file_keys:
J
New.  
James Troup 已提交
578
            if not files[file].has_key("new") and files[file]["type"] == "deb":
579 580
                section = files[file]["section"]
                override_section = files[file]["override section"]
581
                if section.lower() != override_section.lower() and section != "-":
582 583 584
                    summary += "%s: package says section is %s, override says %s.\n" % (file, section, override_section)
                priority = files[file]["priority"]
                override_priority = files[file]["override priority"]
J
New.  
James Troup 已提交
585
                if priority != override_priority and priority != "-":
586
                    summary += "%s: package says priority is %s, override says %s.\n" % (file, priority, override_priority)
J
New.  
James Troup 已提交
587 588

        if summary == "":
589
            return
J
New.  
James Troup 已提交
590

591
        Subst["__SUMMARY__"] = summary
592
        mail_message = utils.TemplateSubst(Subst,self.Cnf["Dir::Templates"]+"/process-unchecked.override-disparity")
593
        utils.send_mail(mail_message)
J
New.  
James Troup 已提交
594 595 596

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

597 598 599 600 601
    def force_reject (self, files):
        """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."""
J
New.  
James Troup 已提交
602 603 604 605 606 607

        Cnf = self.Cnf

        for file in files:
            # Skip any files which don't exist or which we don't have permission to copy.
            if os.access(file,os.R_OK) == 0:
608 609
                continue
            dest_file = os.path.join(Cnf["Dir::Queue::Reject"], file)
J
New.  
James Troup 已提交
610
            try:
611
                dest_fd = os.open(dest_file, os.O_RDWR|os.O_CREAT|os.O_EXCL, 0644)
J
New.  
James Troup 已提交
612 613 614
            except OSError, e:
                # File exists?  Let's try and move it to the morgue
                if errno.errorcode[e.errno] == 'EEXIST':
615
                    morgue_file = os.path.join(Cnf["Dir::Morgue"],Cnf["Dir::MorgueReject"],file)
J
New.  
James Troup 已提交
616
                    try:
617
                        morgue_file = utils.find_next_free(morgue_file)
J
New.  
James Troup 已提交
618 619 620
                    except utils.tried_too_hard_exc:
                        # Something's either gone badly Pete Tong, or
                        # someone is trying to exploit us.
621 622 623
                        utils.warn("**WARNING** failed to move %s from the reject directory to the morgue." % (file))
                        return
                    utils.move(dest_file, morgue_file, perms=0660)
J
New.  
James Troup 已提交
624
                    try:
625
                        dest_fd = os.open(dest_file, os.O_RDWR|os.O_CREAT|os.O_EXCL, 0644)
J
New.  
James Troup 已提交
626 627
                    except OSError, e:
                        # Likewise
628 629
                        utils.warn("**WARNING** failed to claim %s in the reject directory." % (file))
                        return
J
New.  
James Troup 已提交
630
                else:
631
                    raise
J
New.  
James Troup 已提交
632 633
            # If we got here, we own the destination file, so we can
            # safely overwrite it.
634
            utils.move(file, dest_file, 1, perms=0660)
635
            os.close(dest_fd)
636

J
New.  
James Troup 已提交
637 638 639
    ###########################################################################

    def do_reject (self, manual = 0, reject_message = ""):
J
James Troup 已提交
640 641 642
        # 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:
643
            temp_filename = utils.temp_filename()
J
James Troup 已提交
644
            editor = os.environ.get("EDITOR","vi")
645
            answer = 'E'
J
James Troup 已提交
646 647
            while answer == 'E':
                os.system("%s %s" % (editor, temp_filename))
648 649 650 651 652
                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 已提交
653
                prompt = "[R]eject, Edit, Abandon, Quit ?"
654
                answer = "XXX"
655
                while prompt.find(answer) == -1:
656 657
                    answer = utils.our_raw_input(prompt)
                    m = re_default_answer.search(prompt)
J
James Troup 已提交
658
                    if answer == "":
659 660 661
                        answer = m.group(1)
                    answer = answer[:1].upper()
            os.unlink(temp_filename)
J
James Troup 已提交
662
            if answer == 'A':
663
                return 1
J
James Troup 已提交
664
            elif answer == 'Q':
665
                sys.exit(0)
J
James Troup 已提交
666

J
New.  
James Troup 已提交
667 668
        print "Rejecting.\n"

669 670 671
        Cnf = self.Cnf
        Subst = self.Subst
        pkg = self.pkg
J
New.  
James Troup 已提交
672

673 674
        reason_filename = pkg.changes_file[:-8] + ".reason"
        reason_filename = Cnf["Dir::Queue::Reject"] + '/' + reason_filename
J
New.  
James Troup 已提交
675 676

        # Move all the files into the reject directory
677 678
        reject_files = pkg.files.keys() + [pkg.changes_file]
        self.force_reject(reject_files)
J
New.  
James Troup 已提交
679 680 681

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

        if not manual:
687 688
            Subst["__REJECTOR_ADDRESS__"] = Cnf["Dinstall::MyEmailAddress"]
            Subst["__MANUAL_REJECT_MESSAGE__"] = ""
689
            Subst["__CC__"] = "X-DAK-Rejection: automatic (moo)\nX-Katie-Rejection: automatic (moo)"
690
            os.write(reason_fd, reject_message)
691
            reject_mail_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/queue.rejected")
J
New.  
James Troup 已提交
692 693
        else:
            # Build up the rejection email
694
            user_email_address = utils.whoami() + " <%s>" % (Cnf["Dinstall::MyAdminAddress"])
J
New.  
James Troup 已提交
695

696 697 698
            Subst["__REJECTOR_ADDRESS__"] = user_email_address
            Subst["__MANUAL_REJECT_MESSAGE__"] = reject_message
            Subst["__CC__"] = "Cc: " + Cnf["Dinstall::MyEmailAddress"]
699
            reject_mail_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/queue.rejected")
J
New.  
James Troup 已提交
700
            # Write the rejection email out as the <foo>.reason file
701
            os.write(reason_fd, reject_mail_message)
J
James Troup 已提交
702

703
        os.close(reason_fd)
J
New.  
James Troup 已提交
704 705 706

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

709 710
        self.Logger.log(["rejected", pkg.changes_file])
        return 0
J
New.  
James Troup 已提交
711 712 713 714 715 716 717

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

    # Ensure that source exists somewhere in the archive for the binary
    # upload being processed.
    #
    # (1) exact match                      => 1.0-3
R
Ryan Murray 已提交
718
    # (2) Bin-only NMU                     => 1.0-3+b1 , 1.0-3.1+b1
J
New.  
James Troup 已提交
719

720 721 722
    def source_exists (self, package, source_version, suites = ["any"]):
	okay = 1
	for suite in suites:
723
	    if suite == "any":
724 725 726
	    	que = "SELECT s.version FROM source s WHERE s.source = '%s'" % \
		    (package)
	    else:
727 728 729 730 731 732
		# source must exist in suite X, or in some other suite that's
		# mapped to X, recursively... silent-maps are counted too,
		# unreleased-maps aren't.
		maps = self.Cnf.ValueList("SuiteMappings")[:]
		maps.reverse()
		maps = [ m.split() for m in maps ]
733
		maps = [ (x[1], x[2]) for x in maps
734 735 736 737 738
				if x[0] == "map" or x[0] == "silent-map" ]
		s = [suite]
		for x in maps:
			if x[1] in s and x[0] not in s:
				s.append(x[0])
739

740
		que = "SELECT s.version FROM source s JOIN src_associations sa ON (s.id = sa.source) JOIN suite su ON (sa.suite = su.id) WHERE s.source = '%s' AND (%s)" % (package, " OR ".join(["su.suite_name = '%s'" % a for a in s]))
741 742 743
            q = self.projectB.query(que)

            # Reduce the query results to a list of version numbers
744
            ql = [ i[0] for i in q.getresult() ]
745 746

            # Try (1)
747
            if source_version in ql:
748 749 750
                continue

            # Try (2)
751
            orig_source_version = re_bin_only_nmu.sub('', source_version)
752
            if orig_source_version in ql:
753 754 755 756
                continue

            # No source found...
            okay = 0
R
Ryan Murray 已提交
757
	    break
758
	return okay
J
New.  
James Troup 已提交
759 760

    ################################################################################
761
    
J
New.  
James Troup 已提交
762
    def in_override_p (self, package, component, suite, binary_type, file):
763
        files = self.pkg.files
J
New.  
James Troup 已提交
764 765

        if binary_type == "": # must be source
766
            type = "dsc"
J
New.  
James Troup 已提交
767
        else:
768
            type = binary_type
J
New.  
James Troup 已提交
769 770 771

        # Override suite name; used for example with proposed-updates
        if self.Cnf.Find("Suite::%s::OverrideSuite" % (suite)) != "":
772
            suite = self.Cnf["Suite::%s::OverrideSuite" % (suite)]
J
New.  
James Troup 已提交
773 774

        # Avoid <undef> on unknown distributions
775
        suite_id = database.get_suite_id(suite)
J
New.  
James Troup 已提交
776
        if suite_id == -1:
777
            return None
778 779
        component_id = database.get_component_id(component)
        type_id = database.get_override_type_id(type)
J
New.  
James Troup 已提交
780 781

        q = self.projectB.query("SELECT s.section, p.priority FROM override o, section s, priority p WHERE package = '%s' AND suite = %s AND component = %s AND type = %s AND o.section = s.id AND o.priority = p.id"
782 783
                           % (package, suite_id, component_id, type_id))
        result = q.getresult()
J
New.  
James Troup 已提交
784 785
        # If checking for a source package fall back on the binary override type
        if type == "dsc" and not result:
786 787
            deb_type_id = database.get_override_type_id("deb")
            udeb_type_id = database.get_override_type_id("udeb")
788
            q = self.projectB.query("SELECT s.section, p.priority FROM override o, section s, priority p WHERE package = '%s' AND suite = %s AND component = %s AND (type = %s OR type = %s) AND o.section = s.id AND o.priority = p.id"
789 790
                               % (package, suite_id, component_id, deb_type_id, udeb_type_id))
            result = q.getresult()
J
New.  
James Troup 已提交
791 792

        # Remember the section and priority so we can check them later if appropriate
793
        if result:
794 795
            files[file]["override section"] = result[0][0]
            files[file]["override priority"] = result[0][1]
J
New.  
James Troup 已提交
796

797
        return result
J
New.  
James Troup 已提交
798 799 800 801 802

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

    def reject (self, str, prefix="Rejected: "):
        if str:
803 804 805
            # Unlike other rejects we add new lines first to avoid trailing
            # new lines when this message is passed back up to a caller.
            if self.reject_message:
806 807
                self.reject_message += "\n"
            self.reject_message += prefix + str
J
New.  
James Troup 已提交
808

809 810
    ################################################################################

811 812 813 814
    def get_anyversion(self, query_result, suite):
        anyversion=None
        anysuite = [suite] + self.Cnf.ValueList("Suite::%s::VersionChecks::Enhances" % (suite))
        for (v, s) in query_result:
815
            if s in [ x.lower() for x in anysuite ]:
816 817 818 819 820 821
                if not anyversion or apt_pkg.VersionCompare(anyversion, v) <= 0:
                    anyversion=v
        return anyversion

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

822 823 824 825 826 827 828
    def cross_suite_version_check(self, query_result, file, new_version):
        """Ensure versions are newer than existing packages in target
        suites and that cross-suite version checking rules as
        set out in the conf file are satisfied."""

        # Check versions for each target suite
        for target_suite in self.pkg.changes["distribution"].keys():
829 830
            must_be_newer_than = [ i.lower for i in self.Cnf.ValueList("Suite::%s::VersionChecks::MustBeNewerThan" % (target_suite)) ]
            must_be_older_than = [ i.lower for i in self.Cnf.ValueList("Suite::%s::VersionChecks::MustBeOlderThan" % (target_suite)) ]
831 832
            # Enforce "must be newer than target suite" even if conffile omits it
            if target_suite not in must_be_newer_than:
833
                must_be_newer_than.append(target_suite)
834
            for entry in query_result:
835 836
                existent_version = entry[0]
                suite = entry[1]
837
                if suite in must_be_newer_than and \
838
                   apt_pkg.VersionCompare(new_version, existent_version) < 1:
839
                    self.reject("%s: old version (%s) in %s >= new version (%s) targeted at %s." % (file, existent_version, suite, new_version, target_suite))
840
                if suite in must_be_older_than and \
841
                   apt_pkg.VersionCompare(new_version, existent_version) > -1:
842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861
                    ch = self.pkg.changes
                    cansave = 0
                    if ch.get('distribution-version', {}).has_key(suite):
                        # we really use the other suite, ignoring the conflicting one ...
                        addsuite = ch["distribution-version"][suite]
                    
                        add_version = self.get_anyversion(query_result, addsuite)
                        target_version = self.get_anyversion(query_result, target_suite)
                    
                        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
                            self.reject("%s is mapped to, but not enhanced by %s - adding anyways" % (suite, addsuite), "Warning: ")
862 863
                            self.pkg.changes.setdefault("propdistribution", {})
                            self.pkg.changes["propdistribution"][addsuite] = 1
864 865 866 867 868 869 870 871
                            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.
                            self.reject("Won't propogate NEW packages.")
                        elif apt_pkg.VersionCompare(new_version, add_version) < 0:
                            # propogation would be redundant. no need to reject though.
872
                            self.reject("ignoring versionconflict: %s: old version (%s) in %s <= new version (%s) targeted at %s." % (file, existent_version, suite, new_version, target_suite), "Warning: ")
873 874
                            cansave = 1
                        elif apt_pkg.VersionCompare(new_version, add_version) > 0 and \
875
                             apt_pkg.VersionCompare(add_version, target_version) >= 0:
876
                            # propogate!!
877 878 879
                            self.reject("Propogating upload to %s" % (addsuite), "Warning: ")
                            self.pkg.changes.setdefault("propdistribution", {})
                            self.pkg.changes["propdistribution"][addsuite] = 1
880 881 882
                            cansave = 1
                
                    if not cansave:
883
                        self.reject("%s: old version (%s) in %s <= new version (%s) targeted at %s." % (file, existent_version, suite, new_version, target_suite))
884 885 886 887

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

    def check_binary_against_db(self, file):
888 889
        self.reject_message = ""
        files = self.pkg.files
J
New.  
James Troup 已提交
890

891 892 893 894 895 896 897
        # Ensure version is sane
        q = self.projectB.query("""
SELECT b.version, su.suite_name FROM binaries b, bin_associations ba, suite su,
                                     architecture a
 WHERE b.package = '%s' AND (a.arch_string = '%s' OR a.arch_string = 'all')
   AND ba.bin = b.id AND ba.suite = su.id AND b.architecture = a.id"""
                                % (files[file]["package"],
898 899
                                   files[file]["architecture"]))
        self.cross_suite_version_check(q.getresult(), file, files[file]["version"])
900

J
New.  
James Troup 已提交
901
        # Check for any existing copies of the file
902 903 904 905 906 907 908 909
        q = self.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():
910
            self.reject("%s: can not overwrite existing copy already in the archive." % (file))
J
New.  
James Troup 已提交
911

912
        return self.reject_message
J
New.  
James Troup 已提交
913 914 915 916

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

    def check_source_against_db(self, file):
917 918
        self.reject_message = ""
        dsc = self.pkg.dsc
J
New.  
James Troup 已提交
919

920 921 922
        # Ensure version is sane
        q = self.projectB.query("""
SELECT s.version, su.suite_name FROM source s, src_associations sa, suite su
923 924
 WHERE s.source = '%s' AND sa.source = s.id AND sa.suite = su.id""" % (dsc.get("source")))
        self.cross_suite_version_check(q.getresult(), file, dsc.get("version"))
925

926
        return self.reject_message
J
New.  
James Troup 已提交
927 928 929

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

930 931 932 933 934
    # **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
J
James Troup 已提交
935
    # ensure you haven't just tried to dereference the deleted entry.
936 937
    # **WARNING**

J
New.  
James Troup 已提交
938
    def check_dsc_against_db(self, file):
939 940 941 942 943
        self.reject_message = ""
        files = self.pkg.files
        dsc_files = self.pkg.dsc_files
        legacy_source_untouchable = self.pkg.legacy_source_untouchable
        self.pkg.orig_tar_gz = None
J
New.  
James Troup 已提交
944 945 946 947

        # 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.
948 949
        # The ordering on the select is needed to pick the newest orig
        # when it exists in multiple places.
J
New.  
James Troup 已提交
950
        for dsc_file in dsc_files.keys():
951
            found = None
J
New.  
James Troup 已提交
952
            if files.has_key(dsc_file):
953 954
                actual_md5 = files[dsc_file]["md5sum"]
                actual_size = int(files[dsc_file]["size"])
J
New.  
James Troup 已提交
955 956
                found = "%s in incoming" % (dsc_file)
                # Check the file does not already exist in the archive
957
                q = self.projectB.query("SELECT f.size, f.md5sum, l.path, f.filename FROM files f, location l WHERE f.filename LIKE '%%%s%%' AND l.id = f.location ORDER BY f.id DESC" % (dsc_file))
958
                ql = q.getresult()
959 960
                # Strip out anything that isn't '%s' or '/%s$'
                for i in ql:
961
                    if i[3] != dsc_file and i[3][-(len(dsc_file)+1):] != '/'+dsc_file:
962
                        ql.remove(i)
963

964
                # "[dak] has not broken them.  [dak] has fixed a
J
New.  
James Troup 已提交
965 966 967 968 969 970 971 972
                # 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

973
                if ql:
974
                    # Ignore exact matches for .orig.tar.gz
975
                    match = 0
976
                    if dsc_file.endswith(".orig.tar.gz"):
977
                        for i in ql:
978 979
                            if files.has_key(dsc_file) and \
                               int(files[dsc_file]["size"]) == int(i[0]) and \
980
                               files[dsc_file]["md5sum"] == i[1]:
981 982 983 984
                                self.reject("ignoring %s, since it's already in the archive." % (dsc_file), "Warning: ")
                                del files[dsc_file]
                                self.pkg.orig_tar_gz = i[2] + i[3]
                                match = 1
985 986

                    if not match:
987
                        self.reject("can not overwrite existing copy of '%s' already in the archive." % (dsc_file))
988
            elif dsc_file.endswith(".orig.tar.gz"):
J
New.  
James Troup 已提交
989
                # Check in the pool
990 991
                q = self.projectB.query("SELECT l.path, f.filename, l.type, f.id, l.id FROM files f, location l WHERE f.filename LIKE '%%%s%%' AND l.id = f.location" % (dsc_file))
                ql = q.getresult()
992 993 994
                # Strip out anything that isn't '%s' or '/%s$'
                for i in ql:
                    if i[1] != dsc_file and i[1][-(len(dsc_file)+1):] != '/'+dsc_file:
995
                        ql.remove(i)
J
New.  
James Troup 已提交
996

997
                if ql:
998 999 1000
                    # 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 已提交
1001 1002 1003 1004 1005

                    x = ql[0]; # default to something sane in case we don't match any or have only one

                    if len(ql) > 1:
                        for i in ql:
1006
                            old_file = i[0] + i[1]
1007
                            old_file_fh = utils.open_file(old_file)
1008
                            actual_md5 = apt_pkg.md5sum(old_file_fh)
1009
                            old_file_fh.close()
1010
                            actual_size = os.stat(old_file)[stat.ST_SIZE]
J
New.  
James Troup 已提交
1011
                            if actual_md5 == dsc_files[dsc_file]["md5sum"] and actual_size == int(dsc_files[dsc_file]["size"]):
1012
                                x = i
J
New.  
James Troup 已提交
1013
                            else:
1014
                                legacy_source_untouchable[i[3]] = ""
J
New.  
James Troup 已提交
1015

1016
                    old_file = x[0] + x[1]
1017
                    old_file_fh = utils.open_file(old_file)
1018
                    actual_md5 = apt_pkg.md5sum(old_file_fh)
1019
                    old_file_fh.close()
1020 1021 1022
                    actual_size = os.stat(old_file)[stat.ST_SIZE]
                    found = old_file
                    suite_type = x[2]
J
New.  
James Troup 已提交
1023
                    dsc_files[dsc_file]["files id"] = x[3]; # need this for updating dsc_files in install()
1024
                    # See install() in process-accepted...
1025 1026
                    self.pkg.orig_tar_id = x[3]
                    self.pkg.orig_tar_gz = old_file
J
New.  
James Troup 已提交
1027
                    if suite_type == "legacy" or suite_type == "legacy-mixed":
1028
                        self.pkg.orig_tar_location = "legacy"
J
New.  
James Troup 已提交
1029
                    else:
1030
                        self.pkg.orig_tar_location = x[4]
J
New.  
James Troup 已提交
1031
                else:
1032
                    # Not there? Check the queue directories...
J
New.  
James Troup 已提交
1033

1034
                    in_unchecked = os.path.join(self.Cnf["Dir::Queue::Unchecked"],dsc_file)
1035
                    # See process_it() in 'dak process-unchecked' for explanation of this
1036 1037 1038
		    # in_unchecked check dropped by ajt 2007-08-28, how did that
		    # ever make sense?
                    if os.path.exists(in_unchecked) and False:
1039
                        return (self.reject_message, in_unchecked)
J
New.  
James Troup 已提交
1040
                    else:
1041
                        for dir in [ "Accepted", "New", "Byhand", "ProposedUpdates", "OldProposedUpdates" ]:
1042
                            in_otherdir = os.path.join(self.Cnf["Dir::Queue::%s" % (dir)],dsc_file)
1043
                            if os.path.exists(in_otherdir):
1044
                                in_otherdir_fh = utils.open_file(in_otherdir)
1045
                                actual_md5 = apt_pkg.md5sum(in_otherdir_fh)
1046
                                in_otherdir_fh.close()
1047 1048 1049
                                actual_size = os.stat(in_otherdir)[stat.ST_SIZE]
                                found = in_otherdir
                                self.pkg.orig_tar_gz = in_otherdir
1050 1051

                    if not found:
1052 1053 1054
                        self.reject("%s refers to %s, but I can't find it in the queue or in the pool." % (file, dsc_file))
                        self.pkg.orig_tar_gz = -1
                        continue
J
New.  
James Troup 已提交
1055
            else:
1056 1057
                self.reject("%s refers to %s, but I can't find it in the queue." % (file, dsc_file))
                continue
J
New.  
James Troup 已提交
1058
            if actual_md5 != dsc_files[dsc_file]["md5sum"]:
1059
                self.reject("md5sum for %s doesn't match %s." % (found, file))
J
New.  
James Troup 已提交
1060
            if actual_size != int(dsc_files[dsc_file]["size"]):
1061
                self.reject("size for %s doesn't match %s." % (found, file))
J
New.  
James Troup 已提交
1062

1063
        return (self.reject_message, None)
J
James Troup 已提交
1064 1065

    def do_query(self, q):
1066 1067 1068 1069 1070 1071
        sys.stderr.write("query: \"%s\" ... " % (q))
        before = time.time()
        r = self.projectB.query(q)
        time_diff = time.time()-before
        sys.stderr.write("took %.3f seconds.\n" % (time_diff))
        return r