3 # Utility functions for katie
4 # Copyright (C) 2001 James Troup <james@nocrew.org>
5 # $Id: katie.py,v 1.1 2002-02-12 23:08:07 troup 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;
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" ]:
162 d_changes[i] = changes[i];
163 # Optional changes fields
164 for i in [ "changed-by", "changedby822", "maintainer822", "filecontents" ]:
165 d_changes[i] = changes[i];
167 for i in [ "source", "version", "maintainer", "fingerprint" ]:
171 for file in dsc_files.keys():
172 d_dsc_files[file] = {};
173 # Mandatory dsc_files fields
174 for i in [ "size", "md5sum" ]:
175 d_dsc_files[file][i] = dsc_files[file][i];
176 # Optional dsc_files fields
177 for i in [ "files id" ]:
178 if dsc_files[file].has_key(i):
179 d_dsc_files[file][i] = dsc_files[file][i];
181 for i in [ d_changes, d_dsc, d_files, d_dsc_files,
182 legacy_source_untouchable, orig_tar_id, orig_tar_location ]:
186 ###########################################################################
188 # Set up the per-package template substitution mappings
190 def update_subst (self, reject_message = ""):
192 changes = self.pkg.changes;
193 # If jennifer crashed out in the right place, architecture may still be a string.
194 if not changes.has_key("architecture") or not isinstance(changes["architecture"], DictType):
195 changes["architecture"] = { "Unknown" : "" };
196 # and maintainer822 may not exist.
197 if not changes.has_key("maintainer822"):
198 changes["maintainer822"] = self.Cnf["Dinstall::MyEmailAddress"];
200 Subst["__ARCHITECTURE__"] = string.join(changes["architecture"].keys(), ' ' );
201 Subst["__CHANGES_FILENAME__"] = os.path.basename(self.pkg.changes_file);
202 Subst["__FILE_CONTENTS__"] = changes.get("filecontents", "");
204 # For source uploads the Changed-By field wins; otherwise Maintainer wins.
205 if changes["architecture"].has_key("source") and changes["changedby822"] != "" and (changes["changedby822"] != changes["maintainer822"]):
206 Subst["__MAINTAINER_FROM__"] = changes["changedby822"];
207 Subst["__MAINTAINER_TO__"] = changes["changedby822"] + ", " + changes["maintainer822"];
208 Subst["__MAINTAINER__"] = changes.get("changed-by", "Unknown");
210 Subst["__MAINTAINER_FROM__"] = changes["maintainer822"];
211 Subst["__MAINTAINER_TO__"] = changes["maintainer822"];
212 Subst["__MAINTAINER__"] = changes.get("maintainer", "Unknown");
213 if self.Cnf.has_key("Dinstall::TrackingServer") and changes.has_key("source"):
214 Subst["__MAINTAINER_TO__"] = Subst["__MAINTAINER_TO__"] + "\nBcc: %s@%s" % (changes["source"], Cnf["Dinstall::TrackingServer"])
216 Subst["__REJECT_MESSAGE__"] = reject_message;
217 Subst["__SOURCE__"] = changes.get("source", "Unknown");
218 Subst["__VERSION__"] = changes.get("version", "Unknown");
220 ###########################################################################
222 def build_summaries(self):
223 changes = self.pkg.changes;
224 files = self.pkg.files;
226 byhand = summary = new = "";
228 # changes["distribution"] may not exist in corner cases
229 # (e.g. unreadable changes files)
230 if not changes.has_key("distribution") or not isinstance(changes["distribution"], DictType):
231 changes["distribution"] = {};
233 file_keys = files.keys();
235 for file in file_keys:
236 if files[file].has_key("byhand"):
238 summary = summary + file + " byhand\n"
239 elif files[file].has_key("new"):
241 summary = summary + "(new) %s %s %s\n" % (file, files[file]["priority"], files[file]["section"])
242 if files[file].has_key("othercomponents"):
243 summary = summary + "WARNING: Already present in %s distribution.\n" % (files[file]["othercomponents"])
244 if files[file]["type"] == "deb":
245 summary = summary + apt_pkg.ParseSection(apt_inst.debExtractControl(utils.open_file(file)))["Description"] + '\n';
247 files[file]["pool name"] = utils.poolify (changes["source"], files[file]["component"])
248 destination = self.Cnf["Dir::PoolRoot"] + files[file]["pool name"] + file
249 summary = summary + file + "\n to " + destination + "\n"
251 short_summary = summary;
253 # This is for direport's benefit...
254 f = re_fdnic.sub("\n .\n", changes.get("changes",""));
257 summary = summary + "Changes: " + f;
259 summary = summary + self.announce(short_summary, 0)
261 return (summary, short_summary);
263 ###########################################################################
265 def announce (self, short_summary, action):
268 changes = self.pkg.changes;
271 # Only do announcements for source uploads with a recent dpkg-dev installed
272 if float(changes.get("format", 0)) < 1.6 or not changes["architecture"].has_key("source"):
277 Subst["__SHORT_SUMMARY__"] = short_summary;
279 for dist in changes["distribution"].keys():
280 list = Cnf.Find("Suite::%s::Announce" % (dist))
281 if list == "" or lists_done.has_key(list):
284 summary = summary + "Announcing to %s\n" % (list)
287 Subst["__ANNOUNCE_LIST_ADDRESS__"] = list;
288 mail_message = utils.TemplateSubst(Subst,open(Cnf["Dir::TemplatesDir"]+"/jennifer.announce","r").read());
289 utils.send_mail (mail_message, "")
291 bugs = changes["closes"].keys()
293 if not self.nmu.is_an_nmu(self.pkg):
294 summary = summary + "Closing bugs: "
296 summary = summary + "%s " % (bug)
298 Subst["__BUG_NUMBER__"] = bug;
299 if changes["distribution"].has_key("stable"):
300 Subst["__STABLE_WARNING__"] = """
301 Note that this package is not part of the released stable Debian
302 distribution. It may have dependencies on other unreleased software,
303 or other instabilities. Please take care if you wish to install it.
304 The update will eventually make its way into the next released Debian
307 Subst["__STABLE_WARNING__"] = "";
308 mail_message = utils.TemplateSubst(Subst,open(Cnf["Dir::TemplatesDir"]+"/jennifer.bug-close","r").read());
309 utils.send_mail (mail_message, "")
311 self.Logger.log(["closing bugs"]+bugs);
313 summary = summary + "Setting bugs to severity fixed: "
316 summary = summary + "%s " % (bug)
317 control_message = control_message + "tag %s + fixed\n" % (bug)
318 if action and control_message != "":
319 Subst["__CONTROL_MESSAGE__"] = control_message;
320 mail_message = utils.TemplateSubst(Subst,open(Cnf["Dir::TemplatesDir"]+"/jennifer.bug-nmu-fixed","r").read());
321 utils.send_mail (mail_message, "")
323 self.Logger.log(["setting bugs to fixed"]+bugs);
324 summary = summary + "\n"
328 ###########################################################################
330 def accept (self, summary, short_summary):
333 files = self.pkg.files;
337 self.dump_vars(Cnf["Dir::QueueAcceptedDir"]);
339 # Move all the files into the accepted directory
340 utils.move(self.pkg.changes_file, Cnf["Dir::QueueAcceptedDir"]);
341 file_keys = files.keys();
342 for file in file_keys:
343 utils.move(file, Cnf["Dir::QueueAcceptedDir"]);
344 self.accept_bytes = self.accept_bytes + float(files[file]["size"])
345 self.accept_count = self.accept_count + 1;
347 # Send accept mail, announce to lists, close bugs and check for
348 # override disparities
349 if not Cnf["Dinstall::Options::No-Mail"]:
350 Subst["__SUITE__"] = "";
351 Subst["__SUMMARY__"] = summary;
352 mail_message = utils.TemplateSubst(Subst,open(Cnf["Dir::TemplatesDir"]+"/jennifer.accepted","r").read());
353 utils.send_mail(mail_message, "")
354 self.announce(short_summary, 1)
356 ###########################################################################
358 def check_override (self):
360 changes = self.pkg.changes;
361 files = self.pkg.files;
363 # Only check section & priority on sourceful uploads
364 if not changes["architecture"].has_key("source"):
368 for file in files.keys():
369 if not files[file].has_key("new") and files[file]["type"] == "deb":
370 section = files[file]["section"];
371 override_section = files[file]["override section"];
372 if lower(section) != lower(override_section) and section != "-":
373 # Ignore this; it's a common mistake and not worth whining about
374 if lower(section) == "non-us/main" and lower(override_section) == "non-us":
376 summary = summary + "%s: section is overridden from %s to %s.\n" % (file, section, override_section);
377 priority = files[file]["priority"];
378 override_priority = files[file]["override priority"];
379 if priority != override_priority and priority != "-":
380 summary = summary + "%s: priority is overridden from %s to %s.\n" % (file, priority, override_priority);
385 Subst["__SUMMARY__"] = summary;
386 mail_message = utils.TemplateSubst(Subst,utils.open_file(self.Cnf["Dir::TemplatesDir"]+"/jennifer.override-disparity").read());
387 utils.send_mail (mail_message, "");
389 ###########################################################################
391 def force_move (self, files):
392 """Forcefully move files from the current directory to the reject
393 directory. If any file already exists it will be moved to the
394 morgue to make way for the new file."""
399 # Skip any files which don't exist or which we don't have permission to copy.
400 if os.access(file,os.R_OK) == 0:
402 dest_file = os.path.join(Cnf["Dir::QueueRejectDir"], file);
404 os.open(dest_file, os.O_RDWR|os.O_CREAT|os.O_EXCL, 0644);
406 # File exists? Let's try and move it to the morgue
407 if errno.errorcode[e.errno] == 'EEXIST':
408 morgue_file = os.path.join(Cnf["Dir::Morgue"],Cnf["Dir::MorgueRejectDir"],file);
410 morgue_file = utils.find_next_free(morgue_file);
411 except utils.tried_too_hard_exc:
412 # Something's either gone badly Pete Tong, or
413 # someone is trying to exploit us.
414 utils.warn("**WARNING** failed to move %s from the reject directory to the morgue." % (file));
416 utils.move(dest_file, morgue_file);
418 os.open(dest_file, os.O_RDWR|os.O_CREAT|os.O_EXCL, 0644);
421 utils.warn("**WARNING** failed to claim %s in the reject directory." % (file));
425 # If we got here, we own the destination file, so we can
426 # safely overwrite it.
427 utils.move(file, dest_file, 1);
429 ###########################################################################
431 def do_reject (self, manual = 0, reject_message = ""):
438 reason_filename = pkg.changes_file[:-8] + ".reason";
439 reject_filename = Cnf["Dir::QueueRejectDir"] + '/' + reason_filename;
441 # Move all the files into the reject directory
442 reject_files = pkg.files.keys() + [pkg.changes_file];
443 self.force_move(reject_files);
445 # If we fail here someone is probably trying to exploit the race
446 # so let's just raise an exception ...
447 if os.path.exists(reject_filename):
448 os.unlink(reject_filename);
449 fd = os.open(reject_filename, os.O_RDWR|os.O_CREAT|os.O_EXCL, 0644);
452 Subst["__REJECTOR_ADDRESS__"] = Cnf["Dinstall::MyEmailAddress"];
453 Subst["__MANUAL_REJECT_MESSAGE__"] = "";
454 Subst["__CC__"] = "X-Katie-Rejection: automatic (moo)";
455 os.write(fd, reject_message);
458 # Build up the rejection email
459 user_email_address = utils.whoami() + " <%s>" % (Cnf["Dinstall::MyAdminAddress"]);
461 Subst["__REJECTOR_ADDRESS__"] = user_email_address;
462 Subst["__MANUAL_REJECT_MESSAGE__"] = reject_message;
463 Subst["__CC__"] = "Cc: " + Cnf["Dinstall::MyEmailAddress"];
464 reject_mail_message = utils.TemplateSubst(Subst,utils.open_file(Cnf["Dir::TemplatesDir"]+"/katie.rejected").read());
466 # Write the rejection email out as the <foo>.reason file
467 os.write(fd, reject_mail_message);
470 # If we weren't given a manual rejection message, spawn an
471 # editor so the user can add one in...
472 if reject_message == "":
473 editor = os.environ.get("EDITOR","vi")
474 result = os.system("%s +6 %s" % (editor, reject_filename))
476 utils.fubar("editor invocation failed for '%s'!" % (reject_filename), result);
478 # Send the rejection mail if appropriate
479 if not Cnf["Dinstall::Options::No-Mail"]:
480 reject_mail_message = utils.TemplateSubst(Subst,utils.open_file(Cnf["Dir::TemplatesDir"]+"/katie.rejected").read());
481 utils.send_mail (reject_mail_message, "");
483 self.Logger.log(["rejected", pkg.changes_file]);
485 ################################################################################
487 # Ensure that source exists somewhere in the archive for the binary
488 # upload being processed.
490 # (1) exact match => 1.0-3
491 # (2) Bin-only NMU of an MU => 1.0-3.0.1
492 # (3) Bin-only NMU of a sourceful-NMU => 1.0-3.1.1
494 def source_exists (self, package, source_version):
495 q = self.projectB.query("SELECT s.version FROM source s WHERE s.source = '%s'" % (package));
497 # Reduce the query results to a list of version numbers
498 ql = map(lambda x: x[0], q.getresult());
501 if ql.count(source_version):
505 orig_source_version = re_bin_only_nmu_of_mu.sub('', source_version);
506 if ql.count(orig_source_version):
510 orig_source_version = re_bin_only_nmu_of_nmu.sub('', source_version);
511 if ql.count(orig_source_version):
517 ################################################################################
519 def in_override_p (self, package, component, suite, binary_type, file):
520 files = self.pkg.files;
522 if binary_type == "": # must be source
527 # Override suite name; used for example with proposed-updates
528 if self.Cnf.Find("Suite::%s::OverrideSuite" % (suite)) != "":
529 suite = self.Cnf["Suite::%s::OverrideSuite" % (suite)];
531 # Avoid <undef> on unknown distributions
532 suite_id = db_access.get_suite_id(suite);
535 component_id = db_access.get_component_id(component);
536 type_id = db_access.get_override_type_id(type);
538 # FIXME: nasty non-US speficic hack
539 if lower(component[:7]) == "non-us/":
540 component = component[7:];
542 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"
543 % (package, suite_id, component_id, type_id));
544 result = q.getresult();
545 # If checking for a source package fall back on the binary override type
546 if type == "dsc" and not result:
547 type_id = db_access.get_override_type_id("deb");
548 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"
549 % (package, suite_id, component_id, type_id));
550 result = q.getresult();
552 # Remember the section and priority so we can check them later if appropriate
554 files[file]["override section"] = result[0][0];
555 files[file]["override priority"] = result[0][1];
559 ################################################################################
561 def reject (self, str, prefix="Rejected: "):
563 self.reject_message = self.reject_message + prefix + str + "\n";
565 def check_binaries_against_db(self, file, suite):
566 self.reject_message = "";
567 files = self.pkg.files;
569 # Find any old binary packages
570 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"
571 % (files[file]["package"], suite, files[file]["architecture"]))
572 for oldfile in q.dictresult():
573 files[file]["oldfiles"][suite] = oldfile;
574 # Check versions [NB: per-suite only; no cross-suite checking done (yet)]
575 if apt_pkg.VersionCompare(files[file]["version"], oldfile["version"]) != 1:
576 reject("%s Old version `%s' >= new version `%s'." % (file, oldfile["version"], files[file]["version"]));
577 # Check for any existing copies of the file
578 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"]))
579 if q.getresult() != []:
580 reject("can not overwrite existing copy of '%s' already in the archive." % (file));
582 return self.reject_message;
584 ################################################################################
586 def check_source_against_db(self, file):
587 """Ensure source is newer than existing source in target suites."""
588 self.reject_message = "";
589 changes = self.pkg.changes;
592 package = dsc.get("source");
593 new_version = dsc.get("version");
594 for suite in changes["distribution"].keys():
595 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"
597 ql = map(lambda x: x[0], q.getresult());
598 for old_version in ql:
599 if apt_pkg.VersionCompare(new_version, old_version) != 1:
600 reject("%s: Old version `%s' >= new version `%s'." % (file, old_version, new_version));
601 return self.reject_message;
603 ################################################################################
605 def check_dsc_against_db(self, file):
606 self.reject_message = "";
607 files = self.pkg.files;
608 dsc_files = self.pkg.dsc_files;
609 legacy_source_untouchable = self.pkg.legacy_source_untouchable;
612 # Try and find all files mentioned in the .dsc. This has
613 # to work harder to cope with the multiple possible
614 # locations of an .orig.tar.gz.
615 for dsc_file in dsc_files.keys():
616 if files.has_key(dsc_file):
617 actual_md5 = files[dsc_file]["md5sum"];
618 actual_size = int(files[dsc_file]["size"]);
619 found = "%s in incoming" % (dsc_file)
620 # Check the file does not already exist in the archive
621 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));
623 # "It has not broken them. It has fixed a
624 # brokenness. Your crappy hack exploited a bug in
627 # "(Come on! I thought it was always obvious that
628 # one just doesn't release different files with
629 # the same name and version.)"
630 # -- ajk@ on d-devel@l.d.o
632 if q.getresult() != []:
633 reject("can not overwrite existing copy of '%s' already in the archive." % (dsc_file));
634 elif dsc_file[-12:] == ".orig.tar.gz":
636 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));
640 # Unfortunately, we make get more than one
641 # match here if, for example, the package was
642 # in potato but had a -sa upload in woody. So
643 # we need to choose the right one.
645 x = ql[0]; # default to something sane in case we don't match any or have only one
649 old_file = i[0] + i[1];
650 actual_md5 = apt_pkg.md5sum(utils.open_file(old_file));
651 actual_size = os.stat(old_file)[stat.ST_SIZE];
652 if actual_md5 == dsc_files[dsc_file]["md5sum"] and actual_size == int(dsc_files[dsc_file]["size"]):
655 legacy_source_untouchable[i[3]] = "";
657 old_file = x[0] + x[1];
658 actual_md5 = apt_pkg.md5sum(utils.open_file(old_file));
659 actual_size = os.stat(old_file)[stat.ST_SIZE];
662 dsc_files[dsc_file]["files id"] = x[3]; # need this for updating dsc_files in install()
663 # See install() in katie...
664 self.pkg.orig_tar_id = x[3];
665 if suite_type == "legacy" or suite_type == "legacy-mixed":
666 self.pkg.orig_tar_location = "legacy";
668 self.pkg.orig_tar_location = x[4];
671 # Not there? Check in Incoming...
672 # [See comment above jennifer's process_it() for
673 # explanation of why this is necessary...]
674 orig_tar_gz = self.pkg.directory + '/' + dsc_file;
675 if os.path.exists(orig_tar_gz):
676 return (self.reject_message, orig_tar_gz);
678 reject("%s refers to %s, but I can't find it in Incoming or in the pool." % (file, dsc_file));
681 reject("%s refers to %s, but I can't find it in Incoming." % (file, dsc_file));
683 if actual_md5 != dsc_files[dsc_file]["md5sum"]:
684 reject("md5sum for %s doesn't match %s." % (found, file));
685 if actual_size != int(dsc_files[dsc_file]["size"]):
686 reject("size for %s doesn't match %s." % (found, file));
688 return (self.reject_message, orig_tar_gz);