7 @contact: Debian FTP Master <ftpmaster@debian.org>
8 @copyright: 2001 - 2006 James Troup <james@nocrew.org>
9 @copyright: 2009 Joerg Jaspert <joerg@debian.org>
10 @copyright: 2009 Mark Hymers <mhy@debian.org>
11 @license: GNU General Public License version 2 or later
14 # This program is free software; you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation; either version 2 of the License, or
17 # (at your option) any later version.
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
24 # You should have received a copy of the GNU General Public License
25 # along with this program; if not, write to the Free Software
26 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 ###############################################################################
34 from cPickle import Unpickler, Pickler
35 from errno import EPERM
37 from apt_pkg import TagSection
39 from utils import open_file, fubar, poolify, deb_extract_control
43 ###############################################################################
47 ###############################################################################
49 CHANGESFIELDS_MANDATORY = [ "distribution", "source", "architecture",
50 "version", "maintainer", "urgency", "fingerprint", "changedby822",
51 "changedby2047", "changedbyname", "maintainer822", "maintainer2047",
52 "maintainername", "maintaineremail", "closes", "changes" ]
54 __all__.append('CHANGESFIELDS_MANDATORY')
56 CHANGESFIELDS_OPTIONAL = [ "changed-by", "filecontents", "format",
57 "process-new note", "adv id", "distribution-version", "sponsoremail" ]
59 __all__.append('CHANGESFIELDS_OPTIONAL')
61 CHANGESFIELDS_FILES = [ "package", "version", "architecture", "type", "size",
62 "md5sum", "sha1sum", "sha256sum", "component", "location id",
63 "source package", "source version", "maintainer", "dbtype", "files id",
64 "new", "section", "priority", "othercomponents", "pool name",
65 "original component" ]
67 __all__.append('CHANGESFIELDS_FILES')
69 CHANGESFIELDS_DSC = [ "source", "version", "maintainer", "fingerprint",
70 "uploaders", "bts changelog", "dm-upload-allowed" ]
72 __all__.append('CHANGESFIELDS_DSC')
74 CHANGESFIELDS_DSCFILES_MANDATORY = [ "size", "md5sum" ]
76 __all__.append('CHANGESFIELDS_DSCFILES_MANDATORY')
78 CHANGESFIELDS_DSCFILES_OPTIONAL = [ "files id" ]
80 __all__.append('CHANGESFIELDS_DSCFILES_OPTIONAL')
82 CHANGESFIELDS_ORIGFILES = [ "id", "location" ]
84 __all__.append('CHANGESFIELDS_ORIGFILES')
86 ###############################################################################
88 class Changes(object):
89 """ Convenience wrapper to carry around all the package information """
91 def __init__(self, **kwds):
95 self.changes_file = ""
103 def file_summary(self):
104 # changes["distribution"] may not exist in corner cases
105 # (e.g. unreadable changes files)
106 if not self.changes.has_key("distribution") or not \
107 isinstance(self.changes["distribution"], dict):
108 self.changes["distribution"] = {}
113 override_summary = ""
115 for name, entry in sorted(self.files.items()):
116 if entry.has_key("byhand"):
118 summary += name + " byhand\n"
120 elif entry.has_key("new"):
122 summary += "(new) %s %s %s\n" % (name, entry["priority"], entry["section"])
124 if entry.has_key("othercomponents"):
125 summary += "WARNING: Already present in %s distribution.\n" % (entry["othercomponents"])
127 if entry["type"] == "deb":
128 deb_fh = open_file(name)
129 summary += TagSection(deb_extract_control(deb_fh))["Description"] + '\n'
133 entry["pool name"] = poolify(self.changes.get("source", ""), entry["component"])
134 destination = entry["pool name"] + name
135 summary += name + "\n to " + destination + "\n"
137 if not entry.has_key("type"):
138 entry["type"] = "unknown"
140 if entry["type"] in ["deb", "udeb", "dsc"]:
141 # (queue/unchecked), there we have override entries already, use them
142 # (process-new), there we dont have override entries, use the newly generated ones.
143 override_prio = entry.get("override priority", entry["priority"])
144 override_sect = entry.get("override section", entry["section"])
145 override_summary += "%s - %s %s\n" % (name, override_prio, override_sect)
147 return (byhand, new, summary, override_summary)
149 def check_override(self):
151 Checks override entries for validity.
153 Returns an empty string if there are no problems
154 or the text of a warning if there are
159 # Abandon the check if it's a non-sourceful upload
160 if not self.changes["architecture"].has_key("source"):
163 for name, entry in sorted(self.files.items()):
164 if not entry.has_key("new") and entry["type"] == "deb":
165 if entry["section"] != "-":
166 if entry["section"].lower() != entry["override section"].lower():
167 summary += "%s: package says section is %s, override says %s.\n" % (name,
169 entry["override section"])
171 if entry["priority"] != "-":
172 if entry["priority"] != entry["override priority"]:
173 summary += "%s: package says priority is %s, override says %s.\n" % (name,
175 entry["override priority"])
180 def remove_known_changes(self, session=None):
181 session.delete(get_dbchange(self.changes_file, session))
183 def mark_missing_fields(self):
184 """add "missing" in fields which we will require for the known_changes table"""
185 for key in ['urgency', 'maintainer', 'fingerprint', 'changed-by' ]:
186 if (not self.changes.has_key(key)) or (not self.changes[key]):
187 self.changes[key]='missing'
189 def __get_file_from_pool(self, filename, entry, session, logger):
192 if cnf.has_key("Dinstall::SuiteSuffix"):
193 component = cnf["Dinstall::SuiteSuffix"] + entry["component"]
195 component = entry["component"]
197 poolname = poolify(entry["source"], component)
198 l = get_location(cnf["Dir::Pool"], component, session=session)
200 found, poolfile = check_poolfile(os.path.join(poolname, filename),
207 if logger is not None:
208 logger.log(["E: Found multiple files for pool (%s) for %s" % (filename, component)])
210 elif found is False and poolfile is not None:
211 if logger is not None:
212 logger.log(["E: md5sum/size mismatch for %s in pool" % (filename)])
216 if logger is not None:
217 logger.log(["E: Could not find %s in pool" % (filename)])
223 def add_known_changes(self, dirpath, in_queue=None, session=None, logger=None):
224 """add "missing" in fields which we will require for the known_changes table"""
227 changesfile = os.path.join(dirpath, self.changes_file)
228 filetime = datetime.datetime.fromtimestamp(os.path.getctime(changesfile))
230 self.mark_missing_fields()
233 for key in ("distribution", "architecture", "binary"):
234 if isinstance(self.changes[key], dict):
235 multivalues[key] = " ".join(self.changes[key].keys())
237 multivalues[key] = self.changes[key]
240 chg.changesname = self.changes_file
242 chg.in_queue_id = in_queue
243 chg.source = self.changes["source"]
244 chg.binaries = multivalues["binary"]
245 chg.architecture = multivalues["architecture"]
246 chg.version = self.changes["version"]
247 chg.distribution = multivalues["distribution"]
248 chg.urgency = self.changes["urgency"]
249 chg.maintainer = self.changes["maintainer"]
250 chg.fingerprint = self.changes["fingerprint"]
251 chg.changedby = self.changes["changed-by"]
252 chg.date = self.changes["date"]
257 for chg_fn, entry in self.files.items():
259 f = open(os.path.join(dirpath, chg_fn))
260 cpf = ChangePendingFile()
261 cpf.filename = chg_fn
262 cpf.size = entry['size']
263 cpf.md5sum = entry['md5sum']
265 if entry.has_key('sha1sum'):
266 cpf.sha1sum = entry['sha1sum']
269 cpf.sha1sum = apt_pkg.sha1sum(f)
271 if entry.has_key('sha256sum'):
272 cpf.sha256sum = entry['sha256sum']
275 cpf.sha256sum = apt_pkg.sha256sum(f)
282 # Can't find the file, try to look it up in the pool
283 poolfile = self.__get_file_from_pool(chg_fn, entry, session)
285 chg.poolfiles.append(poolfile)
289 # Add files referenced in .dsc, but not included in .changes
290 for name, entry in self.dsc_files.items():
291 if self.files.has_key(name):
294 entry['source'] = self.changes['source']
295 poolfile = self.__get_file_from_pool(name, entry, session, logger)
297 chg.poolfiles.append(poolfile)
300 chg = session.query(DBChange).filter_by(changesname = self.changes_file).one();
304 def unknown_files_fields(self, name):
305 return sorted(list( set(self.files[name].keys()) -
306 set(CHANGESFIELDS_FILES)))
308 def unknown_changes_fields(self):
309 return sorted(list( set(self.changes.keys()) -
310 set(CHANGESFIELDS_MANDATORY + CHANGESFIELDS_OPTIONAL)))
312 def unknown_dsc_fields(self):
313 return sorted(list( set(self.dsc.keys()) -
314 set(CHANGESFIELDS_DSC)))
316 def unknown_dsc_files_fields(self, name):
317 return sorted(list( set(self.dsc_files[name].keys()) -
318 set(CHANGESFIELDS_DSCFILES_MANDATORY + CHANGESFIELDS_DSCFILES_OPTIONAL)))
322 for name, entry in self.files.items():
323 r.append(" %s:" % (name))
324 for i in CHANGESFIELDS_FILES:
326 r.append(" %s: %s" % (i.capitalize(), entry[i]))
327 xfields = self.unknown_files_fields(name)
329 r.append("files[%s] still has following unrecognised keys: %s" % (name, ", ".join(xfields)))
333 def str_changes(self):
335 for i in CHANGESFIELDS_MANDATORY:
336 val = self.changes[i]
337 if isinstance(val, list):
339 elif isinstance(val, dict):
340 val = " ".join(val.keys())
341 r.append(' %s: %s' % (i.capitalize(), val))
343 for i in CHANGESFIELDS_OPTIONAL:
344 if self.changes.has_key(i):
345 r.append(' %s: %s' % (i.capitalize(), self.changes[i]))
347 xfields = self.unknown_changes_fields()
349 r.append("Warning: changes still has the following unrecognised fields: %s" % ", ".join(xfields))
355 for i in CHANGESFIELDS_DSC:
356 if self.dsc.has_key(i):
357 r.append(' %s: %s' % (i.capitalize(), self.dsc[i]))
359 xfields = self.unknown_dsc_fields()
361 r.append("Warning: dsc still has the following unrecognised fields: %s" % ", ".join(xfields))
365 def str_dsc_files(self):
367 for name, entry in self.dsc_files.items():
368 r.append(" %s:" % (name))
369 for i in CHANGESFIELDS_DSCFILES_MANDATORY:
370 r.append(" %s: %s" % (i.capitalize(), entry[i]))
371 for i in CHANGESFIELDS_DSCFILES_OPTIONAL:
373 r.append(" %s: %s" % (i.capitalize(), entry[i]))
374 xfields = self.unknown_dsc_files_fields(name)
376 r.append("dsc_files[%s] still has following unrecognised keys: %s" % (name, ", ".join(xfields)))
383 r.append(" Changes:")
384 r += self.str_changes()
394 r += self.str_files()
398 r.append(" Dsc Files:")
399 r += self.str_dsc_files()
403 __all__.append('Changes')