queue.py 47.7 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

J
New.  
James Troup 已提交
35 36 37 38 39 40
###############################################################################

# Convenience wrapper to carry around all the package information in

class Pkg:
    def __init__(self, **kwds):
41
        self.__dict__.update(kwds)
J
New.  
James Troup 已提交
42 43

    def update(self, **kwds):
44
        self.__dict__.update(kwds)
J
New.  
James Troup 已提交
45 46 47 48 49 50

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

class nmu_p:
    # Read in the group maintainer override file
    def __init__ (self, Cnf):
51 52
        self.group_maint = {}
        self.Cnf = Cnf
J
New.  
James Troup 已提交
53
        if Cnf.get("Dinstall::GroupOverrideFilename"):
54 55
            filename = Cnf["Dir::Override"] + Cnf["Dinstall::GroupOverrideFilename"]
            file = utils.open_file(filename)
J
New.  
James Troup 已提交
56
            for line in file.readlines():
57
                line = utils.re_comments.sub('', line).lower().strip()
J
New.  
James Troup 已提交
58
                if line != "":
59 60
                    self.group_maint[line] = 1
            file.close()
J
New.  
James Troup 已提交
61 62

    def is_an_nmu (self, pkg):
63 64 65
        Cnf = self.Cnf
        changes = pkg.changes
        dsc = pkg.dsc
J
New.  
James Troup 已提交
66

67
        i = utils.fix_maintainer (dsc.get("maintainer",
68 69
                                          Cnf["Dinstall::MyEmailAddress"]).lower())
        (dsc_rfc822, dsc_rfc2047, dsc_name, dsc_email) = i
J
New.  
James Troup 已提交
70
        # changes["changedbyname"] == dsc_name is probably never true, but better safe than sorry
71 72
        if dsc_name == changes["maintainername"].lower() and \
           (changes["changedby822"] == "" or changes["changedbyname"].lower() == dsc_name):
73
            return 0
J
New.  
James Troup 已提交
74 75

        if dsc.has_key("uploaders"):
76 77
            uploaders = dsc["uploaders"].lower().split(",")
            uploadernames = {}
J
New.  
James Troup 已提交
78
            for i in uploaders:
79 80
                (rfc822, rfc2047, name, email) = utils.fix_maintainer (i.strip())
                uploadernames[name] = ""
81
            if uploadernames.has_key(changes["changedbyname"].lower()):
82
                return 0
J
New.  
James Troup 已提交
83 84

        # Some group maintained packages (e.g. Debian QA) are never NMU's
85
        if self.group_maint.has_key(changes["maintaineremail"].lower()):
86
            return 0
J
New.  
James Troup 已提交
87

88
        return 1
J
New.  
James Troup 已提交
89 90 91

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

92
class Upload:
J
New.  
James Troup 已提交
93 94

    def __init__(self, Cnf):
95
        self.Cnf = Cnf
J
New.  
James Troup 已提交
96
        # Read in the group-maint override file
97 98 99
        self.nmu = nmu_p(Cnf)
        self.accept_count = 0
        self.accept_bytes = 0L
J
New.  
James Troup 已提交
100
        self.pkg = Pkg(changes = {}, dsc = {}, dsc_files = {}, files = {},
101
                       legacy_source_untouchable = {})
J
New.  
James Troup 已提交
102 103

        # Initialize the substitution template mapping global
104 105 106 107
        Subst = self.Subst = {}
        Subst["__ADMIN_ADDRESS__"] = Cnf["Dinstall::MyAdminAddress"]
        Subst["__BUG_SERVER__"] = Cnf["Dinstall::BugServer"]
        Subst["__DISTRO__"] = Cnf["Dinstall::MyDistribution"]
108
        Subst["__DAK_ADDRESS__"] = Cnf["Dinstall::MyEmailAddress"]
J
New.  
James Troup 已提交
109

110
        self.projectB = pg.connect(Cnf["DB::Name"], Cnf["DB::Host"], int(Cnf["DB::Port"]))
111
        database.init(Cnf, self.projectB)
J
New.  
James Troup 已提交
112 113 114 115 116

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

    def init_vars (self):
        for i in [ "changes", "dsc", "files", "dsc_files", "legacy_source_untouchable" ]:
117 118 119 120
            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 已提交
121 122 123 124

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

    def update_vars (self):
125
        dump_filename = self.pkg.changes_file[:-8]+".dak"
126 127
        dump_file = utils.open_file(dump_filename)
        p = cPickle.Unpickler(dump_file)
J
New.  
James Troup 已提交
128
        for i in [ "changes", "dsc", "files", "dsc_files", "legacy_source_untouchable" ]:
129
            exec "self.pkg.%s.update(p.load());" % (i)
J
New.  
James Troup 已提交
130
        for i in [ "orig_tar_id", "orig_tar_location" ]:
131 132
            exec "self.pkg.%s = p.load();" % (i)
        dump_file.close()
