queue.py 47.0 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
New.  
James Troup 已提交
34 35 36 37 38 39
###############################################################################

# Convenience wrapper to carry around all the package information in

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    # Set up the per-package template substitution mappings

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

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

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

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

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

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

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

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

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

254 255
        file_keys = files.keys()
        file_keys.sort()
J
New.  
James Troup 已提交
256 257 258
        for file in file_keys:
            if files[file].has_key("byhand"):
                byhand = 1
259
                summary += file + " byhand\n"
J
New.  
James Troup 已提交
260 261
            elif files[file].has_key("new"):
                new = 1
262
                summary += "(new) %s %s %s\n" % (file, files[file]["priority"], files[file]["section"])
J
New.  
James Troup 已提交
263
                if files[file].has_key("othercomponents"):
264
                    summary += "WARNING: Already present in %s distribution.\n" % (files[file]["othercomponents"])
J
New.  
James Troup 已提交
265
                if files[file]["type"] == "deb":
266
                    deb_fh = utils.open_file(file)
267
                    summary += apt_pkg.ParseSection(apt_inst.debExtractControl(deb_fh))["Description"] + '\n'
268
                    deb_fh.close()
J
New.  
James Troup 已提交
269
            else:
270
                files[file]["pool name"] = utils.poolify (changes.get("source",""), files[file]["component"])
J
New.  
James Troup 已提交
271
                destination = self.Cnf["Dir::PoolRoot"] + files[file]["pool name"] + file
272
                summary += file + "\n  to " + destination + "\n"
J
New.  
James Troup 已提交
273

274
        short_summary = summary
J
New.  
James Troup 已提交
275 276

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

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

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

284
        return (summary, short_summary)
J
New.  
James Troup 已提交
285 286 287

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

288
    def close_bugs (self, summary, action):
289 290 291
        changes = self.pkg.changes
        Subst = self.Subst
        Cnf = self.Cnf
J
New.  
James Troup 已提交
292

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

295
        if not bugs:
296
            return summary
J
New.  
James Troup 已提交
297

298
        bugs.sort()
J
New.  
James Troup 已提交
299
        if not self.nmu.is_an_nmu(self.pkg):
300 301
            if changes["distribution"].has_key("experimental"):
		# tag bugs as fixed-in-experimental for uploads to experimental
302 303
		summary += "Setting bugs to severity fixed: "
		control_message = ""
304
		for bug in bugs:
305 306
		    summary += "%s " % (bug)
		    control_message += "tag %s + fixed-in-experimental\n" % (bug)
307
		if action and control_message != "":
308
		    Subst["__CONTROL_MESSAGE__"] = control_message
309
		    mail_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/process-unchecked.bug-experimental-fixed")
310
		    utils.send_mail (mail_message)
311
		if action:
312
		    self.Logger.log(["setting bugs to fixed"]+bugs)
313 314 315


	    else:
316
		summary += "Closing bugs: "
317
		for bug in bugs:
318
		    summary += "%s " % (bug)
319
		    if action:
320
			Subst["__BUG_NUMBER__"] = bug
321 322
			if changes["distribution"].has_key("stable"):
			    Subst["__STABLE_WARNING__"] = """
323 324 325 326
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
327
distribution."""
328
		        else:
329
			    Subst["__STABLE_WARNING__"] = ""
330
			    mail_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/process-unchecked.bug-close")
331
			    utils.send_mail (mail_message)
332
                if action:
333
                    self.Logger.log(["closing bugs"]+bugs)
334 335

	else:                     # NMU
336 337
            summary += "Setting bugs to severity fixed: "
            control_message = ""
J
New.  
James Troup 已提交
338
            for bug in bugs:
339 340
                summary += "%s " % (bug)
                control_message += "tag %s + fixed\n" % (bug)
J
New.  
James Troup 已提交
341
            if action and control_message != "":
342
                Subst["__CONTROL_MESSAGE__"] = control_message
343
                mail_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/process-unchecked.bug-nmu-fixed")
344
                utils.send_mail (mail_message)
J
New.  
James Troup 已提交
345
            if action:
346 347 348
                self.Logger.log(["setting bugs to fixed"]+bugs)
        summary += "\n"
        return summary
349 350 351 352

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

    def announce (self, short_summary, action):
353 354 355
        Subst = self.Subst
        Cnf = self.Cnf
        changes = self.pkg.changes
356 357 358

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

