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_inst import debExtractControl
38 from apt_pkg import ParseSection
40 from utils import open_file, fubar, poolify
44 ###############################################################################
48 ###############################################################################
50 CHANGESFIELDS_MANDATORY = [ "distribution", "source", "architecture",
51 "version", "maintainer", "urgency", "fingerprint", "changedby822",
52 "changedby2047", "changedbyname", "maintainer822", "maintainer2047",
53 "maintainername", "maintaineremail", "closes", "changes" ]
55 __all__.append('CHANGESFIELDS_MANDATORY')
57 CHANGESFIELDS_OPTIONAL = [ "changed-by", "filecontents", "format",
58 "process-new note", "adv id", "distribution-version", "sponsoremail" ]
60 __all__.append('CHANGESFIELDS_OPTIONAL')
62 CHANGESFIELDS_FILES = [ "package", "version", "architecture", "type", "size",
63 "md5sum", "sha1sum", "sha256sum", "component", "location id",
64 "source package", "source version", "maintainer", "dbtype", "files id",
65 "new", "section", "priority", "othercomponents", "pool name",
66 "original component" ]
68 __all__.append('CHANGESFIELDS_FILES')
70 CHANGESFIELDS_DSC = [ "source", "version", "maintainer", "fingerprint",
71 "uploaders", "bts changelog", "dm-upload-allowed" ]
73 __all__.append('CHANGESFIELDS_DSC')
75 CHANGESFIELDS_DSCFILES_MANDATORY = [ "size", "md5sum" ]
77 __all__.append('CHANGESFIELDS_DSCFILES_MANDATORY')
79 CHANGESFIELDS_DSCFILES_OPTIONAL = [ "files id" ]
81 __all__.append('CHANGESFIELDS_DSCFILES_OPTIONAL')
83 CHANGESFIELDS_ORIGFILES = [ "id", "location" ]
85 __all__.append('CHANGESFIELDS_ORIGFILES')
87 ###############################################################################
89 class Changes(object):
90 """ Convenience wrapper to carry around all the package information """
92 def __init__(self, **kwds):
96 self.changes_file = ""
104 def file_summary(self):
105 # changes["distribution"] may not exist in corner cases
106 # (e.g. unreadable changes files)
107 if not self.changes.has_key("distribution") or not \
108 isinstance(self.changes["distribution"], dict):
109 self.changes["distribution"] = {}
114 override_summary = ""
116 for name, entry in sorted(self.files.items()):
117 if entry.has_key("byhand"):
119 summary += name + " byhand\n"
121 elif entry.has_key("new"):
123 summary += "(new) %s %s %s\n" % (name, entry["priority"], entry["section"])
125 if entry.has_key("othercomponents"):
126 summary += "WARNING: Already present in %s distribution.\n" % (entry["othercomponents"])
128 if entry["type"] == "deb":
129 deb_fh = open_file(name)
130 summary += ParseSection(debExtractControl(deb_fh))["Description"] + '\n'
134 entry["pool name"] = poolify(self.changes.get("source", ""), entry["component"])
135 destination = entry["pool name"] + name
136 summary += name + "\n to " + destination + "\n"
138 if not entry.has_key("type"):
139 entry["type"] = "unknown"
141 if entry["type"] in ["deb", "udeb", "dsc"]:
142 # (queue/unchecked), there we have override entries already, use them
143 # (process-new), there we dont have override entries, use the newly generated ones.
144 override_prio = entry.get("override priority", entry["priority"])
145 override_sect = entry.get("override section", entry["section"])
146 override_summary += "%s - %s %s\n" % (name, override_prio, override_sect)
148 return (byhand, new, summary, override_summary)
150 def check_override(self):
152 Checks override entries for validity.
154 Returns an empty string if there are no problems
155 or the text of a warning if there are
160 # Abandon the check if it's a non-sourceful upload
161 if not self.changes["architecture"].has_key("source"):
164 for name, entry in sorted(self.files.items()):
165 if not entry.has_key("new") and entry["type"] == "deb":
166 if entry["section"] != "-":
167 if entry["section"].lower() != entry["override section"].lower():
168 summary += "%s: package says section is %s, override says %s.\n" % (name,
170 entry["override section"])
172 if entry["priority"] != "-":
173 if entry["priority"] != entry["override priority"]:
174 summary += "%s: package says priority is %s, override says %s.\n" % (name,
176 entry["override priority"])
181 def remove_known_changes(self, session=None):
182 session.delete(get_dbchange(self.changes_file, session))
184 def mark_missing_fields(self):
185 """add "missing" in fields which we will require for the known_changes table"""
186 for key in ['urgency', 'maintainer', 'fingerprint', 'changed-by' ]:
187 if (not self.changes.has_key(key)) or (not self.changes[key]):
188 self.changes[key]='missing'
191 def add_known_changes(self, dirpath, session=None):
192 """add "missing" in fields which we will require for the known_changes table"""
195 changesfile = os.path.join(dirpath, self.changes_file)
196 filetime = datetime.datetime.fromtimestamp(os.path.getctime(changesfile))
198 self.mark_missing_fields()
201 for key in ("distribution", "architecture", "binary"):
202 if isinstance(self.changes[key], dict):
203 multivalues[key] = " ".join(self.changes[key].keys())
205 multivalues[key] = self.changes[key].keys()
209 """INSERT INTO changes
210 (changesname, seen, source, binaries, architecture, version,
211 distribution, urgency, maintainer, fingerprint, changedby, date)
212 VALUES (:changesfile,:filetime,:source,:binary, :architecture,
213 :version,:distribution,:urgency,:maintainer,:fingerprint,:changedby,:date)""",
214 { 'changesfile': self.changes_file,
215 'filetime': filetime,
216 'source': self.changes["source"],
217 'binary': multivalues["binary"],
218 'architecture': multivalues["architecture"],
219 'version': self.changes["version"],
220 'distribution': multivalues["distribution"],
221 'urgency': self.changes["urgency"],
222 'maintainer': self.changes["maintainer"],
223 'fingerprint': self.changes["fingerprint"],
224 'changedby': self.changes["changed-by"],
225 'date': self.changes["date"]} )
229 return session.query(DBChange).filter_by(changesname = self.changes_file).one()
231 def unknown_files_fields(self, name):
232 return sorted(list( set(self.files[name].keys()) -
233 set(CHANGESFIELDS_FILES)))
235 def unknown_changes_fields(self):
236 return sorted(list( set(self.changes.keys()) -
237 set(CHANGESFIELDS_MANDATORY + CHANGESFIELDS_OPTIONAL)))
239 def unknown_dsc_fields(self):
240 return sorted(list( set(self.dsc.keys()) -
241 set(CHANGESFIELDS_DSC)))
243 def unknown_dsc_files_fields(self, name):
244 return sorted(list( set(self.dsc_files[name].keys()) -
245 set(CHANGESFIELDS_DSCFILES_MANDATORY + CHANGESFIELDS_DSCFILES_OPTIONAL)))
249 for name, entry in self.files.items():
250 r.append(" %s:" % (name))
251 for i in CHANGESFIELDS_FILES:
253 r.append(" %s: %s" % (i.capitalize(), entry[i]))
254 xfields = self.unknown_files_fields(name)
256 r.append("files[%s] still has following unrecognised keys: %s" % (name, ", ".join(xfields)))
260 def str_changes(self):
262 for i in CHANGESFIELDS_MANDATORY:
263 val = self.changes[i]
264 if isinstance(val, list):
266 elif isinstance(val, dict):
267 val = " ".join(val.keys())
268 r.append(' %s: %s' % (i.capitalize(), val))
270 for i in CHANGESFIELDS_OPTIONAL:
271 if self.changes.has_key(i):
272 r.append(' %s: %s' % (i.capitalize(), self.changes[i]))
274 xfields = self.unknown_changes_fields()
276 r.append("Warning: changes still has the following unrecognised fields: %s" % ", ".join(xfields))
282 for i in CHANGESFIELDS_DSC:
283 if self.dsc.has_key(i):
284 r.append(' %s: %s' % (i.capitalize(), self.dsc[i]))
286 xfields = self.unknown_dsc_fields()
288 r.append("Warning: dsc still has the following unrecognised fields: %s" % ", ".join(xfields))
292 def str_dsc_files(self):
294 for name, entry in self.dsc_files.items():
295 r.append(" %s:" % (name))
296 for i in CHANGESFIELDS_DSCFILES_MANDATORY:
297 r.append(" %s: %s" % (i.capitalize(), entry[i]))
298 for i in CHANGESFIELDS_DSCFILES_OPTIONAL:
300 r.append(" %s: %s" % (i.capitalize(), entry[i]))
301 xfields = self.unknown_dsc_files_fields(name)
303 r.append("dsc_files[%s] still has following unrecognised keys: %s" % (name, ", ".join(xfields)))
310 r.append(" Changes:")
311 r += self.str_changes()
321 r += self.str_files()
325 r.append(" Dsc Files:")
326 r += self.str_dsc_files()
330 __all__.append('Changes')