J
New.  
James Troup 已提交
133 134 135

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

136 137 138
    # 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 已提交
139 140 141 142

    def dump_vars(self, dest_dir):
        for i in [ "changes", "dsc", "files", "dsc_files",
                   "legacy_source_untouchable", "orig_tar_id", "orig_tar_location" ]:
143
            exec "%s = self.pkg.%s;" % (i,i)
144
        dump_filename = os.path.join(dest_dir,self.pkg.changes_file[:-8] + ".dak")
145
        dump_file = utils.open_file(dump_filename, 'w')
J
James Troup 已提交
146
        try:
147
            os.chmod(dump_filename, 0660)
J
James Troup 已提交
148 149
        except OSError, e:
            if errno.errorcode[e.errno] == 'EPERM':
150
                perms = stat.S_IMODE(os.stat(dump_filename)[stat.ST_MODE])
J
James Troup 已提交
151
                if perms & stat.S_IROTH:
152
                    utils.fubar("%s is world readable and chmod failed." % (dump_filename))
J
James Troup 已提交
153
            else:
154
                raise
J
James Troup 已提交
155

156
        p = cPickle.Pickler(dump_file, 1)
J
New.  
James Troup 已提交
157
        for i in [ "d_changes", "d_dsc", "d_files", "d_dsc_files" ]:
158
            exec "%s = {}" % i
J
New.  
James Troup 已提交
159 160
        ## files
        for file in files.keys():
161
            d_files[file] = {}
J
New.  
James Troup 已提交
162 163 164
            for i in [ "package", "version", "architecture", "type", "size",
                       "md5sum", "component", "location id", "source package",
                       "source version", "maintainer", "dbtype", "files id",
165
                       "new", "section", "priority", "othercomponents",
166
                       "pool name", "original component" ]:
J
New.  
James Troup 已提交
167
                if files[file].has_key(i):
168
                    d_files[file][i] = files[file][i]
J
New.  
James Troup 已提交
169 170
        ## changes
        # Mandatory changes fields
171 172 173 174 175
        for i in [ "distribution", "source", "architecture", "version",
                   "maintainer", "urgency", "fingerprint", "changedby822",
                   "changedby2047", "changedbyname", "maintainer822",
                   "maintainer2047", "maintainername", "maintaineremail",
                   "closes", "changes" ]:
176
            d_changes[i] = changes[i]
J
New.  
James Troup 已提交
177
        # Optional changes fields
J
James Troup 已提交
178
        for i in [ "changed-by", "filecontents", "format", "process-new note", "adv id", "distribution-version" ]:
J
James Troup 已提交
179
            if changes.has_key(i):
180
                d_changes[i] = changes[i]
J
New.  
James Troup 已提交
181
        ## dsc
182 183
        for i in [ "source", "version", "maintainer", "fingerprint",
                   "uploaders", "bts changelog" ]:
J
New.  
James Troup 已提交
184
            if dsc.has_key(i):
185
                d_dsc[i] = dsc[i]
J
New.  
James Troup 已提交
186 187
        ## dsc_files
        for file in dsc_files.keys():
188
            d_dsc_files[file] = {}
J
New.  
James Troup 已提交
189 190
            # Mandatory dsc_files fields
            for i in [ "size", "md5sum" ]:
191
                d_dsc_files[file][i] = dsc_files[file][i]
J
New.  
James Troup 已提交
192 193 194
            # Optional dsc_files fields
            for i in [ "files id" ]:
                if dsc_files[file].has_key(i):
195
                    d_dsc_files[file][i] = dsc_files[file][i]
J
New.  
James Troup 已提交
196 197 198

        for i in [ d_changes, d_dsc, d_files, d_dsc_files,
                   legacy_source_untouchable, orig_tar_id, orig_tar_location ]:
199 200
            p.dump(i)
        dump_file.close()
J
New.  
James Troup 已提交
201 202 203 204 205 206

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

    # Set up the per-package template substitution mappings

    def update_subst (self, reject_message = ""):
207 208
        Subst = self.Subst
        changes = self.pkg.changes
209
        # If 'dak process-unchecked' crashed out in the right place, architecture may still be a string.
J
New.  
James Troup 已提交
210
        if not changes.has_key("architecture") or not isinstance(changes["architecture"], DictType):
211
            changes["architecture"] = { "Unknown" : "" }
212 213
        # and maintainer2047 may not exist.
        if not changes.has_key("maintainer2047"):
214
            changes["maintainer2047"] = self.Cnf["Dinstall::MyEmailAddress"]
J
New.  
James Troup 已提交
215

216 217 218
        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 已提交
219 220 221

        # 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"]):
222
            Subst["__MAINTAINER_FROM__"] = changes["changedby2047"]
223
            Subst["__MAINTAINER_TO__"] = "%s, %s" % (changes["changedby2047"],
224 225
                                                     changes["maintainer2047"])
            Subst["__MAINTAINER__"] = changes.get("changed-by", "Unknown")
