1# Copyright (C) 2006 Jean-Marie Traissard <jim AT lapin.org>
2#                    Junglecow J <junglecow AT gmail.com>
3# Copyright (C) 2006-2014 Yann Leboulanger <asterix AT lagaule.org>
4# Copyright (C) 2007 Brendan Taylor <whateley AT gmail.com>
5# Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
6# Copyright (C) 2018 Philipp Hörist <philipp AT hoerist.com>
7#
8# This file is part of Gajim.
9#
10# Gajim is free software; you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published
12# by the Free Software Foundation; version 3 only.
13#
14# Gajim is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
21
22from typing import Dict  # pylint: disable=unused-import
23from typing import List
24from typing import Generator
25from typing import Optional  # pylint: disable=unused-import
26from typing import Tuple
27
28import os
29import sys
30import tempfile
31from pathlib import Path
32
33from gi.repository import GLib
34
35import gajim
36from gajim.common.i18n import _
37from gajim.common.const import PathType, PathLocation
38from gajim.common.types import PathTuple
39
40
41def get(key: str) -> Path:
42    return _paths[key]
43
44
45def get_plugin_dirs() -> List[Path]:
46    if gajim.IS_FLATPAK:
47        return [Path(_paths['PLUGINS_BASE']),
48                Path('/app/plugins')]
49    return [Path(_paths['PLUGINS_BASE']),
50            Path(_paths['PLUGINS_USER'])]
51
52
53def get_paths(type_: PathType) -> Generator[Path, None, None]:
54    for key, value in _paths.items():
55        path_type = value[2]
56        if type_ != path_type:
57            continue
58        yield _paths[key]
59
60
61def override_path(*args, **kwargs):
62    _paths.add(*args, **kwargs)
63
64
65def set_separation(active: bool) -> None:
66    _paths.profile_separation = active
67
68
69def set_profile(profile: str) -> None:
70    _paths.profile = profile
71
72
73def set_config_root(config_root: str) -> None:
74    _paths.custom_config_root = Path(config_root).resolve()
75
76
77def init() -> None:
78    _paths.init()
79
80
81def create_paths() -> None:
82    for path in get_paths(PathType.FOLDER):
83        if path.is_file():
84            print(_('%s is a file but it should be a directory') % path)
85            print(_('Gajim will now exit'))
86            sys.exit()
87
88        if not path.exists():
89            for parent_path in reversed(path.parents):
90                # Create all parent folders
91                # don't use mkdir(parent=True), as it ignores `mode`
92                # when creating the parents
93                if not parent_path.exists():
94                    print(('creating %s directory') % parent_path)
95                    parent_path.mkdir(mode=0o700)
96            print(('creating %s directory') % path)
97            path.mkdir(mode=0o700)
98
99
100class ConfigPaths:
101    def __init__(self) -> None:
102        self._paths = {}  # type: Dict[str, PathTuple]
103        self.profile = ''
104        self.profile_separation = False
105        self.custom_config_root = None  # type: Optional[Path]
106
107        if os.name == 'nt':
108            if gajim.IS_PORTABLE:
109                application_path = Path(sys.executable).parent
110                self.config_root = self.cache_root = self.data_root = \
111                        application_path.parent / 'UserData'
112            else:
113                # Documents and Settings\[User Name]\Application Data\Gajim
114                self.config_root = self.cache_root = self.data_root = \
115                        Path(os.environ['appdata']) / 'Gajim'
116        else:
117            self.config_root = Path(GLib.get_user_config_dir()) / 'gajim'
118            self.cache_root = Path(GLib.get_user_cache_dir()) / 'gajim'
119            self.data_root = Path(GLib.get_user_data_dir()) / 'gajim'
120
121        if sys.version_info < (3, 9):
122            import pkg_resources
123            basedir = Path(pkg_resources.resource_filename("gajim", "."))
124        else:
125            import importlib.resources
126            basedir = importlib.resources.files('gajim')
127
128        source_paths = [
129            ('DATA', basedir / 'data'),
130            ('STYLE', basedir / 'data' / 'style'),
131            ('EMOTICONS', basedir / 'data' / 'emoticons'),
132            ('GUI', basedir / 'data' / 'gui'),
133            ('ICONS', basedir / 'data' / 'icons'),
134            ('HOME', Path.home()),
135            ('PLUGINS_BASE', basedir / 'data' / 'plugins'),
136        ]
137
138        for path in source_paths:
139            self.add(*path)
140
141    def __getitem__(self, key: str) -> Path:
142        location, path, _ = self._paths[key]
143        if location == PathLocation.CONFIG:
144            return self.config_root / path
145        if location == PathLocation.CACHE:
146            return self.cache_root / path
147        if location == PathLocation.DATA:
148            return self.data_root / path
149        return path
150
151    def items(self) -> Generator[Tuple[str, PathTuple], None, None]:
152        for key, value in self._paths.items():
153            yield (key, value)
154
155    def _prepare(self, path: Path, unique: bool) -> Path:
156        if os.name == 'nt':
157            path = Path(str(path).capitalize())
158        if self.profile:
159            if unique or self.profile_separation:
160                return Path(f'{path}.{self.profile}')
161        return path
162
163    def add(self,
164            name: str,
165            path: Path,
166            location: PathLocation = None,
167            path_type: PathType = None,
168            unique: bool = False) -> None:
169        if location is not None:
170            path = self._prepare(path, unique)
171        self._paths[name] = (location, path, path_type)
172
173    def init(self):
174        if self.custom_config_root:
175            self.config_root = self.custom_config_root
176            self.cache_root = self.data_root = self.custom_config_root
177
178        user_dir_paths = [
179            ('TMP', Path(tempfile.gettempdir())),
180            ('MY_CONFIG', Path(), PathLocation.CONFIG, PathType.FOLDER),
181            ('MY_CACHE', Path(), PathLocation.CACHE, PathType.FOLDER),
182            ('MY_DATA', Path(), PathLocation.DATA, PathType.FOLDER),
183        ]
184
185        for path in user_dir_paths:
186            self.add(*path)
187
188        # These paths are unique per profile
189        unique_profile_paths = [
190            # Data paths
191            ('SECRETS_FILE', 'secrets', PathLocation.DATA, PathType.FILE),
192            ('MY_PEER_CERTS', 'certs', PathLocation.DATA, PathType.FOLDER),
193            ('CERT_STORE', 'cert_store', PathLocation.DATA, PathType.FOLDER),
194            ('DEBUG', 'debug', PathLocation.DATA, PathType.FOLDER),
195            ('PLUGINS_DATA', 'plugins_data', PathLocation.DATA, PathType.FOLDER),
196
197            # Config paths
198            ('SETTINGS', 'settings.sqlite', PathLocation.CONFIG, PathType.FILE),
199            ('CONFIG_FILE', 'config', PathLocation.CONFIG, PathType.FILE),
200            ('PLUGINS_CONFIG_DIR',
201             'pluginsconfig', PathLocation.CONFIG, PathType.FOLDER),
202            ('MY_CERT', 'localcerts', PathLocation.CONFIG, PathType.FOLDER),
203        ]
204
205        for path in unique_profile_paths:
206            self.add(*path, unique=True)
207
208        # These paths are only unique per profile if the commandline arg
209        # `separate` is passed
210        paths = [
211            # Data paths
212            ('LOG_DB', 'logs.db', PathLocation.DATA, PathType.FILE),
213            ('PLUGINS_DOWNLOAD', 'plugins_download', PathLocation.CACHE, PathType.FOLDER),
214            ('PLUGINS_USER', 'plugins', PathLocation.DATA, PathType.FOLDER),
215            ('MY_EMOTS',
216             'emoticons', PathLocation.DATA, PathType.FOLDER_OPTIONAL),
217            ('MY_ICONSETS',
218             'iconsets', PathLocation.DATA, PathType.FOLDER_OPTIONAL),
219
220            # Cache paths
221            ('CACHE_DB', 'cache.db', PathLocation.CACHE, PathType.FILE),
222            ('AVATAR', 'avatars', PathLocation.CACHE, PathType.FOLDER),
223            ('BOB', 'bob', PathLocation.CACHE, PathType.FOLDER),
224
225            # Config paths
226            ('MY_THEME', 'theme', PathLocation.CONFIG, PathType.FOLDER),
227
228        ]
229
230        for path in paths:
231            self.add(*path)
232
233
234_paths = ConfigPaths()
235