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

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

# 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

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

23
import cPickle, errno, os, pg, re, stat, sys, time
24
import apt_inst, apt_pkg
25
import utils, database
J
Joerg Jaspert 已提交
26
from dak_exceptions import *
J
New.  
James Troup 已提交
27

28
from types import *
J
New.  
James Troup 已提交
29 30 31

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

32 33 34 35
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 已提交
36

37 38 39 40 41 42 43 44
################################################################################

# Determine what parts in a .changes are NEW

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

    # Build up a list of potentially new things
J
Joerg Jaspert 已提交
45 46
    for file_entry in files.keys():
        f = files[file_entry]
47 48 49 50 51 52
        # Skip byhand elements
        if f["type"] == "byhand":
            continue
        pkg = f["package"]
        priority = f["priority"]
        section = f["section"]
J
Joerg Jaspert 已提交
53
        file_type = get_type(f)
54 55
        component = f["component"]

J
Joerg Jaspert 已提交
56
        if file_type == "dsc":
57 58 59 60 61
            priority = "source"
        if not new.has_key(pkg):
            new[pkg] = {}
            new[pkg]["priority"] = priority
            new[pkg]["section"] = section
J
Joerg Jaspert 已提交
62
            new[pkg]["type"] = file_type
63 64 65 66
            new[pkg]["component"] = component
            new[pkg]["files"] = []
        else:
            old_type = new[pkg]["type"]
J
Joerg Jaspert 已提交
67
            if old_type != file_type:
68 69 70 71
                # source gets trumped by deb or udeb
                if old_type == "dsc":
                    new[pkg]["priority"] = priority
                    new[pkg]["section"] = section
J
Joerg Jaspert 已提交
72
                    new[pkg]["type"] = file_type
73
                    new[pkg]["component"] = component
J
Joerg Jaspert 已提交
74
        new[pkg]["files"].append(file_entry)
75 76 77 78 79 80 81 82 83 84 85
        if f.has_key("othercomponents"):
            new[pkg]["othercomponents"] = f["othercomponents"]

    for suite in changes["suite"].keys():
        suite_id = database.get_suite_id(suite)
        for pkg in new.keys():
            component_id = database.get_component_id(new[pkg]["component"])
            type_id = database.get_override_type_id(new[pkg]["type"])
            q = projectB.query("SELECT package FROM override WHERE package = '%s' AND suite = %s AND component = %s AND type = %s" % (pkg, suite_id, component_id, type_id))
            ql = q.getresult()
            if ql:
J
Joerg Jaspert 已提交
86 87 88
                for file_entry in new[pkg]["files"]:
                    if files[file_entry].has_key("new"):
                        del files[file_entry]["new"]
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
                del new[pkg]

    if warn:
        if changes["suite"].has_key("stable"):
            print "WARNING: overrides will be added for stable!"
            if changes["suite"].has_key("oldstable"):
                print "WARNING: overrides will be added for OLDstable!"
        for pkg in new.keys():
            if new[pkg].has_key("othercomponents"):
                print "WARNING: %s already present in %s distribution." % (pkg, new[pkg]["othercomponents"])

    return new

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

def get_type(f):
    # Determine the type
    if f.has_key("dbtype"):
J
Joerg Jaspert 已提交
107
        file_type = f["dbtype"]
108
    elif f["type"] in [ "orig.tar.gz", "orig.tar.bz2", "tar.gz", "tar.bz2", "diff.gz", "diff.bz2", "dsc" ]:
J
Joerg Jaspert 已提交
109
        file_type = "dsc"
110
    else:
J
Joerg Jaspert 已提交
111
        utils.fubar("invalid type (%s) for new.  Dazed, confused and sure as heck not continuing." % (file_type))
112 113

    # Validate the override type
J
Joerg Jaspert 已提交
114
    type_id = database.get_override_type_id(file_type)
115
    if type_id == -1:
J
Joerg Jaspert 已提交
116
        utils.fubar("invalid type (%s) for new.  Say wha?" % (file_type))
117

J
Joerg Jaspert 已提交
118
    return file_type
119 120 121 122 123 124 125 126 127

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

# check if section/priority values are valid

def check_valid(new):
    for pkg in new.keys():
        section = new[pkg]["section"]
        priority = new[pkg]["priority"]
J
Joerg Jaspert 已提交
128
        file_type = new[pkg]["type"]
129 130 131 132
        new[pkg]["section id"] = database.get_section_id(section)
        new[pkg]["priority id"] = database.get_priority_id(new[pkg]["priority"])
        # Sanity checks
        di = section.find("debian-installer") != -1
133
        if (di and file_type not in ("udeb", "dsc")) or (not di and file_type == "udeb"):
134
            new[pkg]["section id"] = -1
J
Joerg Jaspert 已提交
135 136
        if (priority == "source" and file_type != "dsc") or \
           (priority != "source" and file_type == "dsc"):
137 138 139
            new[pkg]["priority id"] = -1


J
New.  
James Troup 已提交
140 141 142 143 144 145
###############################################################################

# Convenience wrapper to carry around all the package information in

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

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

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

153
class Upload:
J
New.  
James Troup 已提交
154 155

    def __init__(self, Cnf):
156 157 158
        self.Cnf = Cnf
        self.accept_count = 0
        self.accept_bytes = 0L
J
Joerg Jaspert 已提交
159
        self.reject_message = ""
J
New.  
James Troup 已提交
160
        self.pkg = Pkg(changes = {}, dsc = {}, dsc_files = {}, files = {},
161
                       legacy_source_untouchable = {})