J
New.  
James Troup 已提交
226
        else:
227 228 229
            Subst["__MAINTAINER_FROM__"] = changes["maintainer2047"]
            Subst["__MAINTAINER_TO__"] = changes["maintainer2047"]
            Subst["__MAINTAINER__"] = changes.get("maintainer", "Unknown")
J
New.  
James Troup 已提交
230
        if self.Cnf.has_key("Dinstall::TrackingServer") and changes.has_key("source"):
231
            Subst["__MAINTAINER_TO__"] += "\nBcc: %s@%s" % (changes["source"], self.Cnf["Dinstall::TrackingServer"])
J
New.  
James Troup 已提交
232

233 234
        # Apply any global override of the Maintainer field
        if self.Cnf.get("Dinstall::OverrideMaintainer"):
235 236
            Subst["__MAINTAINER_TO__"] = self.Cnf["Dinstall::OverrideMaintainer"]
            Subst["__MAINTAINER_FROM__"] = self.Cnf["Dinstall::OverrideMaintainer"]
237

238 239 240
        Subst["__REJECT_MESSAGE__"] = reject_message
        Subst["__SOURCE__"] = changes.get("source", "Unknown")
        Subst["__VERSION__"] = changes.get("version", "Unknown")
J
New.  
James Troup 已提交
241 242 243 244

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

    def build_summaries(self):
245 246
        changes = self.pkg.changes
        files = self.pkg.files
J
New.  
James Troup 已提交
247

248
        byhand = summary = new = ""
J
New.  
James Troup 已提交
249 250 251 252

        # 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):
253
            changes["distribution"] = {}
J
New.  
James Troup 已提交
254

255
        override_summary ="";
256 257
        file_keys = files.keys()
        file_keys.sort()
J
New.  
James Troup 已提交
258 259 260
        for file in file_keys:
            if files[file].has_key("byhand"):
                byhand = 1
261
                summary += file + " byhand\n"
J
New.  
James Troup 已提交
262 263
            elif files[file].has_key("new"):
                new = 1
264
                summary += "(new) %s %s %s\n" % (file, files[file]["priority"], files[file]["section"])
J
New.  
James Troup 已提交
265
                if files[file].has_key("othercomponents"):
266
                    summary += "WARNING: Already present in %s distribution.\n" % (files[file]["othercomponents"])
J
New.  
James Troup 已提交
267
                if files[file]["type"] == "deb":
268
                    deb_fh = utils.open_file(file)
269
                    summary += apt_pkg.ParseSection(apt_inst.debExtractControl(deb_fh))["Description"] + '\n'
270
                    deb_fh.close()
J
New.  
James Troup 已提交
271
            else:
272
                files[file]["pool name"] = utils.poolify (changes.get("source",""), files[file]["component"])
J
New.  
James Troup 已提交
273
                destination = self.Cnf["Dir::PoolRoot"] + files[file]["pool name"] + file
274
                summary += file + "\n  to " + destination + "\n"
275 276
		if not files[file].has_key("type"):
		    files[file]["type"] = "unknown"
277
                if files[file]["type"] in ["deb", "udeb", "dsc"]:
278 279 280 281 282
                    # (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 已提交
283

284
        short_summary = summary
J
New.  
James Troup 已提交
285 286

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

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

292 293
        summary += "\n\nOverride entries for your package:\n" + override_summary + "\n"

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

296
        return (summary, short_summary)
J
New.  
James Troup 已提交
297 298 299

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

300
    def close_bugs (self, summary, action):
301 302 303
        changes = self.pkg.changes
        Subst = self.Subst
        Cnf = self.Cnf
J
New.  
James Troup 已提交
304

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

307
        if not bugs:
308
            return summary
J
New.  
James Troup 已提交
309

310
        bugs.sort()
J
New.  
James Troup 已提交
311
        if not self.nmu.is_an_nmu(self.pkg):
312 313
            if changes["distribution"].has_key("experimental"):
		# tag bugs as fixed-in-experimental for uploads to experimental
314 315
		summary += "Setting bugs to severity fixed: "
		control_message = ""
316
		for bug in bugs:
317 318
		    summary += "%s " % (bug)
		    control_message += "tag %s + fixed-in-experimental\n" % (bug)
319
		if action and control_message != "":
320
		    Subst["__CONTROL_MESSAGE__"] = control_message
321
		    mail_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/process-unchecked.bug-experimental-fixed")
322
		    utils.send_mail (mail_message)
323
		if action:
324
		    self.Logger.log(["setting bugs to fixed"]+bugs)
325 326 327


	    else:
328
		summary += "Closing bugs: "
329
		for bug in bugs:
330
		    summary += "%s " % (bug)
331
		    if action:
332
			Subst["__BUG_NUMBER__"] = bug
333 334
			if changes["distribution"].has_key("stable"):
			    Subst["__STABLE_WARNING__"] = """
335 336 337 338
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
339
distribution."""
340
		        else:
341
			    Subst["__STABLE_WARNING__"] = ""
342
			    mail_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/process-unchecked.bug-close")
343
			    utils.send_mail (mail_message)
344
                if action:
345
                    self.Logger.log(["closing bugs"]+bugs)
346 347

	else:                     # NMU
348 349
            summary += "Setting bugs to severity fixed: "
            control_message = ""
J
New.  
James Troup 已提交
350
            for bug in bugs:
351 352
                summary += "%s " % (bug)
                control_message += "tag %s + fixed\n" % (bug)
J
New.  
James Troup 已提交
353
            if action and control_message != "":
354
                Subst["__CONTROL_MESSAGE__"] = control_message
355
                mail_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/process-unchecked.bug-nmu-fixed")
356
                utils.send_mail (mail_message)
J
New.  
James Troup 已提交
357
            if action:
358 359 360
                self.Logger.log(["setting bugs to fixed"]+bugs)
        summary += "\n"
        return summary
361 362 363 364

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

    def announce (self, short_summary, action):
365 366 367
        Subst = self.Subst
        Cnf = self.Cnf
        changes = self.pkg.changes
368 369 370

        # 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"):
371
            return ""
J
New.  
James Troup 已提交
372

373 374 375
        lists_done = {}
        summary = ""
        Subst["__SHORT_SUMMARY__"] = short_summary
376 377

        for dist in changes["distribution"].keys():
378
            list = Cnf.Find("Suite::%s::Announce" % (dist))
379
            if list == "" or lists_done.has_key(list):
380 381 382
                continue
            lists_done[list] = 1
            summary += "Announcing to %s\n" % (list)
383 384

            if action:
385
                Subst["__ANNOUNCE_LIST_ADDRESS__"] = list
386
                if Cnf.get("Dinstall::TrackingServer") and changes["architecture"].has_key("source"):
387
                    Subst["__ANNOUNCE_LIST_ADDRESS__"] = Subst["__ANNOUNCE_LIST_ADDRESS__"] + "\nBcc: %s@%s" % (changes["source"], Cnf["Dinstall::TrackingServer"])
388
                mail_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/process-unchecked.announce")
389
                utils.send_mail (mail_message)
390

J
James Troup 已提交
391
        if Cnf.FindB("Dinstall::CloseBugs"):
392
            summary = self.close_bugs(summary, action)
393

394
        return summary
J
New.  
James Troup 已提交
395 396 397 398

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

    def accept (self, summary, short_summary):
399 400 401 402 403 404
        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 已提交
405 406

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

409
        self.dump_vars(Cnf["Dir::Queue::Accepted"])
J
New.  
James Troup 已提交
410 411

        # Move all the files into the accepted directory
412 413
        utils.move(changes_file, Cnf["Dir::Queue::Accepted"])
        file_keys = files.keys()
J
New.  
James Troup 已提交
414
        for file in file_keys:
415
            utils.move(file, Cnf["Dir::Queue::Accepted"])
416
            self.accept_bytes += float(files[file]["size"])
417
        self.accept_count += 1
J
New.  
James Troup 已提交
418 419 420 421

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

428 429 430 431 432 433 434 435 436 437 438 439

        ## 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"],
440 441 442 443
                                                    dotprefix=1, perms=0644)
                version_history = utils.open_file(temp_filename, 'w')
                version_history.write(dsc["bts changelog"])
                version_history.close()
444
                filename = "%s/%s" % (Cnf["Dir::Queue::BTSVersionTrack"],
445 446
                                      changes_file[:-8]+".versions")
                os.rename(temp_filename, filename)
447 448 449

            # Write out the binary -> source mapping.
            temp_filename = utils.temp_filename(Cnf["Dir::Queue::BTSVersionTrack"],
450 451
                                                dotprefix=1, perms=0644)
            debinfo = utils.open_file(temp_filename, 'w')
452
            for file in file_keys:
453
                f = files[file]
454 455 456
                if f["type"] == "deb":
                    line = " ".join([f["package"], f["version"],
                                     f["architecture"], f["source package"],
457 458 459
                                     f["source version"]])
                    debinfo.write(line+"\n")
            debinfo.close()
460
            filename = "%s/%s" % (Cnf["Dir::Queue::BTSVersionTrack"],
461 462
                                  changes_file[:-8]+".debinfo")
            os.rename(temp_filename, filename)
463

464 465 466 467 468
        self.queue_build("accepted", Cnf["Dir::Queue::Accepted"])

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

    def queue_build (self, queue, path):
469 470 471 472 473 474 475 476
        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()

477
        ## Special support to enable clean auto-building of queued packages
478
        queue_id = database.get_or_set_queue_id(queue)
479

480
        self.projectB.query("BEGIN WORK")
481
        for suite in changes["distribution"].keys():
482
            if suite not in Cnf.ValueList("Dinstall::QueueBuildSuites"):