361 362 363
        lists_done = {}
        summary = ""
        Subst["__SHORT_SUMMARY__"] = short_summary
364 365

        for dist in changes["distribution"].keys():
366
            list = Cnf.Find("Suite::%s::Announce" % (dist))
367
            if list == "" or lists_done.has_key(list):
368 369 370
                continue
            lists_done[list] = 1
            summary += "Announcing to %s\n" % (list)
371 372

            if action:
373
                Subst["__ANNOUNCE_LIST_ADDRESS__"] = list
374
                if Cnf.get("Dinstall::TrackingServer") and changes["architecture"].has_key("source"):
375
                    Subst["__ANNOUNCE_LIST_ADDRESS__"] = Subst["__ANNOUNCE_LIST_ADDRESS__"] + "\nBcc: %s@%s" % (changes["source"], Cnf["Dinstall::TrackingServer"])
376
                mail_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/process-unchecked.announce")
377
                utils.send_mail (mail_message)
378

J
James Troup 已提交
379
        if Cnf.FindB("Dinstall::CloseBugs"):
380
            summary = self.close_bugs(summary, action)
381

382
        return summary
J
New.  
James Troup 已提交
383 384 385 386

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

    def accept (self, summary, short_summary):
387 388 389 390 391 392
        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 已提交
393 394

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

397
        self.dump_vars(Cnf["Dir::Queue::Accepted"])
J
New.  
James Troup 已提交
398 399

        # Move all the files into the accepted directory
400 401
        utils.move(changes_file, Cnf["Dir::Queue::Accepted"])
        file_keys = files.keys()
J
New.  
James Troup 已提交
402
        for file in file_keys:
403
            utils.move(file, Cnf["Dir::Queue::Accepted"])
404
            self.accept_bytes += float(files[file]["size"])
405
        self.accept_count += 1
J
New.  
James Troup 已提交
406 407 408 409

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

416 417 418 419 420 421 422 423 424 425 426 427

        ## 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"],
428 429 430 431
                                                    dotprefix=1, perms=0644)
                version_history = utils.open_file(temp_filename, 'w')
                version_history.write(dsc["bts changelog"])
                version_history.close()
432
                filename = "%s/%s" % (Cnf["Dir::Queue::BTSVersionTrack"],
433 434
                                      changes_file[:-8]+".versions")
                os.rename(temp_filename, filename)
435 436 437

            # Write out the binary -> source mapping.
            temp_filename = utils.temp_filename(Cnf["Dir::Queue::BTSVersionTrack"],
438 439
                                                dotprefix=1, perms=0644)
            debinfo = utils.open_file(temp_filename, 'w')
440
            for file in file_keys:
441
                f = files[file]
442 443 444
                if f["type"] == "deb":
                    line = " ".join([f["package"], f["version"],
                                     f["architecture"], f["source package"],
445 446 447
                                     f["source version"]])
                    debinfo.write(line+"\n")
            debinfo.close()
448
            filename = "%s/%s" % (Cnf["Dir::Queue::BTSVersionTrack"],
449 450
                                  changes_file[:-8]+".debinfo")
            os.rename(temp_filename, filename)
451

452 453 454 455 456
        self.queue_build("accepted", Cnf["Dir::Queue::Accepted"])

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

    def queue_build (self, queue, path):
457 458 459 460 461 462 463 464
        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()

465
        ## Special support to enable clean auto-building of queued packages
466
        queue_id = database.get_or_set_queue_id(queue)
467

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

510
        self.projectB.query("COMMIT WORK")
511

J
New.  
James Troup 已提交
512 513 514
    ###########################################################################

    def check_override (self):
515 516 517 518
        Subst = self.Subst
        changes = self.pkg.changes
        files = self.pkg.files
        Cnf = self.Cnf
J
New.  
James Troup 已提交
519

520 521 522 523 524 525 526
        # 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"]:
527
            return
J
New.  
James Troup 已提交
528

529 530 531
        summary = ""
        file_keys = files.keys()
        file_keys.sort()
J
James Troup 已提交
532
        for file in file_keys:
J
New.  
James Troup 已提交
533
            if not files[file].has_key("new") and files[file]["type"] == "deb":
534 535
                section = files[file]["section"]
                override_section = files[file]["override section"]
536
                if section.lower() != override_section.lower() and section != "-":
J
New.  
James Troup 已提交
537
                    # Ignore this; it's a common mistake and not worth whining about
538
                    if section.lower() == "non-us/main" and override_section.lower() == "non-us":