J
New.  
James Troup 已提交
162 163

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

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

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

    def init_vars (self):
176 177 178 179 180
        self.pkg.changes.clear()
        self.pkg.dsc.clear()
        self.pkg.files.clear()
        self.pkg.dsc_files.clear()
        self.pkg.legacy_source_untouchable.clear()
181 182 183
        self.pkg.orig_tar_id = None
        self.pkg.orig_tar_location = ""
        self.pkg.orig_tar_gz = None
J
New.  
James Troup 已提交
184 185 186 187

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

    def update_vars (self):
188
        dump_filename = self.pkg.changes_file[:-8]+".dak"
189 190
        dump_file = utils.open_file(dump_filename)
        p = cPickle.Unpickler(dump_file)
191 192 193 194 195 196 197 198 199 200

        self.pkg.changes.update(p.load())
        self.pkg.dsc.update(p.load())
        self.pkg.files.update(p.load())
        self.pkg.dsc_files.update(p.load())
        self.pkg.legacy_source_untouchable.update(p.load())

        self.pkg.orig_tar_id = p.load()
        self.pkg.orig_tar_location = p.load()

201
        dump_file.close()
J
New.  
James Troup 已提交
202 203 204

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

205 206 207
    # 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 已提交
208 209

    def dump_vars(self, dest_dir):
210 211 212 213 214 215 216 217 218

        changes = self.pkg.changes
        dsc = self.pkg.dsc
        files = self.pkg.files
        dsc_files = self.pkg.dsc_files
        legacy_source_untouchable = self.pkg.legacy_source_untouchable
        orig_tar_id = self.pkg.orig_tar_id
        orig_tar_location = self.pkg.orig_tar_location

219
        dump_filename = os.path.join(dest_dir,self.pkg.changes_file[:-8] + ".dak")
220
        dump_file = utils.open_file(dump_filename, 'w')
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
        try:
            os.chmod(dump_filename, 0664)
        except OSError, e:
            # chmod may fail when the dumpfile is not owned by the user
            # invoking dak (like e.g. when NEW is processed by a member
            # of ftpteam)
            if errno.errorcode[e.errno] == 'EPERM':
                perms = stat.S_IMODE(os.stat(dump_filename)[stat.ST_MODE])
                # security precaution, should never happen unless a weird
                # umask is set anywhere
                if perms & stat.S_IWOTH:
                    utils.fubar("%s is world writable and chmod failed." % \
                        (dump_filename,))
                # ignore the failed chmod otherwise as the file should
                # already have the right privileges and is just, at worst,
                # unreadable for world
            else:
                raise
J
James Troup 已提交
239

240
        p = cPickle.Pickler(dump_file, 1)
241 242 243 244 245
        d_changes = {}
        d_dsc = {}
        d_files = {}
        d_dsc_files = {}

J
New.  
James Troup 已提交
246
        ## files
J
Joerg Jaspert 已提交
247 248
        for file_entry in files.keys():
            d_files[file_entry] = {}
J
New.  
James Troup 已提交
249
            for i in [ "package", "version", "architecture", "type", "size",
P
Philipp Kern 已提交
250 251 252 253
                       "md5sum", "sha1sum", "sha256sum", "component",
                       "location id", "source package", "source version",
                       "maintainer", "dbtype", "files id", "new",
                       "section", "priority", "othercomponents",
254
                       "pool name", "original component" ]:
J
Joerg Jaspert 已提交
255 256
                if files[file_entry].has_key(i):
                    d_files[file_entry][i] = files[file_entry][i]
J
New.  
James Troup 已提交
257 258
        ## changes
        # Mandatory changes fields
259 260 261 262 263
        for i in [ "distribution", "source", "architecture", "version",
                   "maintainer", "urgency", "fingerprint", "changedby822",
                   "changedby2047", "changedbyname", "maintainer822",
                   "maintainer2047", "maintainername", "maintaineremail",
                   "closes", "changes" ]:
264
            d_changes[i] = changes[i]
J
New.  
James Troup 已提交
265
        # Optional changes fields
266 267
        for i in [ "changed-by", "filecontents", "format", "process-new note", "adv id", "distribution-version",
                   "sponsoremail" ]:
J
James Troup 已提交
268
            if changes.has_key(i):
269
                d_changes[i] = changes[i]
J
New.  
James Troup 已提交
270
        ## dsc
271
        for i in [ "source", "version", "maintainer", "fingerprint",
A
Anthony Towns 已提交
272
                   "uploaders", "bts changelog", "dm-upload-allowed" ]:
J
New.  
James Troup 已提交
273
            if dsc.has_key(i):
274
                d_dsc[i] = dsc[i]
J
New.  
James Troup 已提交
275
        ## dsc_files
J
Joerg Jaspert 已提交
276 277
        for file_entry in dsc_files.keys():
            d_dsc_files[file_entry] = {}
J
New.  
James Troup 已提交
278 279
            # Mandatory dsc_files fields
            for i in [ "size", "md5sum" ]:
J
Joerg Jaspert 已提交
280
                d_dsc_files[file_entry][i] = dsc_files[file_entry][i]
J
New.  
James Troup 已提交
281 282
            # Optional dsc_files fields
            for i in [ "files id" ]:
J
Joerg Jaspert 已提交
283 284
                if dsc_files[file_entry].has_key(i):
                    d_dsc_files[file_entry][i] = dsc_files[file_entry][i]