483
                continue
484
            suite_id = database.get_suite_id(suite)
485
            dest_dir = Cnf["Dir::QueueBuild"]
486
            if Cnf.FindB("Dinstall::SecurityQueueBuild"):
487
                dest_dir = os.path.join(dest_dir, suite)
488
            for file in file_keys:
489 490
                src = os.path.join(path, file)
                dest = os.path.join(dest_dir, file)
491
                if Cnf.FindB("Dinstall::SecurityQueueBuild"):
492
                    # Copy it since the original won't be readable by www-data
493
                    utils.copy(src, dest)
494 495
                else:
                    # Create a symlink to it
496
                    os.symlink(src, dest)
J
James Troup 已提交
497
                # Add it to the list of packages for later processing by apt-ftparchive
498
                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 已提交
499 500
            # If the .orig.tar.gz is in the pool, create a symlink to
            # it (if one doesn't already exist)
501 502 503
            if self.pkg.orig_tar_id:
                # Determine the .orig.tar.gz file name
                for dsc_file in self.pkg.dsc_files.keys():
504
                    if dsc_file.endswith(".orig.tar.gz"):
505 506
                        filename = dsc_file
                dest = os.path.join(dest_dir, filename)
507 508 509
                # If it doesn't exist, create a symlink
                if not os.path.exists(dest):
                    # Find the .orig.tar.gz in the pool
510 511
                    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()
512
                    if not ql:
513 514 515
                        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 已提交
516
                    # Add it to the list of packages for later processing by apt-ftparchive
517
                    self.projectB.query("INSERT INTO queue_build (suite, queue, filename, in_queue) VALUES (%s, %s, '%s', 't')" % (suite_id, queue_id, dest))
518 519
                # if it does, update things to ensure it's not removed prematurely
                else:
520
                    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 已提交
521

522
        self.projectB.query("COMMIT WORK")
523

J
New.  
James Troup 已提交
524 525 526
    ###########################################################################

    def check_override (self):
527 528 529 530
        Subst = self.Subst
        changes = self.pkg.changes
        files = self.pkg.files
        Cnf = self.Cnf
J
New.  
James Troup 已提交
531

532 533 534 535 536 537 538
        # 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"]:
539
            return
J
New.  
James Troup 已提交
540

541 542 543
        summary = ""
        file_keys = files.keys()
        file_keys.sort()
J
James Troup 已提交
544
        for file in file_keys:
J
New.  
James Troup 已提交
545
            if not files[file].has_key("new") and files[file]["type"] == "deb":
546 547
                section = files[file]["section"]
                override_section = files[file]["override section"]
548
                if section.lower() != override_section.lower() and section != "-":
J
New.  
James Troup 已提交
549
                    # Ignore this; it's a common mistake and not worth whining about
550
                    if section.lower() == "non-us/main" and override_section.lower() == "non-us":
551 552 553 554
                        continue
                    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 已提交
555
                if priority != override_priority and priority != "-":
556
                    summary += "%s: package says priority is %s, override says %s.\n" % (file, priority, override_priority)
J
New.  
James Troup 已提交
557 558

        if summary == "":
559
            return
J
New.  
James Troup 已提交
560

561
        Subst["__SUMMARY__"] = summary
562
        mail_message = utils.TemplateSubst(Subst,self.Cnf["Dir::Templates"]+"/process-unchecked.override-disparity")
563
        utils.send_mail(mail_message)
J
New.  
James Troup 已提交
564 565 566

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

567 568 569 570 571
    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 已提交
572 573 574 575 576 577

        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:
578 579
                continue
            dest_file = os.path.join(Cnf["Dir::Queue::Reject"], file)
J
New.  
James Troup 已提交
580
            try:
581
                dest_fd = os.open(dest_file, os.O_RDWR|os.O_CREAT|os.O_EXCL, 0644)
J
New.  
James Troup 已提交
582 583 584
            except OSError, e:
                # File exists?  Let's try and move it to the morgue
                if errno.errorcode[e.errno] == 'EEXIST':
585
                    morgue_file = os.path.join(Cnf["Dir::Morgue"],Cnf["Dir::MorgueReject"],file)
J
New.  
James Troup 已提交
586
                    try:
587
                        morgue_file = utils.find_next_free(morgue_file)
J
New.  
James Troup 已提交
588 589 590
                    except utils.tried_too_hard_exc:
                        # Something's either gone badly Pete Tong, or
                        # someone is trying to exploit us.
591 592 593
                        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 已提交
594
                    try:
595
                        dest_fd = os.open(dest_file, os.O_RDWR|os.O_CREAT|os.O_EXCL, 0644)
J
New.  
James Troup 已提交
596 597
                    except OSError, e:
                        # Likewise
598 599
                        utils.warn("**WARNING** failed to claim %s in the reject directory." % (file))
                        return
J
New.  
James Troup 已提交
600
                else:
601
                    raise
