1""" 2pgxnclient -- tar file utilities 3""" 4 5# Copyright (C) 2011-2021 Daniele Varrazzo 6 7# This file is part of the PGXN client 8 9import os 10import tarfile 11 12from pgxnclient.i18n import _ 13from pgxnclient.errors import PgxnClientException 14from pgxnclient.archive import Archive 15 16import logging 17 18logger = logging.getLogger('pgxnclient.tar') 19 20 21class TarArchive(Archive): 22 """Handle .tar archives""" 23 24 _file = None 25 26 def can_open(self): 27 return tarfile.is_tarfile(self.filename) 28 29 def open(self): 30 assert not self._file, "archive already open" 31 try: 32 self._file = tarfile.open(self.filename, 'r') 33 except Exception as e: 34 raise PgxnClientException( 35 _("cannot open archive '%s': %s") % (self.filename, e) 36 ) 37 38 def close(self): 39 if self._file is not None: 40 self._file.close() 41 self._file = None 42 43 def list_files(self): 44 assert self._file, "archive not open" 45 return self._file.getnames() 46 47 def read(self, fn): 48 assert self._file, "archive not open" 49 return self._file.extractfile(fn).read() 50 51 def unpack(self, destdir): 52 tarname = self.filename 53 logger.info(_("unpacking: %s"), tarname) 54 destdir = os.path.abspath(destdir) 55 self.open() 56 try: 57 for fn in self.list_files(): 58 fname = os.path.abspath(os.path.join(destdir, fn)) 59 if not fname.startswith(destdir): 60 raise PgxnClientException( 61 _("archive file '%s' trying to escape!") % fname 62 ) 63 64 self._file.extractall(path=destdir) 65 finally: 66 self.close() 67 68 return self._find_work_directory(destdir) 69 70 71def unpack(filename, destdir): 72 return TarArchive(filename).unpack(destdir) 73