J
New.  
James Troup 已提交
285 286 287

        for i in [ d_changes, d_dsc, d_files, d_dsc_files,
                   legacy_source_untouchable, orig_tar_id, orig_tar_location ]:
288 289
            p.dump(i)
        dump_file.close()
J
New.  
James Troup 已提交
290 291 292 293 294 295

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

    # Set up the per-package template substitution mappings

    def update_subst (self, reject_message = ""):
296 297
        Subst = self.Subst
        changes = self.pkg.changes
298
        # If 'dak process-unchecked' crashed out in the right place, architecture may still be a string.
J
New.  
James Troup 已提交
299
        if not changes.has_key("architecture") or not isinstance(changes["architecture"], DictType):
300
            changes["architecture"] = { "Unknown" : "" }
301 302
        # and maintainer2047 may not exist.
        if not changes.has_key("maintainer2047"):
303
            changes["maintainer2047"] = self.Cnf["Dinstall::MyEmailAddress"]
J
New.  
James Troup 已提交
304

305 306 307
        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 已提交
308 309 310

        # 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"]):
311
            Subst["__MAINTAINER_FROM__"] = changes["changedby2047"]
312
            Subst["__MAINTAINER_TO__"] = "%s, %s" % (changes["changedby2047"],
313 314
                                                     changes["maintainer2047"])
            Subst["__MAINTAINER__"] = changes.get("changed-by", "Unknown")
J
New.  
James Troup 已提交
315
        else:
316 317 318
            Subst["__MAINTAINER_FROM__"] = changes["maintainer2047"]
            Subst["__MAINTAINER_TO__"] = changes["maintainer2047"]
            Subst["__MAINTAINER__"] = changes.get("maintainer", "Unknown")
J
Joerg Jaspert 已提交
319 320 321 322

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

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

326 327
        # Apply any global override of the Maintainer field
        if self.Cnf.get("Dinstall::OverrideMaintainer"):
328 329
            Subst["__MAINTAINER_TO__"] = self.Cnf["Dinstall::OverrideMaintainer"]
            Subst["__MAINTAINER_FROM__"] = self.Cnf["Dinstall::OverrideMaintainer"]
330

331 332 333
        Subst["__REJECT_MESSAGE__"] = reject_message
        Subst["__SOURCE__"] = changes.get("source", "Unknown")
        Subst["__VERSION__"] = changes.get("version", "Unknown")
J
New.  
James Troup 已提交
334 335 336 337

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

    def build_summaries(self):
338 339
        changes = self.pkg.changes
        files = self.pkg.files
J
New.  
James Troup 已提交
340

341
        byhand = summary = new = ""
J
New.  
James Troup 已提交
342 343 344 345

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

J
Various  
Joerg Jaspert 已提交
348
        override_summary =""
349 350
        file_keys = files.keys()
        file_keys.sort()
J
Joerg Jaspert 已提交
351 352
        for file_entry in file_keys:
            if files[file_entry].has_key("byhand"):
J
New.  
James Troup 已提交
353
                byhand = 1
J
Joerg Jaspert 已提交
354 355
                summary += file_entry + " byhand\n"
            elif files[file_entry].has_key("new"):
J
New.  
James Troup 已提交
356
                new = 1
J
Joerg Jaspert 已提交
357 358 359 360 361
                summary += "(new) %s %s %s\n" % (file_entry, files[file_entry]["priority"], files[file_entry]["section"])
                if files[file_entry].has_key("othercomponents"):
                    summary += "WARNING: Already present in %s distribution.\n" % (files[file_entry]["othercomponents"])
                if files[file_entry]["type"] == "deb":
                    deb_fh = utils.open_file(file_entry)
362
                    summary += apt_pkg.ParseSection(apt_inst.debExtractControl(deb_fh))["Description"] + '\n'
363
                    deb_fh.close()
J
New.  
James Troup 已提交
364
            else:
J
Joerg Jaspert 已提交
365 366 367 368 369 370
                files[file_entry]["pool name"] = utils.poolify (changes.get("source",""), files[file_entry]["component"])
                destination = self.Cnf["Dir::PoolRoot"] + files[file_entry]["pool name"] + file_entry
                summary += file_entry + "\n  to " + destination + "\n"
                if not files[file_entry].has_key("type"):
                    files[file_entry]["type"] = "unknown"
                if files[file_entry]["type"] in ["deb", "udeb", "dsc"]:
371 372
                    # (queue/unchecked), there we have override entries already, use them
                    # (process-new), there we dont have override entries, use the newly generated ones.
J
Joerg Jaspert 已提交
373 374 375
                    override_prio = files[file_entry].get("override priority", files[file_entry]["priority"])
                    override_sect = files[file_entry].get("override section", files[file_entry]["section"])
                    override_summary += "%s - %s %s\n" % (file_entry, override_prio, override_sect)
J
New.  
James Troup 已提交
376

377
        short_summary = summary
J
New.  
James Troup 已提交
378 379

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

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

385 386
        summary += "\n\nOverride entries for your package:\n" + override_summary + "\n"

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

389
        return (summary, short_summary)
J
New.  
James Troup 已提交
390 391 392

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

393
    def close_bugs (self, summary, action):
394 395 396
        changes = self.pkg.changes
        Subst = self.Subst
        Cnf = self.Cnf
J
New.  
James Troup 已提交
397

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

400
        if not bugs:
401
            return summary
J
New.  
James Troup 已提交
402

403
        bugs.sort()