J
New.  
James Troup 已提交
602 603
            # If we got here, we own the destination file, so we can
            # safely overwrite it.
604
            utils.move(file, dest_file, 1, perms=0660)
605
            os.close(dest_fd)
606

J
New.  
James Troup 已提交
607 608 609
    ###########################################################################

    def do_reject (self, manual = 0, reject_message = ""):
J
James Troup 已提交
610 611 612
        # 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:
613
            temp_filename = utils.temp_filename()
J
James Troup 已提交
614
            editor = os.environ.get("EDITOR","vi")
615
            answer = 'E'
J
James Troup 已提交
616 617
            while answer == 'E':
                os.system("%s %s" % (editor, temp_filename))
618 619 620 621 622
                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 已提交
623
                prompt = "[R]eject, Edit, Abandon, Quit ?"
624
                answer = "XXX"
625
                while prompt.find(answer) == -1:
626 627
                    answer = utils.our_raw_input(prompt)
                    m = re_default_answer.search(prompt)
J
James Troup 已提交
628
                    if answer == "":
629 630 631
                        answer = m.group(1)
                    answer = answer[:1].upper()
            os.unlink(temp_filename)
J
James Troup 已提交
632
            if answer == 'A':
633
                return 1
J
James Troup 已提交
634
            elif answer == 'Q':
635
                sys.exit(0)
J
James Troup 已提交
636

J
New.  
James Troup 已提交
637 638
        print "Rejecting.\n"

639 640 641
        Cnf = self.Cnf
        Subst = self.Subst
        pkg = self.pkg
J
New.  
James Troup 已提交
642

643 644
        reason_filename = pkg.changes_file[:-8] + ".reason"
        reason_filename = Cnf["Dir::Queue::Reject"] + '/' + reason_filename
J
New.  
James Troup 已提交
645 646

        # Move all the files into the reject directory
647 648
        reject_files = pkg.files.keys() + [pkg.changes_file]
        self.force_reject(reject_files)
J
New.  
James Troup 已提交
649 650 651

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

        if not manual:
657 658
            Subst["__REJECTOR_ADDRESS__"] = Cnf["Dinstall::MyEmailAddress"]
            Subst["__MANUAL_REJECT_MESSAGE__"] = ""
659
            Subst["__CC__"] = "X-DAK-Rejection: automatic (moo)\nX-Katie-Rejection: automatic (moo)"
660
            os.write(reason_fd, reject_message)
661
            reject_mail_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/queue.rejected")
J
New.  
James Troup 已提交
662 663
        else:
            # Build up the rejection email
664
            user_email_address = utils.whoami() + " <%s>" % (Cnf["Dinstall::MyAdminAddress"])
J
New.  
James Troup 已提交
665

666 667 668
            Subst["__REJECTOR_ADDRESS__"] = user_email_address
            Subst["__MANUAL_REJECT_MESSAGE__"] = reject_message
            Subst["__CC__"] = "Cc: " + Cnf["Dinstall::MyEmailAddress"]
669
            reject_mail_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/queue.rejected")
J
New.  
James Troup 已提交
670
            # Write the rejection email out as the <foo>.reason file
671
            os.write(reason_fd, reject_mail_message)
J
James Troup 已提交
672

673
        os.close(reason_fd)
J
New.  
James Troup 已提交
674 675 676

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

679 680
        self.Logger.log(["rejected", pkg.changes_file])
        return 0
J
New.  
James Troup 已提交
681 682 683 684 685 686 687

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

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

690 691 692
    def source_exists (self, package, source_version, suites = ["any"]):
	okay = 1
	for suite in suites:
693
	    if suite == "any":
694 695 696
	    	que = "SELECT s.version FROM source s WHERE s.source = '%s'" % \
		    (package)
	    else:
697 698 699 700 701 702
		# 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 ]
703
		maps = [ (x[1], x[2]) for x in maps
704 705 706 707 708
				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])
709

710
		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]))
711 712 713
            q = self.projectB.query(que)

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

            # Try (1)
717
            if source_version in ql:
718 719 720
                continue

            # Try (2)
721
            orig_source_version = re_bin_only_nmu.sub('', source_version)
722
            if orig_source_version in ql:
723 724 725 726
                continue

            # No source found...
            okay = 0
R
Ryan Murray 已提交
727
	    break
728
	return okay
J
New.  
James Troup 已提交
729 730

    ################################################################################
731
    
J
New.  
James Troup 已提交
732
    def in_override_p (self, package, component, suite, binary_type, file):
733
        files = self.pkg.files
J
New.  
James Troup 已提交
734 735

        if binary_type == "": # must be source
736
            type = "dsc"
J
New.  
James Troup 已提交
737
        else:
738
            type = binary_type
J
New.  
James Troup 已提交
739 740 741

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

        # Avoid <undef> on unknown distributions
745
        suite_id = database.get_suite_id(suite)
J
New.  
James Troup 已提交
746
        if suite_id == -1:
747
            return None
748 749
        component_id = database.get_component_id(component)
        type_id = database.get_override_type_id(type)
J
New.  
James Troup 已提交
750 751

        # FIXME: nasty non-US speficic hack
752
        if component.lower().startswith("non-us/"):
753
            component = component[7:]
J
New.  
James Troup 已提交
754 755

        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"
756 757
                           % (package, suite_id, component_id, type_id))
        result = q.getresult()
J
New.  
James Troup 已提交
758 759
        # If checking for a source package fall back on the binary override type
        if type == "dsc" and not result:
760 761
            deb_type_id = database.get_override_type_id("deb")
            udeb_type_id = database.get_override_type_id("udeb")
762
            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"
763 764
                               % (package, suite_id, component_id, deb_type_id, udeb_type_id))
            result = q.getresult()
J
New.  
James Troup 已提交
765 766

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

771
        return result
J
New.  
James Troup 已提交
772 773 774 775 776

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

    def reject (self, str, prefix="Rejected: "):
        if str:
777 778 779
            # 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:
780 781
                self.reject_message += "\n"
            self.reject_message += prefix + str
J
New.  
James Troup 已提交
782

783 784
    ################################################################################

785 786 787 788
    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:
789
            if s in [ x.lower() for x in anysuite ]:
790 791 792 793 794 795
                if not anyversion or apt_pkg.VersionCompare(anyversion, v) <= 0:
                    anyversion=v
        return anyversion

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

796 797 798 799 800 801 802
    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():
803 804
            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)) ]
805 806
            # Enforce "must be newer than target suite" even if conffile omits it
            if target_suite not in must_be_newer_than:
807
                must_be_newer_than.append(target_suite)
808
            for entry in query_result:
809 810
                existent_version = entry[0]
                suite = entry[1]
811
                if suite in must_be_newer_than and \
812
                   apt_pkg.VersionCompare(new_version, existent_version) < 1:
813
                    self.reject("%s: old version (%s) in %s >= new version (%s) targeted at %s." % (file, existent_version, suite, new_version, target_suite))
814
                if suite in must_be_older_than and \
815
                   apt_pkg.VersionCompare(new_version, existent_version) > -1:
816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835
                    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: ")
836 837
                            self.pkg.changes.setdefault("propdistribution", {})
                            self.pkg.changes["propdistribution"][addsuite] = 1
838 839 840 841 842 843 844 845
                            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.
846
                            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: ")
847 848
                            cansave = 1
                        elif apt_pkg.VersionCompare(new_version, add_version) > 0 and \
849
                             apt_pkg.VersionCompare(add_version, target_version) >= 0:
850
                            # propogate!!
851 852 853
                            self.reject("Propogating upload to %s" % (addsuite), "Warning: ")
                            self.pkg.changes.setdefault("propdistribution", {})
                            self.pkg.changes["propdistribution"][addsuite] = 1
854 855 856
                            cansave = 1
                
                    if not cansave:
857
                        self.reject("%s: old version (%s) in %s <= new version (%s) targeted at %s." % (file, existent_version, suite, new_version, target_suite))
858 859 860 861

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

    def check_binary_against_db(self, file):
862 863
        self.reject_message = ""
        files = self.pkg.files
J
New.  
James Troup 已提交
864

865 866 867 868 869 870 871
        # 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"],
872 873
                                   files[file]["architecture"]))
        self.cross_suite_version_check(q.getresult(), file, files[file]["version"])
874

J
New.  
James Troup 已提交
875
        # Check for any existing copies of the file
876 877 878 879 880 881 882 883
        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():
884
            self.reject("%s: can not overwrite existing copy already in the archive." % (file))
J
New.  
James Troup 已提交
885

886
        return self.reject_message
J
New.  
James Troup 已提交
887 888 889 890

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

    def check_source_against_db(self, file):
891 892
        self.reject_message = ""
        dsc = self.pkg.dsc
J
New.  
James Troup 已提交
893