539 540 541 542
                        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 已提交
543
                if priority != override_priority and priority != "-":
544
                    summary += "%s: package says priority is %s, override says %s.\n" % (file, priority, override_priority)
J
New.  
James Troup 已提交
545 546

        if summary == "":
547
            return
J
New.  
James Troup 已提交
548

549
        Subst["__SUMMARY__"] = summary
550
        mail_message = utils.TemplateSubst(Subst,self.Cnf["Dir::Templates"]+"/process-unchecked.override-disparity")
551
        utils.send_mail(mail_message)
J
New.  
James Troup 已提交
552 553 554

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

555 556 557 558 559
    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 已提交
560 561 562 563 564 565

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

J
New.  
James Troup 已提交
595 596 597
    ###########################################################################

    def do_reject (self, manual = 0, reject_message = ""):
J
James Troup 已提交
598 599 600
        # 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:
601
            temp_filename = utils.temp_filename()
J
James Troup 已提交
602
            editor = os.environ.get("EDITOR","vi")
603
            answer = 'E'
J
James Troup 已提交
604 605
            while answer == 'E':
                os.system("%s %s" % (editor, temp_filename))
606 607 608 609 610
                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 已提交
611
                prompt = "[R]eject, Edit, Abandon, Quit ?"
612
                answer = "XXX"
613
                while prompt.find(answer) == -1:
614 615
                    answer = utils.our_raw_input(prompt)
                    m = re_default_answer.search(prompt)
J
James Troup 已提交
616
                    if answer == "":
617 618 619
                        answer = m.group(1)
                    answer = answer[:1].upper()
            os.unlink(temp_filename)
J
James Troup 已提交
620
            if answer == 'A':
621
                return 1
J
James Troup 已提交
622
            elif answer == 'Q':
623
                sys.exit(0)
J
James Troup 已提交
624

J
New.  
James Troup 已提交
625 626
        print "Rejecting.\n"

627 628 629
        Cnf = self.Cnf
        Subst = self.Subst
        pkg = self.pkg
J
New.  
James Troup 已提交
630

631 632
        reason_filename = pkg.changes_file[:-8] + ".reason"
        reason_filename = Cnf["Dir::Queue::Reject"] + '/' + reason_filename
J
New.  
James Troup 已提交
633 634

        # Move all the files into the reject directory
635 636
        reject_files = pkg.files.keys() + [pkg.changes_file]
        self.force_reject(reject_files)
J
New.  
James Troup 已提交
637 638 639

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

        if not manual:
645 646
            Subst["__REJECTOR_ADDRESS__"] = Cnf["Dinstall::MyEmailAddress"]
            Subst["__MANUAL_REJECT_MESSAGE__"] = ""
647
            Subst["__CC__"] = "X-DAK-Rejection: automatic (moo)"
648
            os.write(reason_fd, reject_message)
649
            reject_mail_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/dak.rejected")
J
New.  
James Troup 已提交
650 651
        else:
            # Build up the rejection email
652
            user_email_address = utils.whoami() + " <%s>" % (Cnf["Dinstall::MyAdminAddress"])
J
New.  
James Troup 已提交
653

654 655 656
            Subst["__REJECTOR_ADDRESS__"] = user_email_address
            Subst["__MANUAL_REJECT_MESSAGE__"] = reject_message
            Subst["__CC__"] = "Cc: " + Cnf["Dinstall::MyEmailAddress"]
657
            reject_mail_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/dak.rejected")
J
New.  
James Troup 已提交
658
            # Write the rejection email out as the <foo>.reason file
659
            os.write(reason_fd, reject_mail_message)
J
James Troup 已提交
660

661
        os.close(reason_fd)
J
New.  
James Troup 已提交
662 663 664

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

667 668
        self.Logger.log(["rejected", pkg.changes_file])
        return 0
J
New.  
James Troup 已提交
669 670 671 672 673 674 675

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

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

678 679 680
    def source_exists (self, package, source_version, suites = ["any"]):
	okay = 1
	for suite in suites:
681
	    if suite == "any":
682 683 684
	    	que = "SELECT s.version FROM source s WHERE s.source = '%s'" % \
		    (package)
	    else:
685 686 687 688 689 690
		# 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 ]
691
		maps = [ (x[1], x[2]) for x in maps
692 693 694 695 696
				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])
697

698
		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]))
699 700 701
            q = self.projectB.query(que)

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

            # Try (1)