404 405 406 407 408 409 410
        summary += "Closing bugs: "
        for bug in bugs:
            summary += "%s " % (bug)
            if action:
                Subst["__BUG_NUMBER__"] = bug
                if changes["distribution"].has_key("stable"):
                    Subst["__STABLE_WARNING__"] = """
411 412 413 414
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
415
distribution."""
416 417 418 419 420 421
                else:
                    Subst["__STABLE_WARNING__"] = ""
                    mail_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/process-unchecked.bug-close")
                    utils.send_mail (mail_message)
        if action:
            self.Logger.log(["closing bugs"]+bugs)
422
        summary += "\n"
423

424
        return summary
425 426 427 428

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

    def announce (self, short_summary, action):
429 430 431
        Subst = self.Subst
        Cnf = self.Cnf
        changes = self.pkg.changes
432 433 434

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

437 438 439
        lists_done = {}
        summary = ""
        Subst["__SHORT_SUMMARY__"] = short_summary
440 441

        for dist in changes["distribution"].keys():
J
Joerg Jaspert 已提交
442 443
            announce_list = Cnf.Find("Suite::%s::Announce" % (dist))
            if announce_list == "" or lists_done.has_key(announce_list):
444
                continue
J
Joerg Jaspert 已提交
445 446
            lists_done[announce_list] = 1
            summary += "Announcing to %s\n" % (announce_list)
447 448

            if action:
J
Joerg Jaspert 已提交
449
                Subst["__ANNOUNCE_LIST_ADDRESS__"] = announce_list
450
                if Cnf.get("Dinstall::TrackingServer") and changes["architecture"].has_key("source"):
451
                    Subst["__ANNOUNCE_LIST_ADDRESS__"] = Subst["__ANNOUNCE_LIST_ADDRESS__"] + "\nBcc: %s@%s" % (changes["source"], Cnf["Dinstall::TrackingServer"])
452
                mail_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/process-unchecked.announce")
453
                utils.send_mail (mail_message)
454

J
James Troup 已提交
455
        if Cnf.FindB("Dinstall::CloseBugs"):
456
            summary = self.close_bugs(summary, action)
457

458
        return summary
J
New.  
James Troup 已提交
459 460 461 462

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

    def accept (self, summary, short_summary):
463 464 465 466 467 468
        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 已提交
469 470

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

473
        self.dump_vars(Cnf["Dir::Queue::Accepted"])
J
New.  
James Troup 已提交
474 475

        # Move all the files into the accepted directory
476 477
        utils.move(changes_file, Cnf["Dir::Queue::Accepted"])
        file_keys = files.keys()
J
Joerg Jaspert 已提交
478 479 480
        for file_entry in file_keys:
            utils.move(file_entry, Cnf["Dir::Queue::Accepted"])
            self.accept_bytes += float(files[file_entry]["size"])
481
        self.accept_count += 1
J
New.  
James Troup 已提交
482 483 484 485

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

492 493 494 495 496 497 498 499 500 501 502

        ## 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"):

J
various  
Joerg Jaspert 已提交
503
                (fd, temp_filename) = utils.temp_filename(Cnf["Dir::Queue::BTSVersionTrack"], prefix=".")
J
fdopen  
Joerg Jaspert 已提交
504
                version_history = os.fdopen(fd, 'w')
505 506
                version_history.write(dsc["bts changelog"])
                version_history.close()
507
                filename = "%s/%s" % (Cnf["Dir::Queue::BTSVersionTrack"],
508 509
                                      changes_file[:-8]+".versions")
                os.rename(temp_filename, filename)
J
various  
Joerg Jaspert 已提交
510
                os.chmod(filename, "0644")
511 512

            # Write out the binary -> source mapping.
J
various  
Joerg Jaspert 已提交
513
            (fd, temp_filename) = utils.temp_filename(Cnf["Dir::Queue::BTSVersionTrack"], prefix=".")
J
fdopen  
Joerg Jaspert 已提交
514
            debinfo = os.fdopen(fd, 'w')
J
Joerg Jaspert 已提交
515 516
            for file_entry in file_keys:
                f = files[file_entry]
517 518 519
                if f["type"] == "deb":
                    line = " ".join([f["package"], f["version"],
                                     f["architecture"], f["source package"],
520 521 522
                                     f["source version"]])
                    debinfo.write(line+"\n")
            debinfo.close()
523
            filename = "%s/%s" % (Cnf["Dir::Queue::BTSVersionTrack"],
524 525
                                  changes_file[:-8]+".debinfo")
            os.rename(temp_filename, filename)
J
various  
Joerg Jaspert 已提交
526
            os.chmod(filename, "0644")
527

528 529 530 531 532
        self.queue_build("accepted", Cnf["Dir::Queue::Accepted"])

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

    def queue_build (self, queue, path):
533 534 535 536 537 538 539 540
        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()

541
        ## Special support to enable clean auto-building of queued packages
542
        queue_id = database.get_or_set_queue_id(queue)
543

544
        self.projectB.query("BEGIN WORK")
545
        for suite in changes["distribution"].keys():
546
            if suite not in Cnf.ValueList("Dinstall::QueueBuildSuites"):
547
                continue
548
            suite_id = database.get_suite_id(suite)
549
            dest_dir = Cnf["Dir::QueueBuild"]
550
            if Cnf.FindB("Dinstall::SecurityQueueBuild"):
551
                dest_dir = os.path.join(dest_dir, suite)
J
Joerg Jaspert 已提交
552 553 554
            for file_entry in file_keys:
                src = os.path.join(path, file_entry)
                dest = os.path.join(dest_dir, file_entry)
