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