3 # Utility functions for katie
4 # Copyright (C) 2001 James Troup <james@nocrew.org>
5 # $Id: katie.py,v 1.11 2002-03-14 14:12:04 ajt Exp $
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 ###############################################################################
23 import cPickle, errno, os, pg, re, stat, string, sys, time;
24 import utils, db_access;
25 import apt_inst, apt_pkg;
28 from string import lower;
30 ###############################################################################
32 re_isanum = re.compile (r"^\d+$");
33 re_default_answer = re.compile(r"\[(.*)\]");
34 re_fdnic = re.compile("\n\n");
35 re_bin_only_nmu_of_mu = re.compile("\.\d+\.\d+$");
36 re_bin_only_nmu_of_nmu = re.compile("\.\d+$");
38 ###############################################################################
40 # Convenience wrapper to carry around all the package information in
43 def __init__(self, **kwds):
44 self.__dict__.update(kwds);
46 def update(self, **kwds):
47 self.__dict__.update(kwds);
49 ###############################################################################
52 # Read in the group maintainer override file
53 def __init__ (self, Cnf):
54 self.group_maint = {};
56 if Cnf.get("Dinstall::GroupOverrideFilename"):
57 filename = Cnf["Dir::OverrideDir"] + Cnf["Dinstall::GroupOverrideFilename"];
58 file = utils.open_file(filename);
59 for line in file.readlines():
60 line = lower(string.strip(utils.re_comments.sub('', line)));
62 self.group_maint[line] = 1;
65 def is_an_nmu (self, pkg):
67 changes = pkg.changes;
70 (dsc_rfc822, dsc_name, dsc_email) = utils.fix_maintainer (lower(dsc.get("maintainer",Cnf["Dinstall::MyEmailAddress"])));
71 # changes["changedbyname"] == dsc_name is probably never true, but better safe than sorry
72 if dsc_name == lower(changes["maintainername"]) and \
73 (changes["changedby822"] == "" or lower(changes["changedbyname"]) == dsc_name):
76 if dsc.has_key("uploaders"):
77 uploaders = string.split(lower(dsc["uploaders"]), ",");
80 (rfc822, name, email) = utils.fix_maintainer (string.strip(i));
81 uploadernames[name] = "";
82 if uploadernames.has_key(lower(changes["changedbyname"])):
85 # Some group maintained packages (e.g. Debian QA) are never NMU's
86 if self.group_maint.has_key(lower(changes["maintaineremail"])):
91 ###############################################################################
95 def __init__(self, Cnf):
98 # Read in the group-maint override file
99 self.nmu = nmu_p(Cnf);
100 self.accept_count = 0;
101 self.accept_bytes = 0L;
102 self.pkg = Pkg(changes = {}, dsc = {}, dsc_files = {}, files = {},
103 legacy_source_untouchable = {});
105 # Initialize the substitution template mapping global
106 Subst = self.Subst = {};
107 Subst["__ADMIN_ADDRESS__"] = Cnf["Dinstall::MyAdminAddress"];
108 Subst["__BUG_SERVER__"] = Cnf["Dinstall::BugServer"];
109 Subst["__DISTRO__"] = Cnf["Dinstall::MyDistribution"];
110 Subst["__KATIE_ADDRESS__"] = Cnf["Dinstall::MyEmailAddress"];
112 self.projectB = pg.connect(Cnf["DB::Name"], Cnf["DB::Host"], int(Cnf["DB::Port"]));
113 db_access.init(Cnf, self.projectB);
115 ###########################################################################
117 def init_vars (self):
118 for i in [ "changes", "dsc", "files", "dsc_files", "legacy_source_untouchable" ]:
119 exec "self.pkg.%s.clear();" % (i);
120 self.pkg.orig_tar_id = None;
121 self.pkg.orig_tar_location = "";
123 ###########################################################################
125 def update_vars (self):
126 dump_filename = self.pkg.changes_file[:-8]+".katie";
127 dump_file = utils.open_file(dump_filename);
128 p = cPickle.Unpickler(dump_file);
129 for i in [ "changes", "dsc", "files", "dsc_files", "legacy_source_untouchable" ]:
130 exec "self.pkg.%s.update(p.load());" % (i);
131 for i in [ "orig_tar_id", "orig_tar_location" ]:
132 exec "self.pkg.%s = p.load();" % (i);
135 ###########################################################################
137 # This could just dump the dictionaries as is, but I'd like to avoid
138 # this so there's some idea of what katie & lisa use from jennifer
140 def dump_vars(self, dest_dir):
141 for i in [ "changes", "dsc", "files", "dsc_files",
142 "legacy_source_untouchable", "orig_tar_id", "orig_tar_location" ]:
143 exec "%s = self.pkg.%s;" % (i,i);
144 dump_filename = os.path.join(dest_dir,self.pkg.changes_file[:-8] + ".katie");
145 dump_file = utils.open_file(dump_filename, 'w');
146 p = cPickle.Pickler(dump_file, 1);
147 for i in [ "d_changes", "d_dsc", "d_files", "d_dsc_files" ]:
150 for file in files.keys():
152 for i in [ "package", "version", "architecture", "type", "size",
153 "md5sum", "component", "location id", "source package",
154 "source version", "maintainer", "dbtype", "files id",
155 "new", "section", "priority", "oldfiles", "othercomponents" ]:
156 if files[file].has_key(i):
157 d_files[file][i] = files[file][i];
159 # Mandatory changes fields
160 for i in [ "distribution", "source", "architecture", "version", "maintainer",
161 "urgency", "fingerprint", "changedby822", "changedbyname",
162 "maintainername", "maintaineremail", "closes" ]:
163 d_changes[i] = changes[i];
164 # Optional changes fields
165 for i in [ "changed-by", "maintainer822", "filecontents", "format" ]:
166 if changes.has_key(i):
167 d_changes[i] = changes[i];
169 for i in [ "source", "version", "maintainer", "fingerprint", "uploaders" ]:
173 for file in dsc_files.keys():
174 d_dsc_files[file] = {};
175 # Mandatory dsc_files fields
176 for i in [ "size", "md5sum" ]:
177 d_dsc_files[file][i] = dsc_files[file][i];
178 # Optional dsc_files fields
179 for i in [ "files id" ]:
180 if dsc_files[file].has_key(i):
181 d_dsc_files[file][i] = dsc_files[file][i];
183 for i in [ d_changes, d_dsc, d_files, d_dsc_files,
184 legacy_source_untouchable, orig_tar_id, orig_tar_location ]:
188 ###########################################################################
190 # Set up the per-package template substitution mappings
192 def update_subst (self, reject_message = ""):
194 changes = self.pkg.changes;
195 # If jennifer crashed out in the right place, architecture may still be a string.
196 if not changes.has_key("architecture") or not isinstance(changes["architecture"], DictType):
197 changes["architecture"] = { "Unknown" : "" };
198 # and maintainer822 may not exist.
199 if not changes.has_key("maintainer822"):
200 changes["maintainer822"] = self.Cnf["Dinstall::MyEmailAddress"];
202 Subst["__ARCHITECTURE__"] = string.join(changes["architecture"].keys(), ' ' );
203 Subst["__CHANGES_FILENAME__"] = os.path.basename(self.pkg.changes_file);
204 Subst["__FILE_CONTENTS__"] = changes.get("filecontents", "");
206 # For source uploads the Changed-By field wins; otherwise Maintainer wins.
207 if changes["architecture"].has_key("source") and changes["changedby822"] != "" and (changes["changedby822"] != changes["maintainer822"]):
208 Subst["__MAINTAINER_FROM__"] = changes["changedby822"];
209 Subst["__MAINTAINER_TO__"] = changes["changedby822"] + ", " + changes["maintainer822"];
210 Subst["__MAINTAINER__"] = changes.get("changed-by", "Unknown");
212 Subst["__MAINTAINER_FROM__"] = changes["maintainer822"];
213 Subst["__MAINTAINER_TO__"] = changes["maintainer822"];
214 Subst["__MAINTAINER__"] = changes.get("maintainer", "Unknown");
215 if self.Cnf.has_key("Dinstall::TrackingServer") and changes.has_key("source"):
216 Subst["__MAINTAINER_TO__"] = Subst["__MAINTAINER_TO__"] + "\nBcc: %s@%s" % (changes["source"], self.Cnf["Dinstall::TrackingServer"])
218 Subst["__REJECT_MESSAGE__"] = reject_message;
219 Subst["__SOURCE__"] = changes.get("source", "Unknown");
220 Subst["__VERSION__"] = changes.get("version", "Unknown");
222 ###########################################################################
224 def build_summaries(self):
225 changes = self.pkg.changes;
226 files = self.pkg.files;
228 byhand = summary = new = "";
230 # changes["distribution"] may not exist in corner cases
231 # (e.g. unreadable changes files)
232 if not changes.has_key("distribution") or not isinstance(changes["distribution"], DictType):
233 changes["distribution"] = {};
235 file_keys = files.keys();
237 for file in file_keys:
238 if files[file].has_key("byhand"):
240 summary = summary + file + " byhand\n"
241 elif files[file].has_key("new"):
243 summary = summary + "(new) %s %s %s\n" % (file, files[file]["priority"], files[file]["section"])
244 if files[file].has_key("othercomponents"):
245 summary = summary + "WARNING: Already present in %s distribution.\n" % (files[file]["othercomponents"])
246 if files[file]["type"] == "deb":
247 summary = summary + apt_pkg.ParseSection(apt_inst.debExtractControl(utils.open_file(file)))["Description"] + '\n';
249 files[file]["pool name"] = utils.poolify (changes["source"], files[file]["component"])
250 destination = self.Cnf["Dir::PoolRoot"] + files[file]["pool name"] + file
251 summary = summary + file + "\n to " + destination + "\n"
253 short_summary = summary;
255 # This is for direport's benefit...
256 f = re_fdnic.sub("\n .\n", changes.get("changes",""));
259 summary = summary + "Changes: " + f;
261 summary = summary + self.announce(short_summary, 0)
263 return (summary, short_summary);
265 ###########################################################################
267 def announce (self, short_summary, action):
270 changes = self.pkg.changes;
273 # Only do announcements for source uploads with a recent dpkg-dev installed
274 if float(changes.get("format", 0)) < 1.6 or not changes["architecture"].has_key("source"):
279 Subst["__SHORT_SUMMARY__"] = short_summary;
281 for dist in changes["distribution"].keys():
282 list = Cnf.Find("Suite::%s::Announce" % (dist))
283 if list == "" or lists_done.has_key(list):
286 summary = summary + "Announcing to %s\n" % (list)
289 Subst["__ANNOUNCE_LIST_ADDRESS__"] = list;
290 mail_message = utils.TemplateSubst(Subst,open(Cnf["Dir::TemplatesDir"]+"/jennifer.announce","r").read());
291 utils.send_mail (mail_message, "")
293 bugs = changes["closes"].keys()
295 if not self.nmu.is_an_nmu(self.pkg):
296 summary = summary + "Closing bugs: "
298 summary = summary + "%s " % (bug)
300 Subst["__BUG_NUMBER__"] = bug;
301 if changes["distribution"].has_key("stable"):
302 Subst["__STABLE_WARNING__"] = """
303 Note that this package is not part of the released stable Debian
304 distribution. It may have dependencies on other unreleased software,
305 or other instabilities. Please take care if you wish to install it.
306 The update will eventually make its way into the next released Debian
309 Subst["__STABLE_WARNING__"] = "";
310 mail_message = utils.TemplateSubst(Subst,open(Cnf["Dir::TemplatesDir"]+"/jennifer.bug-close","r").read());
311 utils.send_mail (mail_message, "")
313 self.Logger.log(["closing bugs"]+bugs);
315 summary = summary + "Setting bugs to severity fixed: "
318 summary = summary + "%s " % (bug)
319 control_message = control_message + "tag %s + fixed\n" % (bug)
320 if action and control_message != "":
321 Subst["__CONTROL_MESSAGE__"] = control_message;
322 mail_message = utils.TemplateSubst(Subst,open(Cnf["Dir::TemplatesDir"]+"/jennifer.bug-nmu-fixed","r").read());
323 utils.send_mail (mail_message, "")
325 self.Logger.log(["setting bugs to fixed"]+bugs);
326 summary = summary + "\n"
330 ###########################################################################
332 def accept (self, summary, short_summary):
335 files = self.pkg.files;
338 self.Logger.log(["Accepting changes",self.pkg.changes_file]);
340 self.dump_vars(Cnf["Dir::QueueAcceptedDir"]);
342 # Move all the files into the accepted directory
343 utils.move(self.pkg.changes_file, Cnf["Dir::QueueAcceptedDir"]);
344 file_keys = files.keys();
345 for file in file_keys:
346 utils.move(file, Cnf["Dir::QueueAcceptedDir"]);
347 self.accept_bytes = self.accept_bytes + float(files[file]["size"])
348 self.accept_count = self.accept_count + 1;
350 # Send accept mail, announce to lists, close bugs and check for
351 # override disparities
352 if not Cnf["Dinstall::Options::No-Mail"]:
353 Subst["__SUITE__"] = "";
354 Subst["__SUMMARY__"] = summary;
355 mail_message = utils.TemplateSubst(Subst,open(Cnf["Dir::TemplatesDir"]+"/jennifer.accepted","r").read());
356 utils.send_mail(mail_message, "")
357 self.announce(short_summary, 1)
359 ###########################################################################
361 def check_override (self):
363 changes = self.pkg.changes;
364 files = self.pkg.files;
366 # Only check section & priority on sourceful uploads
367 if not changes["architecture"].has_key("source"):
371 for file in files.keys():
372 if not files[file].has_key("new") and files[file]["type"] == "deb":
373 section = files[file]["section"];
374 override_section = files[file]["override section"];
375 if lower(section) != lower(override_section) and section != "-":
376 # Ignore this; it's a common mistake and not worth whining about
377 if lower(section) == "non-us/main" and lower(override_section) == "non-us":
379 summary = summary + "%s: section is overridden from %s to %s.\n" % (file, section, override_section);
380 priority = files[file]["priority"];
381 override_priority = files[file]["override priority"];
382 if priority != override_priority and priority != "-":
383 summary = summary + "%s: priority is overridden from %s to %s.\n" % (file, priority, override_priority);
388 Subst["__SUMMARY__"] = summary;
389 mail_message = utils.TemplateSubst(Subst,utils.open_file(self.Cnf["Dir::TemplatesDir"]+"/jennifer.override-disparity").read());
390 utils.send_mail (mail_message, "");
392 ###########################################################################
394 def force_move (self, files):
395 """Forcefully move files from the current directory to the reject
396 directory. If any file already exists it will be moved to the
397 morgue to make way for the new file."""
402 # Skip any files which don't exist or which we don't have permission to copy.
403 if os.access(file,os.R_OK) == 0:
405 dest_file = os.path.join(Cnf["Dir::QueueRejectDir"], file);
407 os.open(dest_file, os.O_RDWR|os.O_CREAT|os.O_EXCL, 0644);
409 # File exists? Let's try and move it to the morgue
410 if errno.errorcode[e.errno] == 'EEXIST':
411 morgue_file = os.path.join(Cnf["Dir::Morgue"],Cnf["Dir::MorgueRejectDir"],file);
413 morgue_file = utils.find_next_free(morgue_file);
414 except utils.tried_too_hard_exc:
415 # Something's either gone badly Pete Tong, or
416 # someone is trying to exploit us.
417 utils.warn("**WARNING** failed to move %s from the reject directory to the morgue." % (file));
419 utils.move(dest_file, morgue_file, perms=0660);
421 os.open(dest_file, os.O_RDWR|os.O_CREAT|os.O_EXCL, 0644);
424 utils.warn("**WARNING** failed to claim %s in the reject directory." % (file));
428 # If we got here, we own the destination file, so we can
429 # safely overwrite it.
430 utils.move(file, dest_file, 1, perms=0660);
433 ###########################################################################
435 def do_reject (self, manual = 0, reject_message = ""):
442 reason_filename = pkg.changes_file[:-8] + ".reason";
443 reject_filename = Cnf["Dir::QueueRejectDir"] + '/' + reason_filename;
445 # Move all the files into the reject directory
446 reject_files = pkg.files.keys() + [pkg.changes_file];
447 self.force_move(reject_files);
449 # If we fail here someone is probably trying to exploit the race
450 # so let's just raise an exception ...
451 if os.path.exists(reject_filename):
452 os.unlink(reject_filename);
453 fd = os.open(reject_filename, os.O_RDWR|os.O_CREAT|os.O_EXCL, 0644);
456 Subst["__REJECTOR_ADDRESS__"] = Cnf["Dinstall::MyEmailAddress"];
457 Subst["__MANUAL_REJECT_MESSAGE__"] = "";
458 Subst["__CC__"] = "X-Katie-Rejection: automatic (moo)";
459 os.write(fd, reject_message);
461 reject_mail_message = utils.TemplateSubst(Subst,utils.open_file(Cnf["Dir::TemplatesDir"]+"/katie.rejected").read());
463 # Build up the rejection email
464 user_email_address = utils.whoami() + " <%s>" % (Cnf["Dinstall::MyAdminAddress"]);
466 Subst["__REJECTOR_ADDRESS__"] = user_email_address;
467 Subst["__MANUAL_REJECT_MESSAGE__"] = reject_message;
468 Subst["__CC__"] = "Cc: " + Cnf["Dinstall::MyEmailAddress"];
469 reject_mail_message = utils.TemplateSubst(Subst,utils.open_file(Cnf["Dir::TemplatesDir"]+"/katie.rejected").read());
471 # Write the rejection email out as the <foo>.reason file
472 os.write(fd, reject_mail_message);
475 # If we weren't given a manual rejection message, spawn an
476 # editor so the user can add one in...
477 if reject_message == "":
478 editor = os.environ.get("EDITOR","vi")
479 result = os.system("%s +6 %s" % (editor, reject_filename))
481 utils.fubar("editor invocation failed for '%s'!" % (reject_filename), result);
482 reject_mail_message = utils.open_file(reject_filename).read();
484 # Send the rejection mail if appropriate
485 if not Cnf["Dinstall::Options::No-Mail"]:
486 utils.send_mail (reject_mail_message, "");
488 self.Logger.log(["rejected", pkg.changes_file]);
490 ################################################################################
492 # Ensure that source exists somewhere in the archive for the binary
493 # upload being processed.
495 # (1) exact match => 1.0-3
496 # (2) Bin-only NMU of an MU => 1.0-3.0.1
497 # (3) Bin-only NMU of a sourceful-NMU => 1.0-3.1.1
499 def source_exists (self, package, source_version):
500 q = self.projectB.query("SELECT s.version FROM source s WHERE s.source = '%s'" % (package));
502 # Reduce the query results to a list of version numbers
503 ql = map(lambda x: x[0], q.getresult());
506 if ql.count(source_version):
510 orig_source_version = re_bin_only_nmu_of_mu.sub('', source_version);
511 if ql.count(orig_source_version):
515 orig_source_version = re_bin_only_nmu_of_nmu.sub('', source_version);
516 if ql.count(orig_source_version):
522 ################################################################################
524 def in_override_p (self, package, component, suite, binary_type, file):
525 files = self.pkg.files;
527 if binary_type == "": # must be source
532 # Override suite name; used for example with proposed-updates
533 if self.Cnf.Find("Suite::%s::OverrideSuite" % (suite)) != "":
534 suite = self.Cnf["Suite::%s::OverrideSuite" % (suite)];
536 # Avoid <undef> on unknown distributions
537 suite_id = db_access.get_suite_id(suite);
540 component_id = db_access.get_component_id(component);
541 type_id = db_access.get_override_type_id(type);
543 # FIXME: nasty non-US speficic hack
544 if lower(component[:7]) == "non-us/":
545 component = component[7:];
547 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"
548 % (package, suite_id, component_id, type_id));
549 result = q.getresult();
550 # If checking for a source package fall back on the binary override type
551 if type == "dsc" and not result:
552 type_id = db_access.get_override_type_id("deb");
553 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"
554 % (package, suite_id, component_id, type_id));
555 result = q.getresult();
557 # Remember the section and priority so we can check them later if appropriate
559 files[file]["override section"] = result[0][0];
560 files[file]["override priority"] = result[0][1];
564 ################################################################################
566 def reject (self, str, prefix="Rejected: "):
568 # Unlike other rejects we add new lines first to avoid trailing
569 # new lines when this message is passed back up to a caller.
570 if self.reject_message:
571 self.reject_message = self.reject_message + "\n";
572 self.reject_message = self.reject_message + prefix + str;
574 def check_binaries_against_db(self, file, suite):
575 self.reject_message = "";
576 files = self.pkg.files;
578 # Find any old binary packages
579 q = self.projectB.query("SELECT b.id, b.version, f.filename, l.path, c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f WHERE b.package = '%s' AND s.suite_name = '%s' AND (a.arch_string = '%s' OR a.arch_string = 'all') AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id AND f.location = l.id AND l.component = c.id AND b.file = f.id"
580 % (files[file]["package"], suite, files[file]["architecture"]))
581 for oldfile in q.dictresult():
582 files[file]["oldfiles"][suite] = oldfile;
583 # Check versions [NB: per-suite only; no cross-suite checking done (yet)]
584 if apt_pkg.VersionCompare(files[file]["version"], oldfile["version"]) != 1:
585 self.reject("%s: old version (%s) >= new version (%s)." % (file, oldfile["version"], files[file]["version"]));
586 # Check for any existing copies of the file
587 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"]))
588 if q.getresult() != []:
589 self.reject("can not overwrite existing copy of '%s' already in the archive." % (file));
591 return self.reject_message;
593 ################################################################################
595 def check_source_against_db(self, file):
596 """Ensure source is newer than existing source in target suites."""
597 self.reject_message = "";
598 changes = self.pkg.changes;
601 package = dsc.get("source");
602 new_version = dsc.get("version");
603 for suite in changes["distribution"].keys():
604 q = self.projectB.query("SELECT s.version FROM source s, src_associations sa, suite su WHERE s.source = '%s' AND su.suite_name = '%s' AND sa.source = s.id AND sa.suite = su.id"
606 ql = map(lambda x: x[0], q.getresult());
607 for old_version in ql:
608 if apt_pkg.VersionCompare(new_version, old_version) != 1:
609 self.reject("%s: Old version `%s' >= new version `%s'." % (file, old_version, new_version));
610 return self.reject_message;
612 ################################################################################
614 def check_dsc_against_db(self, file):
615 self.reject_message = "";
616 files = self.pkg.files;
617 dsc_files = self.pkg.dsc_files;
618 legacy_source_untouchable = self.pkg.legacy_source_untouchable;
621 # Try and find all files mentioned in the .dsc. This has
622 # to work harder to cope with the multiple possible
623 # locations of an .orig.tar.gz.
624 for dsc_file in dsc_files.keys():
626 if files.has_key(dsc_file):
627 actual_md5 = files[dsc_file]["md5sum"];
628 actual_size = int(files[dsc_file]["size"]);
629 found = "%s in incoming" % (dsc_file)
630 # Check the file does not already exist in the archive
631 q = self.projectB.query("SELECT f.id FROM files f, location l WHERE (f.filename ~ '/%s$' OR f.filename = '%s') AND l.id = f.location" % (utils.regex_safe(dsc_file), dsc_file));
633 # "It has not broken them. It has fixed a
634 # brokenness. Your crappy hack exploited a bug in
637 # "(Come on! I thought it was always obvious that
638 # one just doesn't release different files with
639 # the same name and version.)"
640 # -- ajk@ on d-devel@l.d.o
642 if q.getresult() != []:
643 self.reject("can not overwrite existing copy of '%s' already in the archive." % (dsc_file));
644 elif dsc_file[-12:] == ".orig.tar.gz":
646 q = self.projectB.query("SELECT l.path, f.filename, l.type, f.id, l.id FROM files f, location l WHERE (f.filename ~ '/%s$' OR f.filename = '%s') AND l.id = f.location" % (utils.regex_safe(dsc_file), dsc_file));
650 # Unfortunately, we make get more than one
651 # match here if, for example, the package was
652 # in potato but had a -sa upload in woody. So
653 # we need to choose the right one.
655 x = ql[0]; # default to something sane in case we don't match any or have only one
659 old_file = i[0] + i[1];
660 actual_md5 = apt_pkg.md5sum(utils.open_file(old_file));
661 actual_size = os.stat(old_file)[stat.ST_SIZE];
662 if actual_md5 == dsc_files[dsc_file]["md5sum"] and actual_size == int(dsc_files[dsc_file]["size"]):
665 legacy_source_untouchable[i[3]] = "";
667 old_file = x[0] + x[1];
668 actual_md5 = apt_pkg.md5sum(utils.open_file(old_file));
669 actual_size = os.stat(old_file)[stat.ST_SIZE];
672 dsc_files[dsc_file]["files id"] = x[3]; # need this for updating dsc_files in install()
673 # See install() in katie...
674 self.pkg.orig_tar_id = x[3];
675 if suite_type == "legacy" or suite_type == "legacy-mixed":
676 self.pkg.orig_tar_location = "legacy";
678 self.pkg.orig_tar_location = x[4];
680 # Not there? Check the queue directories...
682 in_unchecked = os.path.join(self.Cnf["Dir::QueueUncheckedDir"],dsc_file);
683 # See process_it() in jennifer for explanation of this
684 if os.path.exists(in_unchecked):
685 return (self.reject_message, in_unchecked);
687 for dir in [ "Accepted", "New", "Byhand" ]:
688 in_otherdir = os.path.join(self.Cnf["Dir::Queue%sDir" % (dir)],dsc_file);
689 if os.path.exists(in_otherdir):
690 actual_md5 = apt_pkg.md5sum(utils.open_file(in_otherdir));
691 actual_size = os.stat(in_otherdir)[stat.ST_SIZE];
695 self.reject("%s refers to %s, but I can't find it in the queue or in the pool." % (file, dsc_file));
698 self.reject("%s refers to %s, but I can't find it in the queue." % (file, dsc_file));
700 if actual_md5 != dsc_files[dsc_file]["md5sum"]:
701 self.reject("md5sum for %s doesn't match %s." % (found, file));
702 if actual_size != int(dsc_files[dsc_file]["size"]):
703 self.reject("size for %s doesn't match %s." % (found, file));
705 return (self.reject_message, orig_tar_gz);
707 def do_query(self, q):
708 sys.stderr.write("query: \"%s\" ... " % (q));
709 before = time.time();
710 r = self.projectB.query(q);
711 time_diff = time.time()-before;
712 sys.stderr.write("took %.3f seconds.\n" % (time_diff));