555
                if Cnf.FindB("Dinstall::SecurityQueueBuild"):
556
                    # Copy it since the original won't be readable by www-data
557
                    utils.copy(src, dest)
558 559
                else:
                    # Create a symlink to it
560
                    os.symlink(src, dest)
J
James Troup 已提交
561
                # Add it to the list of packages for later processing by apt-ftparchive
562
                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 已提交
563 564
            # If the .orig.tar.gz is in the pool, create a symlink to
            # it (if one doesn't already exist)
565 566 567
            if self.pkg.orig_tar_id:
                # Determine the .orig.tar.gz file name
                for dsc_file in self.pkg.dsc_files.keys():
568
                    if dsc_file.endswith(".orig.tar.gz"):
569 570
                        filename = dsc_file
                dest = os.path.join(dest_dir, filename)
571 572 573
                # If it doesn't exist, create a symlink
                if not os.path.exists(dest):
                    # Find the .orig.tar.gz in the pool
574 575
                    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()
576
                    if not ql:
577 578 579
                        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 已提交
580
                    # Add it to the list of packages for later processing by apt-ftparchive
581
                    self.projectB.query("INSERT INTO queue_build (suite, queue, filename, in_queue) VALUES (%s, %s, '%s', 't')" % (suite_id, queue_id, dest))
582 583
                # if it does, update things to ensure it's not removed prematurely
                else:
584
                    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 已提交
585

586
        self.projectB.query("COMMIT WORK")
587

J
New.  
James Troup 已提交
588 589 590
    ###########################################################################

    def check_override (self):
591 592 593 594
        Subst = self.Subst
        changes = self.pkg.changes
        files = self.pkg.files
        Cnf = self.Cnf
J
New.  
James Troup 已提交
595

596 597 598 599 600 601 602
        # 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"]:
603
            return
J
New.  
James Troup 已提交
604

605 606 607
        summary = ""
        file_keys = files.keys()
        file_keys.sort()
J
Joerg Jaspert 已提交
608 609 610 611
        for file_entry in file_keys:
            if not files[file_entry].has_key("new") and files[file_entry]["type"] == "deb":
                section = files[file_entry]["section"]
                override_section = files[file_entry]["override section"]
612
                if section.lower() != override_section.lower() and section != "-":
J
Joerg Jaspert 已提交
613 614 615
                    summary += "%s: package says section is %s, override says %s.\n" % (file_entry, section, override_section)
                priority = files[file_entry]["priority"]
                override_priority = files[file_entry]["override priority"]
J
New.  
James Troup 已提交
616
                if priority != override_priority and priority != "-":
J
Joerg Jaspert 已提交
617
                    summary += "%s: package says priority is %s, override says %s.\n" % (file_entry, priority, override_priority)
J
New.  
James Troup 已提交
618 619

        if summary == "":
620
            return
J
New.  
James Troup 已提交
621

622
        Subst["__SUMMARY__"] = summary
623
        mail_message = utils.TemplateSubst(Subst,self.Cnf["Dir::Templates"]+"/process-unchecked.override-disparity")
624
        utils.send_mail(mail_message)
J
New.  
James Troup 已提交
625 626 627

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

628 629 630 631 632
    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 已提交
633 634 635

        Cnf = self.Cnf

J
Joerg Jaspert 已提交
636
        for file_entry in files:
J
New.  
James Troup 已提交
637
            # Skip any files which don't exist or which we don't have permission to copy.
J
Joerg Jaspert 已提交
638
            if os.access(file_entry,os.R_OK) == 0:
639
                continue
J
Joerg Jaspert 已提交
640
            dest_file = os.path.join(Cnf["Dir::Queue::Reject"], file_entry)
J
New.  
James Troup 已提交
641
            try:
642
                dest_fd = os.open(dest_file, os.O_RDWR|os.O_CREAT|os.O_EXCL, 0644)
J
New.  
James Troup 已提交
643 644 645
            except OSError, e:
                # File exists?  Let's try and move it to the morgue
                if errno.errorcode[e.errno] == 'EEXIST':
J
Joerg Jaspert 已提交
646
                    morgue_file = os.path.join(Cnf["Dir::Morgue"],Cnf["Dir::MorgueReject"],file_entry)
J
New.  
James Troup 已提交
647
                    try:
648
                        morgue_file = utils.find_next_free(morgue_file)
J
Joerg Jaspert 已提交
649
                    except NoFreeFilenameError:
J
New.  
James Troup 已提交
650 651
                        # Something's either gone badly Pete Tong, or
                        # someone is trying to exploit us.
J
Joerg Jaspert 已提交
652
                        utils.warn("**WARNING** failed to move %s from the reject directory to the morgue." % (file_entry))
653 654
                        return
                    utils.move(dest_file, morgue_file, perms=0660)
J
New.  
James Troup 已提交
655
                    try:
656
                        dest_fd = os.open(dest_file, os.O_RDWR|os.O_CREAT|os.O_EXCL, 0644)
J
New.  
James Troup 已提交
657 658
                    except OSError, e:
                        # Likewise
J
Joerg Jaspert 已提交
659
                        utils.warn("**WARNING** failed to claim %s in the reject directory." % (file_entry))
660
                        return
J
New.  
James Troup 已提交
661
                else:
662
                    raise
J
New.  
James Troup 已提交
663 664
            # If we got here, we own the destination file, so we can
            # safely overwrite it.
J
Joerg Jaspert 已提交
665
            utils.move(file_entry, dest_file, 1, perms=0660)
666
            os.close(dest_fd)
667