705
            if source_version in ql:
706 707 708
                continue

            # Try (2)
709
            orig_source_version = re_bin_only_nmu.sub('', source_version)
710
            if orig_source_version in ql:
711 712 713 714
                continue

            # No source found...
            okay = 0
R
Ryan Murray 已提交
715
	    break
716
	return okay
J
New.  
James Troup 已提交
717 718

    ################################################################################
719
    
J
New.  
James Troup 已提交
720
    def in_override_p (self, package, component, suite, binary_type, file):
721
        files = self.pkg.files
J
New.  
James Troup 已提交
722 723

        if binary_type == "": # must be source
724
            type = "dsc"
J
New.  
James Troup 已提交
725
        else:
726
            type = binary_type
J
New.  
James Troup 已提交
727 728 729

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

        # Avoid <undef> on unknown distributions
733
        suite_id = database.get_suite_id(suite)
J
New.  
James Troup 已提交
734
        if suite_id == -1:
735
            return None
736 737
        component_id = database.get_component_id(component)
        type_id = database.get_override_type_id(type)
J
New.  
James Troup 已提交
738 739

        # FIXME: nasty non-US speficic hack
740
        if component.lower().startswith("non-us/"):
741
            component = component[7:]
J
New.  
James Troup 已提交
742 743

        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"
744 745
                           % (package, suite_id, component_id, type_id))
        result = q.getresult()
J
New.  
James Troup 已提交
746 747
        # If checking for a source package fall back on the binary override type
        if type == "dsc" and not result:
748 749
            deb_type_id = database.get_override_type_id("deb")
            udeb_type_id = database.get_override_type_id("udeb")
750
            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"
751 752
                               % (package, suite_id, component_id, deb_type_id, udeb_type_id))
            result = q.getresult()
J
New.  
James Troup 已提交
753 754

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

759
        return result
J
New.  
James Troup 已提交
760 761 762 763 764

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

    def reject (self, str, prefix="Rejected: "):
        if str:
765 766 767
            # 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:
768 769
                self.reject_message += "\n"
            self.reject_message += prefix + str
J
New.  
James Troup 已提交
770

771 772
    ################################################################################

773 774 775 776
    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:
777
            if s in [ x.lower() for x in anysuite ]:
778 779 780 781 782 783
                if not anyversion or apt_pkg.VersionCompare(anyversion, v) <= 0:
                    anyversion=v
        return anyversion

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

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

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

    def check_binary_against_db(self, file):
850 851
        self.reject_message = ""
        files = self.pkg.files
J
New.  
James Troup 已提交
852

853 854 855 856 857 858 859
        # 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"],
860 861
                                   files[file]["architecture"]))
        self.cross_suite_version_check(q.getresult(), file, files[file]["version"])
862

J
New.  
James Troup 已提交
863
        # Check for any existing copies of the file
864 865 866 867 868 869 870 871
        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():
872
            self.reject("%s: can not overwrite existing copy already in the archive." % (file))
J
New.  
James Troup 已提交
873

874
        return self.reject_message
J
New.  
James Troup 已提交
875 876 877 878

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

    def check_source_against_db(self, file):
879 880
        self.reject_message = ""
        dsc = self.pkg.dsc
J
New.  
James Troup 已提交
881

