]>
Commit | Line | Data |
---|---|---|
9e8b1d7a | 1 | #!/usr/bin/python |
b792d887 MT |
2 | ############################################################################### |
3 | # # | |
4 | # Pakfire - The IPFire package management system # | |
5 | # Copyright (C) 2011 Pakfire development team # | |
6 | # # | |
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 3 of the License, or # | |
10 | # (at your option) any later version. # | |
11 | # # | |
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. # | |
16 | # # | |
17 | # You should have received a copy of the GNU General Public License # | |
18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. # | |
19 | # # | |
20 | ############################################################################### | |
9e8b1d7a | 21 | |
c2808056 | 22 | import hashlib |
9e8b1d7a MT |
23 | import os |
24 | import re | |
c2808056 | 25 | import shutil |
114ac7ee | 26 | import tarfile |
4496b160 | 27 | import tempfile |
9e8b1d7a | 28 | |
8b6bc023 MT |
29 | import logging |
30 | log = logging.getLogger("pakfire") | |
31 | ||
862bea4d | 32 | import pakfire.filelist |
97d7d682 | 33 | import pakfire.lzma as lzma |
4496b160 MT |
34 | import pakfire.util as util |
35 | import pakfire.compress as compress | |
4496b160 | 36 | from pakfire.constants import * |
75bb74a7 | 37 | from pakfire.i18n import _ |
9e8b1d7a | 38 | |
9db18669 MT |
39 | import base |
40 | import lexer | |
41 | import make | |
cd808413 | 42 | import tar |
97d7d682 | 43 | |
9db18669 | 44 | class FilePackage(base.Package): |
9e8b1d7a MT |
45 | """ |
46 | This class is a wrapper that reads package data from the (outer) | |
47 | tarball and should never be used solely. | |
48 | """ | |
9accbba5 MT |
49 | _type = None |
50 | ||
3723913b | 51 | def __init__(self, pakfire, repo, filename): |
9db18669 | 52 | base.Package.__init__(self, pakfire, repo) |
440cede8 | 53 | self.filename = os.path.abspath(filename) |
9e8b1d7a | 54 | |
36084c79 | 55 | # Place to cache the metadata |
9e8b1d7a MT |
56 | self._metadata = {} |
57 | ||
97d7d682 | 58 | # Place to cache the filelist and payload compression algorithm. |
862bea4d | 59 | self._filelist = None |
97d7d682 | 60 | self.__payload_compression = None |
862bea4d | 61 | |
c07a3ca7 MT |
62 | # Store the format of this package file. |
63 | self.format = self.get_format() | |
64 | ||
65 | # XXX need to make this much better. | |
9e8b1d7a MT |
66 | self.check() |
67 | ||
c07a3ca7 MT |
68 | # Read the info file. |
69 | if self.format >= 1: | |
70 | a = self.open_archive() | |
71 | f = a.extractfile("info") | |
72 | ||
9db18669 | 73 | self.lexer = lexer.FileLexer(f.readlines()) |
c07a3ca7 MT |
74 | |
75 | f.close() | |
76 | a.close() | |
77 | ||
78 | elif self.format == 0: | |
79 | pass | |
80 | ||
81 | else: | |
82 | raise PackageFormatUnsupportedError, _("Filename: %s") % self.filename | |
83 | ||
9e8b1d7a MT |
84 | def check(self): |
85 | """ | |
86 | Initially check if the given file is of the correct type and | |
87 | can be opened. | |
88 | """ | |
89 | if not tarfile.is_tarfile(self.filename): | |
90 | raise FileError, "Given file is not of correct format: %s" % self.filename | |
91 | ||
cabf1fbe | 92 | assert self.format in PACKAGE_FORMATS_SUPPORTED, self.format |
c07a3ca7 MT |
93 | |
94 | def get_format(self): | |
95 | a = self.open_archive() | |
96 | try: | |
97 | f = a.extractfile("pakfire-format") | |
98 | except KeyError: | |
99 | return 0 | |
100 | ||
101 | format = f.read() | |
102 | try: | |
103 | format = int(format) | |
104 | except TypeError: | |
105 | format = 0 | |
106 | ||
107 | f.close() | |
108 | a.close() | |
109 | ||
110 | return format | |
111 | ||
9e8b1d7a MT |
112 | def __repr__(self): |
113 | return "<%s %s>" % (self.__class__.__name__, self.filename) | |
114 | ||
edd6a268 MT |
115 | @property |
116 | def local(self): | |
117 | # A file package is always local. | |
118 | return True | |
119 | ||
68c0e769 MT |
120 | def open_archive(self, mode="r"): |
121 | return tarfile.open(self.filename, mode=mode, format=tarfile.PAX_FORMAT) | |
9e8b1d7a | 122 | |
5b2a7a44 MT |
123 | def open_payload_archive(self): |
124 | a = self.open_archive() | |
4496b160 | 125 | |
5b2a7a44 MT |
126 | # Find the payload data. |
127 | payload = a.extractfile("data.img") | |
4496b160 MT |
128 | |
129 | # Decompress the payload if needed. | |
97d7d682 | 130 | if self.payload_compression == "xz": |
cd808413 | 131 | payload_archive = tar.InnerTarFileXz.open(fileobj=payload) |
4496b160 | 132 | |
97d7d682 | 133 | elif self.payload_compression == "none": |
cd808413 | 134 | payload_archive = tar.InnerTarFile.open(fileobj=payload) |
4496b160 MT |
135 | |
136 | else: | |
c844646a MT |
137 | raise Exception, "Unhandled payload compression type: %s" % \ |
138 | self.payload_compression | |
5b2a7a44 MT |
139 | |
140 | return payload_archive | |
141 | ||
142 | def extract(self, message=None, prefix=None): | |
143 | log.debug("Extracting package %s" % self.friendly_name) | |
144 | ||
145 | if prefix is None: | |
146 | prefix = "" | |
147 | ||
148 | # Open package data for read. | |
149 | payload_archive = self.open_payload_archive() | |
4496b160 | 150 | |
4496b160 MT |
151 | # Load progressbar. |
152 | pb = None | |
153 | if message: | |
1e80d5d7 | 154 | message = "%-10s : %s" % (message, self.friendly_name) |
c2808056 | 155 | pb = util.make_progress(message, len(self.filelist), eta=False) |
4496b160 | 156 | |
414f4a0b MT |
157 | # Collect messages with errors and warnings, that are passed to |
158 | # the user. | |
159 | messages = [] | |
160 | ||
132bde17 MT |
161 | name2file = {} |
162 | for file in self.filelist: | |
efe141c7 | 163 | if file.is_dir() and file.name.endswith("/"): |
3ce6a8ad MT |
164 | name = file.name[:-1] |
165 | else: | |
166 | name = file.name | |
c2808056 | 167 | |
132bde17 | 168 | name2file[name] = file |
c2808056 | 169 | |
132bde17 | 170 | i = 0 |
97d7d682 MT |
171 | while True: |
172 | member = payload_archive.next() | |
173 | if not member: | |
174 | break | |
175 | ||
3ce6a8ad MT |
176 | # Check if file is also known in metadata. |
177 | name = member.name | |
178 | if not name.startswith("/"): | |
179 | name = "/%s" % name | |
180 | ||
181 | try: | |
182 | file = name2file[name] | |
183 | except KeyError: | |
184 | log.warning(_("File in archive is missing in file metadata: %s. Skipping.") % name) | |
c2808056 MT |
185 | continue |
186 | ||
132bde17 MT |
187 | # Update progress. |
188 | if pb: | |
189 | i += 1 | |
190 | pb.update(i) | |
191 | ||
4496b160 MT |
192 | target = os.path.join(prefix, member.name) |
193 | ||
c2808056 MT |
194 | # Check if a configuration file is already present. We don't want to |
195 | # overwrite that. | |
196 | if file.is_config(): | |
197 | config_save = "%s%s" % (target, CONFIG_FILE_SUFFIX_SAVE) | |
198 | config_new = "%s%s" % (target, CONFIG_FILE_SUFFIX_NEW) | |
199 | ||
200 | if os.path.exists(config_save) and not os.path.exists(target): | |
201 | # Extract new configuration file, save it as CONFIG_FILE_SUFFIX_NEW, | |
202 | # and reuse _SAVE. | |
203 | payload_archive.extract(member, path=prefix) | |
204 | ||
205 | shutil.move(target, config_new) | |
206 | shutil.move(config_save, target) | |
207 | continue | |
208 | ||
209 | elif os.path.exists(target): | |
210 | # If the files are identical, we skip the extraction of a | |
211 | # new configuration file. We also do that when the new configuration file | |
212 | # is a dummy file. | |
213 | if file.size == 0: | |
214 | continue | |
215 | ||
216 | # Calc hash of the current configuration file. | |
e3bcfd23 | 217 | config_hash1 = hashlib.new("sha512") |
c2808056 MT |
218 | f = open(target) |
219 | while True: | |
220 | buf = f.read(BUFFER_SIZE) | |
221 | if not buf: | |
222 | break | |
223 | config_hash1.update(buf) | |
224 | f.close() | |
225 | ||
226 | if file.hash1 == config_hash1.hexdigest(): | |
227 | continue | |
228 | ||
229 | # Backup old configuration file and extract new one. | |
230 | shutil.move(target, config_save) | |
231 | payload_archive.extract(member, path=prefix) | |
232 | ||
233 | # Save new configuration file as CONFIG_FILE_SUFFIX_NEW and | |
234 | # restore old configuration file. | |
235 | shutil.move(target, config_new) | |
236 | shutil.move(config_save, target) | |
237 | ||
238 | if prefix: | |
239 | config_new = os.path.relpath(config_new, prefix) | |
240 | messages.append(_("Config file created as %s") % config_new) | |
241 | continue | |
242 | ||
3c5a85f3 MT |
243 | # Don't overwrite target files if they already exist. |
244 | if file.is_datafile() and os.path.exists(target): | |
245 | log.debug(_("Don't overwrite already existing datafile '/%s'") % member.name) | |
246 | continue | |
247 | ||
4496b160 MT |
248 | # If the member is a directory and if it already exists, we |
249 | # don't need to create it again. | |
4496b160 MT |
250 | if os.path.exists(target): |
251 | if member.isdir(): | |
252 | continue | |
253 | ||
254 | else: | |
255 | # Remove file if it has been existant | |
414f4a0b MT |
256 | try: |
257 | os.unlink(target) | |
258 | except OSError: | |
259 | messages.append(_("Could not remove file: /%s") % member.name) | |
4496b160 MT |
260 | |
261 | #if self.pakfire.config.get("debug"): | |
262 | # msg = "Creating file (%s:%03d:%03d) " % \ | |
263 | # (tarfile.filemode(member.mode), member.uid, member.gid) | |
264 | # if member.issym(): | |
265 | # msg += "/%s -> %s" % (member.name, member.linkname) | |
266 | # elif member.islnk(): | |
267 | # msg += "/%s link to /%s" % (member.name, member.linkname) | |
268 | # else: | |
269 | # msg += "/%s" % member.name | |
8b6bc023 | 270 | # log.debug(msg) |
4496b160 MT |
271 | |
272 | payload_archive.extract(member, path=prefix) | |
273 | ||
274 | # Close all open files. | |
275 | payload_archive.close() | |
4496b160 | 276 | |
4496b160 MT |
277 | if pb: |
278 | pb.finish() | |
279 | ||
414f4a0b MT |
280 | # Print messages. |
281 | for msg in messages: | |
8b6bc023 | 282 | log.warning(msg) |
414f4a0b | 283 | |
5b2a7a44 MT |
284 | def open_file(self, filename): |
285 | payload_archive = self.open_payload_archive() | |
286 | ||
287 | # Search for filename. | |
288 | while True: | |
289 | member = payload_archive.next() | |
290 | if not member: | |
291 | break | |
292 | ||
293 | # Skip non-matching files. | |
294 | if not filename in (member.name, "/%s" % member.name): | |
295 | continue | |
296 | ||
297 | return payload_archive.extractfile(member) | |
298 | ||
9db18669 MT |
299 | def open_makefile(self): |
300 | """ | |
301 | Opens the makefile inside the package. | |
302 | """ | |
303 | f = self.open_file("%s.%s" % (self.name, MAKEFILE_EXTENSION)) | |
304 | if not f: | |
305 | return | |
306 | ||
307 | return make.Makefile(self.pakfire, lines=f.readlines()) | |
308 | ||
9e8b1d7a MT |
309 | @property |
310 | def metadata(self): | |
311 | """ | |
312 | Read-in the metadata from the "info" file and cache it in _metadata. | |
313 | """ | |
c07a3ca7 MT |
314 | assert self.format == 0, self |
315 | ||
9e8b1d7a | 316 | if not self._metadata: |
36084c79 MT |
317 | a = self.open_archive() |
318 | f = a.extractfile("info") | |
9e8b1d7a MT |
319 | |
320 | for line in f.readlines(): | |
321 | m = re.match(r"^(\w+)=(.*)$", line) | |
322 | if m is None: | |
323 | continue | |
324 | ||
325 | key, val = m.groups() | |
326 | self._metadata[key] = val.strip("\"") | |
327 | ||
328 | f.close() | |
36084c79 | 329 | a.close() |
9e8b1d7a MT |
330 | |
331 | return self._metadata | |
332 | ||
333 | @property | |
334 | def size(self): | |
335 | """ | |
336 | Return the size of the package file. | |
337 | """ | |
338 | return os.path.getsize(self.filename) | |
339 | ||
0304200a MT |
340 | @property |
341 | def inst_size(self): | |
342 | inst_size = 0 | |
343 | ||
344 | if self.format >= 1: | |
345 | inst_size = self.lexer.package.get_var("size") | |
346 | try: | |
347 | inst_size = int(inst_size) | |
348 | except TypeError: | |
349 | inst_size = 0 | |
350 | ||
351 | return inst_size | |
352 | ||
3c5a85f3 MT |
353 | def read_plain_filelist(self, filename): |
354 | a = self.open_archive() | |
355 | files = [] | |
356 | ||
357 | try: | |
358 | f = a.extractfile(filename) | |
359 | for line in f.readlines(): | |
360 | # Strip newline at end of line. | |
361 | file = line.rstrip() | |
362 | ||
363 | # Add a leading / is not present. | |
364 | if not file.startswith("/"): | |
365 | file = "/%s" % file | |
366 | ||
367 | files.append(file) | |
368 | f.close() | |
369 | ||
370 | # Ignore if 'filename' does not exist. | |
371 | except KeyError: | |
372 | pass | |
373 | ||
374 | finally: | |
375 | a.close() | |
376 | ||
377 | return files | |
378 | ||
862bea4d | 379 | def get_filelist(self): |
3e4a9b06 MT |
380 | """ |
381 | Return a list of the files that are contained in the package | |
382 | payload. | |
383 | """ | |
862bea4d MT |
384 | ret = [] |
385 | ||
36084c79 | 386 | a = self.open_archive() |
9e8b1d7a | 387 | |
c2808056 | 388 | # Cache configfiles. |
3c5a85f3 MT |
389 | if self.format >= 5: |
390 | filename = "configfiles" | |
391 | else: | |
392 | filename = "configs" | |
393 | configfiles = self.read_plain_filelist(filename) | |
3e9ef68a | 394 | |
3c5a85f3 MT |
395 | # Cache datafiles. |
396 | datafiles = self.read_plain_filelist("datafiles") | |
c2808056 MT |
397 | |
398 | f = a.extractfile("filelist") | |
18ae128e MT |
399 | for line in f.readlines(): |
400 | line = line.strip() | |
3e4a9b06 | 401 | |
862bea4d MT |
402 | file = pakfire.filelist.File(self.pakfire) |
403 | ||
3e4a9b06 | 404 | if self.format >= 1: |
efe141c7 | 405 | line = line.split(None, 8) |
3e9ef68a | 406 | |
cabf1fbe MT |
407 | # Check if fields do have the correct length. |
408 | if self.format >= 3 and len(line) <= 7: | |
409 | continue | |
410 | elif len(line) <= 6: | |
3e9ef68a MT |
411 | continue |
412 | ||
efe141c7 MT |
413 | # Switch the first and last argument in the line. |
414 | if self.format < 4: | |
415 | line.append(line.pop(0)) | |
416 | ||
417 | name = line[-1] | |
3e4a9b06 | 418 | |
c2808056 MT |
419 | if not name.startswith("/"): |
420 | name = "/%s" % name | |
421 | ||
422 | # Check if configfiles. | |
423 | if name in configfiles: | |
424 | file.config = True | |
425 | ||
3c5a85f3 MT |
426 | # Check if this is a datafile. |
427 | if name in datafiles: | |
428 | file.datafile = True | |
429 | ||
c2808056 MT |
430 | # Parse file type. |
431 | try: | |
efe141c7 | 432 | file.type = int(line[0]) |
c2808056 MT |
433 | except ValueError: |
434 | file.type = 0 | |
435 | ||
8fe16710 MT |
436 | # Parse the size information. |
437 | try: | |
efe141c7 | 438 | file.size = int(line[1]) |
8fe16710 | 439 | except ValueError: |
8fe16710 MT |
440 | file.size = 0 |
441 | ||
c2808056 | 442 | # Parse user and group. |
efe141c7 | 443 | file.user, file.group = line[2], line[3] |
c2808056 MT |
444 | |
445 | # Parse mode. | |
446 | try: | |
efe141c7 | 447 | file.mode = int(line[4]) |
c2808056 MT |
448 | except ValueError: |
449 | file.mode = 0 | |
450 | ||
451 | # Parse time. | |
452 | try: | |
efe141c7 | 453 | file.mtime = line[5] |
c2808056 MT |
454 | except ValueError: |
455 | file.mtime = 0 | |
456 | ||
457 | # Parse hash1 (sha512). | |
efe141c7 MT |
458 | if not line[6] == "-": |
459 | file.hash1 = line[6] | |
18ae128e | 460 | |
efe141c7 MT |
461 | if self.format >= 3 and len(line) >= 9 and not line[7] == "-": |
462 | file.capabilities = line[7] | |
cabf1fbe | 463 | |
862bea4d MT |
464 | else: |
465 | name = line | |
466 | ||
c2808056 MT |
467 | if not name.startswith("/"): |
468 | name = "/%s" % name | |
862bea4d MT |
469 | |
470 | file.name = name | |
471 | file.pkg = self | |
472 | ||
473 | ret.append(file) | |
9e8b1d7a MT |
474 | |
475 | f.close() | |
36084c79 | 476 | a.close() |
9e8b1d7a MT |
477 | |
478 | return ret | |
479 | ||
862bea4d MT |
480 | @property |
481 | def filelist(self): | |
482 | if self._filelist is None: | |
483 | self._filelist = self.get_filelist() | |
484 | ||
485 | return self._filelist | |
486 | ||
6ee3d6b9 MT |
487 | @property |
488 | def configfiles(self): | |
c2808056 | 489 | return [f for f in self.filelist if f.is_config()] |
6ee3d6b9 | 490 | |
3c5a85f3 MT |
491 | @property |
492 | def datafiles(self): | |
493 | return [f for f in self.filelist if f.is_datafile()] | |
494 | ||
9e8b1d7a MT |
495 | @property |
496 | def payload_compression(self): | |
497 | """ | |
c07a3ca7 | 498 | Return the (guessed) compression type of the payload. |
9e8b1d7a | 499 | """ |
97d7d682 MT |
500 | # We cache that because this is costly. |
501 | if self.__payload_compression is None: | |
502 | a = self.open_archive() | |
503 | f = a.extractfile("data.img") | |
c07a3ca7 | 504 | |
97d7d682 MT |
505 | # Go and guess what we do have here. |
506 | self.__payload_compression = compress.guess_algo(fileobj=f) | |
c07a3ca7 | 507 | |
97d7d682 MT |
508 | f.close() |
509 | a.close() | |
c07a3ca7 | 510 | |
97d7d682 | 511 | return self.__payload_compression or "none" |
9e8b1d7a | 512 | |
68c0e769 MT |
513 | ### SIGNATURE STUFF |
514 | ||
9e8b1d7a | 515 | @property |
68c0e769 | 516 | def signatures(self): |
9e8b1d7a | 517 | """ |
68c0e769 | 518 | Read the signatures from the archive. |
9e8b1d7a | 519 | """ |
68c0e769 MT |
520 | ret = {} |
521 | ||
522 | # Open the archive for reading. | |
523 | a = self.open_archive() | |
524 | ||
525 | for member in a.getmembers(): | |
526 | # Skip all files that are not a signature. | |
527 | if not member.name.startswith("signatures/"): | |
528 | continue | |
529 | ||
530 | # Get the ID of the key. | |
531 | key_id = os.path.basename(member.name) | |
532 | ||
533 | # Get the content of the signature file. | |
534 | f = a.extractfile(member.name) | |
4fffe3c4 | 535 | signature = f.read() |
68c0e769 MT |
536 | f.close() |
537 | ||
4fffe3c4 MT |
538 | if signature: |
539 | ret[key_id] = signature | |
540 | ||
68c0e769 MT |
541 | # Close the archive. |
542 | a.close() | |
543 | ||
544 | return ret | |
545 | ||
546 | def has_signature(self, key_id): | |
547 | """ | |
548 | Check if the file a signature of the given key. | |
549 | """ | |
a9260890 MT |
550 | f = self.open_file("signatures/%s" % key_id) |
551 | if f: | |
552 | f.close() | |
553 | ||
554 | return True | |
555 | ||
556 | return False | |
68c0e769 MT |
557 | |
558 | def __has_hardlinks(self): | |
559 | """ | |
560 | Returns True when a file has a hardlink. | |
561 | """ | |
562 | res = os.stat(self.filename) | |
563 | ||
564 | return res.st_nlink > 1 | |
565 | ||
566 | def __remove_hardlinks(self): | |
567 | """ | |
568 | Remove all hardlinks from this file that we can alter it in place. | |
569 | """ | |
570 | if not self.__has_hardlinks(): | |
571 | return | |
572 | ||
573 | # Open a file descriptor to the old file and remove the link from | |
574 | # the filesystem. | |
575 | f = open(self.filename, "rb") | |
576 | os.unlink(self.filename) | |
577 | ||
578 | # Create a new file with the exact same name for copying the data | |
579 | # to. | |
580 | g = open(self.filename, "wb") | |
581 | ||
582 | # Copy the data. | |
583 | while True: | |
584 | buf = f.read(BUFFER_SIZE) | |
585 | if not buf: | |
586 | break | |
587 | ||
588 | g.write(buf) | |
589 | ||
590 | # Close all files. | |
591 | f.close() | |
592 | g.close() | |
593 | ||
594 | # Make sure the whole process above worked fine. | |
595 | assert self.__has_hardlinks() is False | |
596 | ||
597 | def sign(self, key_id): | |
598 | """ | |
599 | Sign the package with the given key. | |
600 | """ | |
601 | # First check if the package has already been signed with this key. | |
602 | # If true, we do not have anything to do here. | |
603 | if self.has_signature(key_id): | |
6e7af0f5 | 604 | return False |
68c0e769 MT |
605 | |
606 | # Remove all hardlinks. | |
607 | self.__remove_hardlinks() | |
608 | ||
609 | # XXX verify the content of the file here. | |
610 | ||
611 | # Open the archive and read the checksum file. | |
612 | a = self.open_archive() | |
36084c79 | 613 | |
68c0e769 MT |
614 | f = a.extractfile("chksums") |
615 | cleartext = f.read() | |
36084c79 | 616 | |
68c0e769 MT |
617 | f.close() |
618 | a.close() | |
619 | ||
620 | # Create the signature. | |
621 | signature = self.pakfire.keyring.sign(key_id, cleartext) | |
622 | ||
68c0e769 | 623 | try: |
6e7af0f5 MT |
624 | # Write the signature to a temporary file. |
625 | f = tempfile.NamedTemporaryFile(mode="w", delete=False) | |
68c0e769 | 626 | f.write(signature) |
9e8b1d7a | 627 | f.close() |
68c0e769 MT |
628 | |
629 | # Reopen the outer tarfile in write mode and append | |
630 | # the new signature. | |
631 | a = self.open_archive("a") | |
6e7af0f5 | 632 | a.add(f.name, "signatures/%s" % key_id) |
36084c79 | 633 | a.close() |
9e8b1d7a | 634 | |
68c0e769 | 635 | finally: |
6e7af0f5 MT |
636 | os.unlink(f.name) |
637 | ||
638 | return True | |
68c0e769 MT |
639 | |
640 | def verify(self): | |
641 | """ | |
642 | Verify the tarball against the given key. | |
643 | ||
644 | If not key is given, only the checksums are compared to | |
645 | the actual data. | |
646 | """ | |
647 | ||
648 | # XXX replace Exception | |
649 | ||
650 | # Read the data of the checksum file. | |
651 | a = self.open_archive() | |
652 | f = a.extractfile("chksums") | |
653 | chksums = f.read() | |
654 | f.close() | |
655 | a.close() | |
656 | ||
657 | sigs = [] | |
658 | for signature in self.signatures.values(): | |
659 | sigs += self.pakfire.keyring.verify(signature, chksums) | |
660 | ||
661 | # Open the archive to access all files we will need. | |
662 | a = self.open_archive() | |
663 | ||
664 | # Read the chksums file. | |
665 | chksums = {} | |
666 | f = a.extractfile("chksums") | |
667 | for line in f.readlines(): | |
668 | filename, chksum = line.split() | |
669 | chksums[filename] = chksum | |
670 | f.close() | |
671 | a.close() | |
672 | ||
673 | for filename, chksum in chksums.items(): | |
674 | ret = self.check_chksum(filename, chksum) | |
675 | ||
676 | if ret: | |
677 | log.debug("Checksum of %s matches." % filename) | |
678 | continue | |
679 | else: | |
680 | log.debug("Checksum of %s does not match." % filename) | |
681 | ||
682 | raise Exception, "Checksum does not match: %s" % filename | |
683 | ||
684 | return sigs | |
685 | ||
686 | def check_chksum(self, filename, chksum, algo="sha512"): | |
687 | a = self.open_archive() | |
688 | f = a.extractfile(filename) | |
689 | ||
690 | h = hashlib.new(algo) | |
691 | while True: | |
692 | buf = f.read(BUFFER_SIZE) | |
693 | if not buf: | |
694 | break | |
695 | ||
696 | h.update(buf) | |
697 | ||
698 | f.close() | |
699 | a.close() | |
9e8b1d7a | 700 | |
68c0e769 | 701 | return h.hexdigest() == chksum |
9e8b1d7a | 702 | |
66af936c MT |
703 | @property |
704 | def hash1(self): | |
705 | """ | |
706 | Calculate the hash1 of this package. | |
707 | """ | |
708 | return util.calc_hash1(self.filename) | |
1b59091e MT |
709 | |
710 | @property | |
711 | def type(self): | |
712 | if self.format >= 2: | |
cc843cfe | 713 | type = self.lexer.package.get_var("type") |
1b59091e MT |
714 | elif self.format == 1: |
715 | type = self._type | |
716 | else: | |
717 | type = self.metadata.get("type") | |
718 | ||
719 | assert type, self | |
720 | return type | |
721 | ||
c07a3ca7 MT |
722 | @property |
723 | def name(self): | |
724 | if self.format >= 1: | |
725 | name = self.lexer.package.get_var("name") | |
726 | elif self.format == 0: | |
727 | name = self.metadata.get("PKG_NAME") | |
728 | ||
729 | assert name, self | |
730 | return name | |
66af936c | 731 | |
a5f5fced | 732 | @property |
c07a3ca7 MT |
733 | def epoch(self): |
734 | if self.format >= 1: | |
735 | epoch = self.lexer.package.get_var("epoch", 0) | |
736 | elif self.format == 0: | |
737 | epoch = self.metadata.get("PKG_EPOCH") | |
738 | ||
a5f5fced | 739 | try: |
c07a3ca7 MT |
740 | epoch = int(epoch) |
741 | except TypeError: | |
742 | epoch = 0 | |
36084c79 | 743 | |
c07a3ca7 | 744 | return epoch |
36084c79 | 745 | |
c07a3ca7 MT |
746 | @property |
747 | def version(self): | |
748 | if self.format >= 1: | |
749 | version = self.lexer.package.get_var("version") | |
750 | elif self.format == 0: | |
751 | version = self.metadata.get("PKG_VER") | |
a5f5fced | 752 | |
c07a3ca7 MT |
753 | assert version, self |
754 | return version | |
755 | ||
756 | @property | |
757 | def release(self): | |
758 | if self.format >= 1: | |
759 | release = self.lexer.package.get_var("release") | |
760 | elif self.format == 0: | |
761 | release = self.metadata.get("PKG_REL") | |
762 | ||
763 | assert release, self | |
764 | return release | |
765 | ||
766 | @property | |
767 | def arch(self): | |
768 | if self.format >= 1: | |
769 | arch = self.lexer.package.get_var("arch") | |
770 | elif self.format == 0: | |
771 | arch = self.metadata.get("PKG_ARCH") | |
772 | ||
773 | assert arch, self | |
774 | return arch | |
775 | ||
776 | @property | |
777 | def vendor(self): | |
778 | if self.format >= 1: | |
779 | vendor = self.lexer.distro.get_var("vendor") | |
780 | elif self.format == 0: | |
781 | vendor = self.metadata.get("PKG_VENDOR") | |
782 | ||
783 | return vendor | |
784 | ||
785 | @property | |
786 | def summary(self): | |
787 | if self.format >= 1: | |
788 | summary = self.lexer.package.get_var("summary") | |
789 | elif self.format == 0: | |
790 | summary = self.metadata.get("PKG_SUMMARY") | |
791 | ||
792 | assert summary, self | |
793 | return summary | |
794 | ||
795 | @property | |
796 | def description(self): | |
797 | if self.format >= 1: | |
798 | description = self.lexer.package.get_var("description") | |
799 | elif self.format == 0: | |
800 | description = self.metadata.get("PKG_DESC") | |
801 | ||
802 | return description | |
803 | ||
804 | @property | |
805 | def groups(self): | |
806 | if self.format >= 1: | |
807 | groups = self.lexer.package.get_var("groups") | |
808 | elif self.format == 0: | |
809 | groups = self.metadata.get("PKG_GROUPS") | |
810 | ||
811 | if groups: | |
812 | return groups.split() | |
813 | ||
814 | return [] | |
815 | ||
816 | @property | |
817 | def license(self): | |
818 | if self.format >= 1: | |
819 | license = self.lexer.package.get_var("license") | |
820 | elif self.format == 0: | |
821 | license = self.metadata.get("PKG_LICENSE") | |
822 | ||
823 | return license | |
824 | ||
825 | @property | |
826 | def url(self): | |
827 | if self.format >= 1: | |
828 | url = self.lexer.package.get_var("url") | |
829 | elif self.format == 0: | |
830 | url = self.metadata.get("PKG_URL") | |
831 | ||
832 | return url | |
833 | ||
834 | @property | |
835 | def maintainer(self): | |
836 | if self.format >= 1: | |
837 | maintainer = self.lexer.package.get_var("maintainer") | |
838 | elif self.format == 0: | |
839 | maintainer = self.metadata.get("PKG_MAINTAINER") | |
840 | ||
841 | return maintainer | |
842 | ||
843 | @property | |
844 | def uuid(self): | |
845 | if self.format >= 1: | |
846 | uuid = self.lexer.package.get_var("uuid") | |
847 | elif self.format == 0: | |
848 | uuid = self.metadata.get("PKG_UUID") | |
849 | ||
43acbf3c | 850 | assert uuid, self |
c07a3ca7 MT |
851 | return uuid |
852 | ||
853 | @property | |
854 | def build_id(self): | |
855 | if self.format >= 1: | |
856 | build_id = self.lexer.build.get_var("id") | |
857 | elif self.format == 0: | |
858 | build_id = self.metadata.get("BUILD_ID") | |
859 | ||
860 | assert build_id, self | |
861 | return build_id | |
862 | ||
863 | @property | |
864 | def build_host(self): | |
865 | if self.format >= 1: | |
866 | build_host = self.lexer.build.get_var("host") | |
867 | elif self.format == 0: | |
868 | build_host = self.metadata.get("BUILD_HOST") | |
869 | ||
870 | assert build_host, self | |
871 | return build_host | |
872 | ||
873 | @property | |
874 | def build_time(self): | |
875 | if self.format >= 1: | |
876 | build_time = self.lexer.build.get_var("time") | |
877 | elif self.format == 0: | |
878 | build_time = self.metadata.get("BUILD_TIME") | |
879 | ||
880 | # XXX re-enable this later | |
881 | #assert build_time, self | |
882 | ||
883 | try: | |
884 | build_time = int(build_time) | |
885 | except TypeError: | |
886 | build_time = 0 | |
887 | ||
888 | return build_time | |
889 | ||
890 | @property | |
891 | def provides(self): | |
892 | if self.format >= 1: | |
893 | provides = self.lexer.deps.get_var("provides") | |
894 | elif self.format == 0: | |
895 | provides = self.metadata.get("PKG_PROVIDES") | |
896 | ||
897 | if not provides: | |
898 | return [] | |
899 | ||
e491c081 MT |
900 | provides = provides.splitlines() |
901 | return self.filter_deps(provides) | |
c07a3ca7 MT |
902 | |
903 | @property | |
904 | def requires(self): | |
905 | if self.format >= 1: | |
906 | requires = self.lexer.deps.get_var("requires") | |
907 | elif self.format == 0: | |
908 | requires = self.metadata.get("PKG_REQUIRES") | |
909 | ||
910 | if not requires: | |
911 | return [] | |
912 | ||
e491c081 MT |
913 | requires = requires.splitlines() |
914 | return self.filter_deps(requires) | |
c07a3ca7 MT |
915 | |
916 | @property | |
917 | def prerequires(self): | |
918 | if self.format >= 1: | |
919 | prerequires = self.lexer.deps.get_var("prerequires") | |
920 | elif self.format == 0: | |
921 | prerequires = self.metadata.get("PKG_PREREQUIRES") | |
922 | ||
923 | if not prerequires: | |
924 | return [] | |
925 | ||
e491c081 MT |
926 | prerequires = prerequires.splitlines() |
927 | return self.filter_deps(prerequires) | |
c07a3ca7 MT |
928 | |
929 | @property | |
930 | def obsoletes(self): | |
931 | if self.format >= 1: | |
932 | obsoletes = self.lexer.deps.get_var("obsoletes") | |
933 | elif self.format == 0: | |
934 | obsoletes = self.metadata.get("PKG_OBSOLETES") | |
935 | ||
936 | if not obsoletes: | |
937 | return [] | |
938 | ||
e491c081 MT |
939 | obsoletes = obsoletes.splitlines() |
940 | return self.filter_deps(obsoletes) | |
c07a3ca7 MT |
941 | |
942 | @property | |
943 | def conflicts(self): | |
944 | if self.format >= 1: | |
945 | conflicts = self.lexer.deps.get_var("conflicts") | |
946 | elif self.format == 0: | |
947 | conflicts = self.metadata.get("PKG_CONFLICTS") | |
948 | ||
949 | if not conflicts: | |
950 | return [] | |
a5f5fced | 951 | |
e491c081 MT |
952 | conflicts = conflicts.splitlines() |
953 | return self.filter_deps(conflicts) | |
e0636b31 | 954 | |
a60f0f7d MT |
955 | @property |
956 | def recommends(self): | |
a8f5d29e MT |
957 | if self.format < 4: |
958 | return [] | |
959 | ||
a60f0f7d MT |
960 | recommends = self.lexer.deps.get_var("recommends") |
961 | ||
962 | if not recommends: | |
963 | return [] | |
964 | ||
965 | recommends = recommends.splitlines() | |
966 | return self.filter_deps(recommends) | |
967 | ||
968 | @property | |
969 | def suggests(self): | |
a8f5d29e MT |
970 | if self.format < 4: |
971 | return [] | |
972 | ||
a60f0f7d MT |
973 | suggests = self.lexer.deps.get_var("suggests") |
974 | ||
975 | if not suggests: | |
976 | return [] | |
977 | ||
978 | suggests = suggests.splitlines() | |
979 | return self.filter_deps(suggests) | |
980 | ||
e0636b31 MT |
981 | |
982 | class SourcePackage(FilePackage): | |
1b59091e MT |
983 | _type = "source" |
984 | ||
985 | @property | |
986 | def arch(self): | |
987 | return "src" | |
988 | ||
989 | @property | |
990 | def supported_arches(self): | |
991 | if self.format >= 2: | |
992 | arches = self.lexer.package.get_var("arch", "all") | |
993 | elif self.format == 1: | |
994 | # Format 1 did not support "supported_arches", so we assume "all". | |
995 | arches = "all" | |
996 | else: | |
997 | arches = self.metadata.get("PKG_SUPPORTED_ARCHES", "all") | |
998 | ||
999 | assert arches, self | |
1000 | return arches | |
e0636b31 | 1001 | |
9db18669 MT |
1002 | @property |
1003 | def requires(self): | |
1004 | m = self.open_makefile() | |
1005 | if not m: | |
1006 | return [] | |
1007 | ||
1008 | requires = m.lexer.build.get_var("requires") | |
1009 | requires = requires.splitlines() | |
1010 | ||
1011 | return self.filter_deps(requires) | |
1012 | ||
e0636b31 MT |
1013 | |
1014 | class BinaryPackage(FilePackage): | |
1b59091e MT |
1015 | _type = "binary" |
1016 | ||
e0636b31 MT |
1017 | def get_scriptlet(self, type): |
1018 | a = self.open_archive() | |
1019 | ||
1020 | # Path of the scriptlet in the tarball. | |
1021 | path = "scriptlets/%s" % type | |
1022 | ||
1023 | try: | |
1024 | f = a.extractfile(path) | |
1025 | except KeyError: | |
1026 | # If the scriptlet is not available, we just return. | |
1027 | return | |
1028 | ||
1029 | scriptlet = f.read() | |
1030 | ||
1031 | f.close() | |
1032 | a.close() | |
1033 | ||
1034 | return scriptlet |