J
New.  
James Troup 已提交
668 669 670
    ###########################################################################

    def do_reject (self, manual = 0, reject_message = ""):
J
James Troup 已提交
671 672 673
        # If we weren't given a manual rejection message, spawn an
        # editor so the user can add one in...
        if manual and not reject_message:
J
various  
Joerg Jaspert 已提交
674
            (fd, temp_filename) = utils.temp_filename()
J
James Troup 已提交
675
            editor = os.environ.get("EDITOR","vi")
676
            answer = 'E'
J
James Troup 已提交
677 678
            while answer == 'E':
                os.system("%s %s" % (editor, temp_filename))
679 680 681 682 683
                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 已提交
684
                prompt = "[R]eject, Edit, Abandon, Quit ?"
685
                answer = "XXX"
686
                while prompt.find(answer) == -1:
687 688
                    answer = utils.our_raw_input(prompt)
                    m = re_default_answer.search(prompt)
J
James Troup 已提交
689
                    if answer == "":
690 691 692
                        answer = m.group(1)
                    answer = answer[:1].upper()
            os.unlink(temp_filename)
J
James Troup 已提交
693
            if answer == 'A':
694
                return 1
J
James Troup 已提交
695
            elif answer == 'Q':
696
                sys.exit(0)
J
James Troup 已提交
697

J
New.  
James Troup 已提交
698 699
        print "Rejecting.\n"

700 701 702
        Cnf = self.Cnf
        Subst = self.Subst
        pkg = self.pkg
J
New.  
James Troup 已提交
703

704 705
        reason_filename = pkg.changes_file[:-8] + ".reason"
        reason_filename = Cnf["Dir::Queue::Reject"] + '/' + reason_filename
J
New.  
James Troup 已提交
706 707

        # Move all the files into the reject directory
708 709
        reject_files = pkg.files.keys() + [pkg.changes_file]
        self.force_reject(reject_files)
J
New.  
James Troup 已提交
710 711 712

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

        if not manual:
718 719
            Subst["__REJECTOR_ADDRESS__"] = Cnf["Dinstall::MyEmailAddress"]
            Subst["__MANUAL_REJECT_MESSAGE__"] = ""
720
            Subst["__CC__"] = "X-DAK-Rejection: automatic (moo)\nX-Katie-Rejection: automatic (moo)"
721
            os.write(reason_fd, reject_message)
722
            reject_mail_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/queue.rejected")
J
New.  
James Troup 已提交
723 724
        else:
            # Build up the rejection email
725
            user_email_address = utils.whoami() + " <%s>" % (Cnf["Dinstall::MyAdminAddress"])
J
New.  
James Troup 已提交
726

727 728 729
            Subst["__REJECTOR_ADDRESS__"] = user_email_address
            Subst["__MANUAL_REJECT_MESSAGE__"] = reject_message
            Subst["__CC__"] = "Cc: " + Cnf["Dinstall::MyEmailAddress"]
730
            reject_mail_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/queue.rejected")
J
New.  
James Troup 已提交
731
            # Write the rejection email out as the <foo>.reason file
732
            os.write(reason_fd, reject_mail_message)
J
James Troup 已提交
733

734
        os.close(reason_fd)
J
New.  
James Troup 已提交
735 736 737

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

740 741
        self.Logger.log(["rejected", pkg.changes_file])
        return 0
J
New.  
James Troup 已提交
742 743 744 745 746 747 748

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

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

751
    def source_exists (self, package, source_version, suites = ["any"]):
752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771
        okay = 1
        for suite in suites:
            if suite == "any":
                que = "SELECT s.version FROM source s WHERE s.source = '%s'" % \
                    (package)
            else:
                # 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 ]
                maps = [ (x[1], x[2]) for x in maps
                                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])

                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]))
772 773 774
            q = self.projectB.query(que)

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

            # Try (1)
778
            if source_version in ql:
779 780 781
                continue

            # Try (2)
782
            orig_source_version = re_bin_only_nmu.sub('', source_version)
783
            if orig_source_version in ql:
784 785 786 787
                continue

            # No source found...
            okay = 0
788 789
            break
        return okay
J
New.  
James Troup 已提交
790 791

    ################################################################################
792

J
New.  
James Troup 已提交
793
    def in_override_p (self, package, component, suite, binary_type, file):
794
        files = self.pkg.files
J
New.  
James Troup 已提交
795 796

        if binary_type == "": # must be source
J
Joerg Jaspert 已提交
797
            file_type = "dsc"
J
New.  
James Troup 已提交
798
        else:
J
Joerg Jaspert 已提交
799
            file_type = binary_type
J
New.  
James Troup 已提交
800 801 802

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

        # Avoid <undef> on unknown distributions
806
        suite_id = database.get_suite_id(suite)
J
New.  
James Troup 已提交
807
        if suite_id == -1:
808
            return None
809
        component_id = database.get_component_id(component)
J
Joerg Jaspert 已提交
810
        type_id = database.get_override_type_id(file_type)
J
New.  
James Troup 已提交
811 812

        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"
813 814
                           % (package, suite_id, component_id, type_id))
        result = q.getresult()
J
New.  
James Troup 已提交
815
        # If checking for a source package fall back on the binary override type
J
Joerg Jaspert 已提交
816
        if file_type == "dsc" and not result:
817 818
            deb_type_id = database.get_override_type_id("deb")
            udeb_type_id = database.get_override_type_id("udeb")
819
            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"
820 821
                               % (package, suite_id, component_id, deb_type_id, udeb_type_id))
            result = q.getresult()