894 895 896
        # Ensure version is sane
        q = self.projectB.query("""
SELECT s.version, su.suite_name FROM source s, src_associations sa, suite su
897 898
 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"))
899

900
        return self.reject_message
J
New.  
James Troup 已提交
901 902 903

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

904 905 906 907 908 909 910 911
    # **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 derefernece the deleted entry.
    # **WARNING**

J
New.  
James Troup 已提交
912
    def check_dsc_against_db(self, file):
913 914 915 916 917
        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 已提交
918 919 920 921 922

        # 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.
        for dsc_file in dsc_files.keys():
923
            found = None
J
New.  
James Troup 已提交
924
            if files.has_key(dsc_file):
925 926
                actual_md5 = files[dsc_file]["md5sum"]
                actual_size = int(files[dsc_file]["size"])
J
New.  
James Troup 已提交
927 928
                found = "%s in incoming" % (dsc_file)
                # Check the file does not already exist in the archive
929 930
                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" % (dsc_file))
                ql = q.getresult()
931 932
                # Strip out anything that isn't '%s' or '/%s$'
                for i in ql:
933
                    if i[3] != dsc_file and i[3][-(len(dsc_file)+1):] != '/'+dsc_file:
934
                        ql.remove(i)
935

936
                # "[dak] has not broken them.  [dak] has fixed a
J
New.  
James Troup 已提交
937 938 939 940 941 942 943 944
                # 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

945
                if ql:
946
                    # Ignore exact matches for .orig.tar.gz
947
                    match = 0
948
                    if dsc_file.endswith(".orig.tar.gz"):
949
                        for i in ql:
950 951
                            if files.has_key(dsc_file) and \
                               int(files[dsc_file]["size"]) == int(i[0]) and \
952
                               files[dsc_file]["md5sum"] == i[1]:
953 954 955 956
                                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
957 958

                    if not match:
959
                        self.reject("can not overwrite existing copy of '%s' already in the archive." % (dsc_file))
960
            elif dsc_file.endswith(".orig.tar.gz"):
J
New.  
James Troup 已提交
961
                # Check in the pool
962 963
                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()
964 965 966
                # 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:
967
                        ql.remove(i)
J
New.  
James Troup 已提交
968

969
                if ql:
970 971 972
                    # 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 已提交
973 974 975 976 977

                    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:
978
                            old_file = i[0] + i[1]
979
                            old_file_fh = utils.open_file(old_file)
980
                            actual_md5 = apt_pkg.md5sum(old_file_fh)
981
                            old_file_fh.close()
982
                            actual_size = os.stat(old_file)[stat.ST_SIZE]
J
New.  
James Troup 已提交
983
                            if actual_md5 == dsc_files[dsc_file]["md5sum"] and actual_size == int(dsc_files[dsc_file]["size"]):
984
                                x = i
J
New.  
James Troup 已提交
985
                            else:
986
                                legacy_source_untouchable[i[3]] = ""
J
New.  
James Troup 已提交
987

988
                    old_file = x[0] + x[1]
989
                    old_file_fh = utils.open_file(old_file)
990
                    actual_md5 = apt_pkg.md5sum(old_file_fh)
991
                    old_file_fh.close()
992 993 994
                    actual_size = os.stat(old_file)[stat.ST_SIZE]
                    found = old_file
                    suite_type = x[2]
J
New.  
James Troup 已提交
995
                    dsc_files[dsc_file]["files id"] = x[3]; # need this for updating dsc_files in install()
996
                    # See install() in process-accepted...
997 998
                    self.pkg.orig_tar_id = x[3]
                    self.pkg.orig_tar_gz = old_file
J
New.  
James Troup 已提交
999
                    if suite_type == "legacy" or suite_type == "legacy-mixed":
1000
                        self.pkg.orig_tar_location = "legacy"
J
New.  
James Troup 已提交
1001
                    else:
1002
                        self.pkg.orig_tar_location = x[4]
J
New.  
James Troup 已提交
1003
                else:
1004
                    # Not there? Check the queue directories...
J
New.  
James Troup 已提交
1005

1006
                    in_unchecked = os.path.join(self.Cnf["Dir::Queue::Unchecked"],dsc_file)
1007
                    # See process_it() in 'dak process-unchecked' for explanation of this
1008
                    if os.path.exists(in_unchecked):
1009
                        return (self.reject_message, in_unchecked)
J
New.  
James Troup 已提交
1010
                    else:
1011
                        for dir in [ "Accepted", "New", "Byhand" ]:
1012
                            in_otherdir = os.path.join(self.Cnf["Dir::Queue::%s" % (dir)],dsc_file)
1013
                            if os.path.exists(in_otherdir):
1014
                                in_otherdir_fh = utils.open_file(in_otherdir)
1015
                                actual_md5 = apt_pkg.md5sum(in_otherdir_fh)
1016
                                in_otherdir_fh.close()
1017 1018 1019
                                actual_size = os.stat(in_otherdir)[stat.ST_SIZE]
                                found = in_otherdir
                                self.pkg.orig_tar_gz = in_otherdir
1020 1021

                    if not found:
1022 1023 1024
                        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 已提交
1025
            else:
1026 1027
                self.reject("%s refers to %s, but I can't find it in the queue." % (file, dsc_file))
                continue
J
New.  
James Troup 已提交
1028
            if actual_md5 != dsc_files[dsc_file]["md5sum"]:
1029
                self.reject("md5sum for %s doesn't match %s." % (found, file))
J
New.  
James Troup 已提交
1030
            if actual_size != int(dsc_files[dsc_file]["size"]):
1031
                self.reject("size for %s doesn't match %s." % (found, file))
J
New.  
James Troup 已提交
1032

1033
        return (self.reject_message, None)
J
James Troup 已提交
1034 1035

    def do_query(self, q):
1036 1037 1038 1039 1040 1041
        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