882 883 884
        # Ensure version is sane
        q = self.projectB.query("""
SELECT s.version, su.suite_name FROM source s, src_associations sa, suite su
885 886
 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"))
887

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

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

892 893 894 895 896 897 898 899
    # **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 已提交
900
    def check_dsc_against_db(self, file):
901 902 903 904 905
        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 已提交
906 907 908 909 910

        # 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():
911
            found = None
J
New.  
James Troup 已提交
912
            if files.has_key(dsc_file):
913 914
                actual_md5 = files[dsc_file]["md5sum"]
                actual_size = int(files[dsc_file]["size"])
J
New.  
James Troup 已提交
915 916
                found = "%s in incoming" % (dsc_file)
                # Check the file does not already exist in the archive
917 918
                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()
919 920
                # Strip out anything that isn't '%s' or '/%s$'
                for i in ql:
921
                    if i[3] != dsc_file and i[3][-(len(dsc_file)+1):] != '/'+dsc_file:
922
                        ql.remove(i)
923

924
                # "[dak] has not broken them.  [dak] has fixed a
J
New.  
James Troup 已提交
925 926 927 928 929 930 931 932
                # 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

933
                if ql:
934
                    # Ignore exact matches for .orig.tar.gz
935
                    match = 0
936
                    if dsc_file.endswith(".orig.tar.gz"):
937
                        for i in ql:
938 939
                            if files.has_key(dsc_file) and \
                               int(files[dsc_file]["size"]) == int(i[0]) and \
940
                               files[dsc_file]["md5sum"] == i[1]:
941 942 943 944
                                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
945 946

                    if not match:
947
                        self.reject("can not overwrite existing copy of '%s' already in the archive." % (dsc_file))
948
            elif dsc_file.endswith(".orig.tar.gz"):
J
New.  
James Troup 已提交
949
                # Check in the pool
950 951
                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()
952 953 954
                # 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:
955
                        ql.remove(i)
J
New.  
James Troup 已提交
956

957
                if ql:
958 959 960
                    # 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 已提交
961 962 963 964 965

                    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:
966
                            old_file = i[0] + i[1]
967
                            old_file_fh = utils.open_file(old_file)
968
                            actual_md5 = apt_pkg.md5sum(old_file_fh)
969
                            old_file_fh.close()
970
                            actual_size = os.stat(old_file)[stat.ST_SIZE]
J
New.  
James Troup 已提交
971
                            if actual_md5 == dsc_files[dsc_file]["md5sum"] and actual_size == int(dsc_files[dsc_file]["size"]):
972
                                x = i
J
New.  
James Troup 已提交
973
                            else:
974
                                legacy_source_untouchable[i[3]] = ""
J
New.  
James Troup 已提交
975

976
                    old_file = x[0] + x[1]
977
                    old_file_fh = utils.open_file(old_file)
978
                    actual_md5 = apt_pkg.md5sum(old_file_fh)
979
                    old_file_fh.close()
980 981 982
                    actual_size = os.stat(old_file)[stat.ST_SIZE]
                    found = old_file
                    suite_type = x[2]
J
New.  
James Troup 已提交
983
                    dsc_files[dsc_file]["files id"] = x[3]; # need this for updating dsc_files in install()
984
                    # See install() in process-accepted...
985 986
                    self.pkg.orig_tar_id = x[3]
                    self.pkg.orig_tar_gz = old_file
J
New.  
James Troup 已提交
987
                    if suite_type == "legacy" or suite_type == "legacy-mixed":
988
                        self.pkg.orig_tar_location = "legacy"
J
New.  
James Troup 已提交
989
                    else:
990
                        self.pkg.orig_tar_location = x[4]
J
New.  
James Troup 已提交
991
                else:
992
                    # Not there? Check the queue directories...
J
New.  
James Troup 已提交
993

994
                    in_unchecked = os.path.join(self.Cnf["Dir::Queue::Unchecked"],dsc_file)
995
                    # See process_it() in 'dak process-unchecked' for explanation of this
996
                    if os.path.exists(in_unchecked):
997
                        return (self.reject_message, in_unchecked)
J
New.  
James Troup 已提交
998
                    else:
999
                        for dir in [ "Accepted", "New", "Byhand" ]:
1000
                            in_otherdir = os.path.join(self.Cnf["Dir::Queue::%s" % (dir)],dsc_file)
1001
                            if os.path.exists(in_otherdir):
1002
                                in_otherdir_fh = utils.open_file(in_otherdir)
1003
                                actual_md5 = apt_pkg.md5sum(in_otherdir_fh)
1004
                                in_otherdir_fh.close()
1005 1006 1007
                                actual_size = os.stat(in_otherdir)[stat.ST_SIZE]
                                found = in_otherdir
                                self.pkg.orig_tar_gz = in_otherdir
1008 1009

                    if not found:
1010 1011 1012
                        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 已提交
1013
            else:
1014 1015
                self.reject("%s refers to %s, but I can't find it in the queue." % (file, dsc_file))
                continue
J
New.  
James Troup 已提交
1016
            if actual_md5 != dsc_files[dsc_file]["md5sum"]:
1017
                self.reject("md5sum for %s doesn't match %s." % (found, file))
J
New.  
James Troup 已提交
1018
            if actual_size != int(dsc_files[dsc_file]["size"]):
1019
                self.reject("size for %s doesn't match %s." % (found, file))
J
New.  
James Troup 已提交
1020

1021
        return (self.reject_message, None)
J
James Troup 已提交
1022 1023

    def do_query(self, q):
1024 1025 1026 1027 1028 1029
        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