J
New.  
James Troup 已提交
822 823

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

828
        return result
J
New.  
James Troup 已提交
829 830 831 832 833

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

    def reject (self, str, prefix="Rejected: "):
        if str:
834 835 836
            # 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:
837 838
                self.reject_message += "\n"
            self.reject_message += prefix + str
J
New.  
James Troup 已提交
839

840 841
    ################################################################################

842 843 844 845
    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:
846
            if s in [ x.lower() for x in anysuite ]:
847 848 849 850 851 852
                if not anyversion or apt_pkg.VersionCompare(anyversion, v) <= 0:
                    anyversion=v
        return anyversion

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

853 854
    def cross_suite_version_check(self, query_result, file, new_version,
            sourceful=False):
855 856 857 858 859 860
        """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():
861 862
            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)) ]
863 864
            # Enforce "must be newer than target suite" even if conffile omits it
            if target_suite not in must_be_newer_than:
865
                must_be_newer_than.append(target_suite)
866
            for entry in query_result:
867 868
                existent_version = entry[0]
                suite = entry[1]
869
                if suite in must_be_newer_than and sourceful and \
870
                   apt_pkg.VersionCompare(new_version, existent_version) < 1:
871
                    self.reject("%s: old version (%s) in %s >= new version (%s) targeted at %s." % (file, existent_version, suite, new_version, target_suite))
872
                if suite in must_be_older_than and \
873
                   apt_pkg.VersionCompare(new_version, existent_version) > -1:
874 875 876
                    ch = self.pkg.changes
                    cansave = 0
                    if ch.get('distribution-version', {}).has_key(suite):
877
                    # we really use the other suite, ignoring the conflicting one ...
878
                        addsuite = ch["distribution-version"][suite]
879

880 881
                        add_version = self.get_anyversion(query_result, addsuite)
                        target_version = self.get_anyversion(query_result, target_suite)
882

883 884 885 886 887 888 889 890 891 892 893
                        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: ")
894 895
                            self.pkg.changes.setdefault("propdistribution", {})
                            self.pkg.changes["propdistribution"][addsuite] = 1
896 897 898 899 900 901 902 903
                            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.
904
                            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: ")
905 906
                            cansave = 1
                        elif apt_pkg.VersionCompare(new_version, add_version) > 0 and \
907
                             apt_pkg.VersionCompare(add_version, target_version) >= 0:
908
                            # propogate!!
909 910 911
                            self.reject("Propogating upload to %s" % (addsuite), "Warning: ")
                            self.pkg.changes.setdefault("propdistribution", {})
                            self.pkg.changes["propdistribution"][addsuite] = 1
912
                            cansave = 1
913

914
                    if not cansave:
915
                        self.reject("%s: old version (%s) in %s <= new version (%s) targeted at %s." % (file, existent_version, suite, new_version, target_suite))
916 917 918 919

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

    def check_binary_against_db(self, file):
920 921
        self.reject_message = ""
        files = self.pkg.files
J
New.  
James Troup 已提交
922

923 924 925 926 927 928 929
        # 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"],
930
                                   files[file]["architecture"]))
931 932
        self.cross_suite_version_check(q.getresult(), file,
            files[file]["version"], sourceful=False)
933

J
New.  
James Troup 已提交
934
        # Check for any existing copies of the file
935 936 937 938 939 940 941 942
        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():
943
            self.reject("%s: can not overwrite existing copy already in the archive." % (file))
J
New.  
James Troup 已提交
944

945
        return self.reject_message
J
New.  
James Troup 已提交
946 947 948 949

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

    def check_source_against_db(self, file):
950 951
        self.reject_message = ""
        dsc = self.pkg.dsc
J
New.  
James Troup 已提交
952

953 954 955
        # Ensure version is sane
        q = self.projectB.query("""
SELECT s.version, su.suite_name FROM source s, src_associations sa, suite su
956
 WHERE s.source = '%s' AND sa.source = s.id AND sa.suite = su.id""" % (dsc.get("source")))
957 958
        self.cross_suite_version_check(q.getresult(), file, dsc.get("version"),
            sourceful=True)
959

960
        return self.reject_message
J
New.  
James Troup 已提交
961 962 963

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

964 965 966 967 968
    # **WARNING**
    # NB: this function can remove entries from the 'files' index [if
    # the .orig.tar.gz is a duplicate of the one in the archive]; if
    # you're iterating over 'files' and call this function as part of
    # the loop, be sure to add a check to the top of the loop to
J
James Troup 已提交
969
    # ensure you haven't just tried to dereference the deleted entry.
970 971
    # **WARNING**

J
New.  
James Troup 已提交
972
    def check_dsc_against_db(self, file):
973 974 975 976 977
        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 已提交
978 979 980 981

        # 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.
982 983
        # The ordering on the select is needed to pick the newest orig
        # when it exists in multiple places.
J
New.  
James Troup 已提交
984
        for dsc_file in dsc_files.keys():
985
            found = None
J
New.  
James Troup 已提交
986
            if files.has_key(dsc_file):
987 988
                actual_md5 = files[dsc_file]["md5sum"]
                actual_size = int(files[dsc_file]["size"])
J
New.  
James Troup 已提交
989 990
                found = "%s in incoming" % (dsc_file)
                # Check the file does not already exist in the archive
991
                q = self.projectB.query("SELECT f.size, f.md5sum, l.path, f.filename FROM files f, location l WHERE f.filename LIKE '%%%s%%' AND l.id = f.location ORDER BY f.id DESC" % (dsc_file))
992
                ql = q.getresult()
993 994
                # Strip out anything that isn't '%s' or '/%s$'
                for i in ql:
995
                    if i[3] != dsc_file and i[3][-(len(dsc_file)+1):] != '/'+dsc_file:
996
                        ql.remove(i)
997

998
                # "[dak] has not broken them.  [dak] has fixed a
J
New.  
James Troup 已提交
999 1000 1001 1002 1003 1004 1005 1006
                # 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

1007
                if ql:
1008
                    # Ignore exact matches for .orig.tar.gz
1009
                    match = 0
1010
                    if dsc_file.endswith(".orig.tar.gz"):
1011
                        for i in ql:
1012 1013
                            if files.has_key(dsc_file) and \
                               int(files[dsc_file]["size"]) == int(i[0]) and \
1014
                               files[dsc_file]["md5sum"] == i[1]:
1015 1016 1017 1018
                                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
1019 1020

                    if not match:
1021
                        self.reject("can not overwrite existing copy of '%s' already in the archive." % (dsc_file))
1022
            elif dsc_file.endswith(".orig.tar.gz"):
J
New.  
James Troup 已提交
1023
                # Check in the pool
1024 1025
                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()
1026 1027 1028
                # 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:
1029
                        ql.remove(i)
J
New.  
James Troup 已提交
1030

1031
                if ql:
1032 1033 1034
                    # 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 已提交
1035

J
Various  
Joerg Jaspert 已提交
1036 1037
                    # default to something sane in case we don't match any or have only one
                    x = ql[0]
J
New.  
James Troup 已提交
1038 1039 1040

                    if len(ql) > 1:
                        for i in ql:
1041
                            old_file = i[0] + i[1]
1042
                            old_file_fh = utils.open_file(old_file)
1043
                            actual_md5 = apt_pkg.md5sum(old_file_fh)
1044
                            old_file_fh.close()
1045
                            actual_size = os.stat(old_file)[stat.ST_SIZE]
J
New.  
James Troup 已提交
1046
                            if actual_md5 == dsc_files[dsc_file]["md5sum"] and actual_size == int(dsc_files[dsc_file]["size"]):
1047
                                x = i
J
New.  
James Troup 已提交
1048
                            else:
1049
                                legacy_source_untouchable[i[3]] = ""
J
New.  
James Troup 已提交
1050

1051
                    old_file = x[0] + x[1]
1052
                    old_file_fh = utils.open_file(old_file)
1053
                    actual_md5 = apt_pkg.md5sum(old_file_fh)
1054
                    old_file_fh.close()
1055 1056 1057
                    actual_size = os.stat(old_file)[stat.ST_SIZE]
                    found = old_file
                    suite_type = x[2]
J
Various  
Joerg Jaspert 已提交
1058 1059
                    # need this for updating dsc_files in install()
                    dsc_files[dsc_file]["files id"] = x[3]
1060
                    # See install() in process-accepted...
1061 1062
                    self.pkg.orig_tar_id = x[3]
                    self.pkg.orig_tar_gz = old_file
J
New.  
James Troup 已提交
1063
                    if suite_type == "legacy" or suite_type == "legacy-mixed":
1064
                        self.pkg.orig_tar_location = "legacy"
J
New.  
James Troup 已提交
1065
                    else:
1066
                        self.pkg.orig_tar_location = x[4]
J
New.  
James Troup 已提交
1067
                else:
1068
                    # Not there? Check the queue directories...
J
New.  
James Troup 已提交
1069

1070
                    in_unchecked = os.path.join(self.Cnf["Dir::Queue::Unchecked"],dsc_file)
1071
                    # See process_it() in 'dak process-unchecked' for explanation of this
1072 1073
                    # in_unchecked check dropped by ajt 2007-08-28, how did that
                    # ever make sense?
1074
                    if os.path.exists(in_unchecked) and False:
1075
                        return (self.reject_message, in_unchecked)
J
New.  
James Troup 已提交
1076
                    else:
J
Joerg Jaspert 已提交
1077 1078
                        for directory in [ "Accepted", "New", "Byhand", "ProposedUpdates", "OldProposedUpdates" ]:
                            in_otherdir = os.path.join(self.Cnf["Dir::Queue::%s" % (directory)],dsc_file)
1079
                            if os.path.exists(in_otherdir):
1080
                                in_otherdir_fh = utils.open_file(in_otherdir)
1081
                                actual_md5 = apt_pkg.md5sum(in_otherdir_fh)
1082
                                in_otherdir_fh.close()
1083 1084 1085
                                actual_size = os.stat(in_otherdir)[stat.ST_SIZE]
                                found = in_otherdir
                                self.pkg.orig_tar_gz = in_otherdir
1086 1087

                    if not found:
1088 1089 1090
                        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 已提交
1091
            else:
1092 1093
                self.reject("%s refers to %s, but I can't find it in the queue." % (file, dsc_file))
                continue
J
New.  
James Troup 已提交
1094
            if actual_md5 != dsc_files[dsc_file]["md5sum"]:
1095
                self.reject("md5sum for %s doesn't match %s." % (found, file))
J
New.  
James Troup 已提交
1096
            if actual_size != int(dsc_files[dsc_file]["size"]):
1097
                self.reject("size for %s doesn't match %s." % (found, file))
J
New.  
James Troup 已提交
1098

1099
        return (self.reject_message, None)
J
James Troup 已提交
1100 1101

    def do_query(self, q):
1102 1103 1104 1105 1106 1107
        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