configpaths.py 8.08 KB
Newer Older
Philipp Hörist's avatar
Philipp Hörist committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Copyright (C) 2006 Jean-Marie Traissard <jim AT lapin.org>
#                    Junglecow J <junglecow AT gmail.com>
# Copyright (C) 2006-2014 Yann Leboulanger <asterix AT lagaule.org>
# Copyright (C) 2007 Brendan Taylor <whateley AT gmail.com>
# Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
# Copyright (C) 2018 Philipp Hörist <philipp AT hoerist.com>
#
# This file is part of Gajim.
#
# Gajim is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
# by the Free Software Foundation; version 3 only.
#
# Gajim is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
roidelapluie's avatar
roidelapluie committed
21

22
23
24
25
26
27
from typing import Dict  # pylint: disable=unused-import
from typing import List
from typing import Generator
from typing import Optional  # pylint: disable=unused-import
from typing import Tuple

28
import os
29
import sys
30
import tempfile
31
from pathlib import Path
32

33
34
from gi.repository import GLib

André's avatar
André committed
35
import gajim
36
from gajim.common.i18n import _
37
from gajim.common.const import PathType, PathLocation
38
from gajim.common.types import PathTuple
39

40

André's avatar
André committed
41
def get(key: str) -> Path:
Philipp Hörist's avatar
Philipp Hörist committed
42
43
44
    return _paths[key]


45
def get_plugin_dirs() -> List[Path]:
46
    if gajim.IS_FLATPAK:
47
48
49
50
        return [Path(_paths['PLUGINS_BASE']),
                Path('/app/plugins')]
    return [Path(_paths['PLUGINS_BASE']),
            Path(_paths['PLUGINS_USER'])]
51
52


André's avatar
André committed
53
def get_paths(type_: PathType) -> Generator[Path, None, None]:
54
    for key, value in _paths.items():
55
        path_type = value[2]
56
57
58
59
60
        if type_ != path_type:
            continue
        yield _paths[key]


61
def override_path(*args, **kwargs):
62
    _paths.add(*args, **kwargs)
63
64


65
def set_separation(active: bool) -> None:
Philipp Hörist's avatar
Philipp Hörist committed
66
67
68
    _paths.profile_separation = active


69
def set_profile(profile: str) -> None:
Philipp Hörist's avatar
Philipp Hörist committed
70
71
72
    _paths.profile = profile


73
def set_config_root(config_root: str) -> None:
André's avatar
André committed
74
    _paths.custom_config_root = Path(config_root).resolve()
Philipp Hörist's avatar
Philipp Hörist committed
75
76


77
def init() -> None:
Philipp Hörist's avatar
Philipp Hörist committed
78
    _paths.init()
79
80


81
def create_paths() -> None:
82
    for path in get_paths(PathType.FOLDER):
André's avatar
André committed
83
84
        if path.is_file():
            print(_('%s is a file but it should be a directory') % path)
85
86
87
            print(_('Gajim will now exit'))
            sys.exit()

André's avatar
André committed
88
89
        if not path.exists():
            for parent_path in reversed(path.parents):
90
                # Create all parent folders
Alexander Krotov's avatar
Alexander Krotov committed
91
                # don't use mkdir(parent=True), as it ignores `mode`
92
93
94
95
                # when creating the parents
                if not parent_path.exists():
                    print(('creating %s directory') % parent_path)
                    parent_path.mkdir(mode=0o700)
André's avatar
André committed
96
97
            print(('creating %s directory') % path)
            path.mkdir(mode=0o700)
98
99


100
class ConfigPaths:
101
102
    def __init__(self) -> None:
        self._paths = {}  # type: Dict[str, PathTuple]
Philipp Hörist's avatar
Philipp Hörist committed
103
104
        self.profile = ''
        self.profile_separation = False
André's avatar
André committed
105
        self.custom_config_root = None  # type: Optional[Path]
106
107

        if os.name == 'nt':
108
109
110
111
112
            if gajim.IS_PORTABLE:
                application_path = Path(sys.executable).parent
                self.config_root = self.cache_root = self.data_root = \
                        application_path.parent / 'UserData'
            else:
113
114
                # Documents and Settings\[User Name]\Application Data\Gajim
                self.config_root = self.cache_root = self.data_root = \
André's avatar
André committed
115
                        Path(os.environ['appdata']) / 'Gajim'
Philipp Hörist's avatar
Philipp Hörist committed
116
        else:
André's avatar
André committed
117
118
119
            self.config_root = Path(GLib.get_user_config_dir()) / 'gajim'
            self.cache_root = Path(GLib.get_user_cache_dir()) / 'gajim'
            self.data_root = Path(GLib.get_user_data_dir()) / 'gajim'
120

121
122
123
124
125
126
        if sys.version_info < (3, 9):
            import pkg_resources
            basedir = Path(pkg_resources.resource_filename("gajim", "."))
        else:
            import importlib.resources
            basedir = importlib.resources.files('gajim')
127

128
        source_paths = [
André's avatar
André committed
129
130
131
132
133
134
135
            ('DATA', basedir / 'data'),
            ('STYLE', basedir / 'data' / 'style'),
            ('EMOTICONS', basedir / 'data' / 'emoticons'),
            ('GUI', basedir / 'data' / 'gui'),
            ('ICONS', basedir / 'data' / 'icons'),
            ('HOME', Path.home()),
            ('PLUGINS_BASE', basedir / 'data' / 'plugins'),
136
137
138
        ]

        for path in source_paths:
139
            self.add(*path)
140

André's avatar
André committed
141
    def __getitem__(self, key: str) -> Path:
142
143
        location, path, _ = self._paths[key]
        if location == PathLocation.CONFIG:
André's avatar
André committed
144
            return self.config_root / path
145
        if location == PathLocation.CACHE:
André's avatar
André committed
146
            return self.cache_root / path
147
        if location == PathLocation.DATA:
André's avatar
André committed
148
            return self.data_root / path
149
150
        return path

151
    def items(self) -> Generator[Tuple[str, PathTuple], None, None]:
152
153
154
        for key, value in self._paths.items():
            yield (key, value)

André's avatar
André committed
155
    def _prepare(self, path: Path, unique: bool) -> Path:
156
        if os.name == 'nt':
André's avatar
André committed
157
            path = Path(str(path).capitalize())
158
159
        if self.profile:
            if unique or self.profile_separation:
André's avatar
André committed
160
                return Path(f'{path}.{self.profile}')
161
162
        return path

163
164
    def add(self,
            name: str,
André's avatar
André committed
165
            path: Path,
166
167
168
            location: PathLocation = None,
            path_type: PathType = None,
            unique: bool = False) -> None:
André's avatar
André committed
169
        if location is not None:
170
171
            path = self._prepare(path, unique)
        self._paths[name] = (location, path, path_type)
172

Philipp Hörist's avatar
Philipp Hörist committed
173
    def init(self):
174
        if self.custom_config_root:
175
176
            self.config_root = self.custom_config_root
            self.cache_root = self.data_root = self.custom_config_root
177

178
        user_dir_paths = [
André's avatar
André committed
179
180
181
182
            ('TMP', Path(tempfile.gettempdir())),
            ('MY_CONFIG', Path(), PathLocation.CONFIG, PathType.FOLDER),
            ('MY_CACHE', Path(), PathLocation.CACHE, PathType.FOLDER),
            ('MY_DATA', Path(), PathLocation.DATA, PathType.FOLDER),
183
184
185
        ]

        for path in user_dir_paths:
186
            self.add(*path)
187
188
189
190
191
192

        # These paths are unique per profile
        unique_profile_paths = [
            # Data paths
            ('SECRETS_FILE', 'secrets', PathLocation.DATA, PathType.FILE),
            ('MY_PEER_CERTS', 'certs', PathLocation.DATA, PathType.FOLDER),
Philipp Hörist's avatar
Philipp Hörist committed
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
Philipp Hörist's avatar
Philipp Hörist committed
198
            ('SETTINGS', 'settings.sqlite', PathLocation.CONFIG, PathType.FILE),
199
            ('CONFIG_FILE', 'config', PathLocation.CONFIG, PathType.FILE),
200
201
            ('PLUGINS_CONFIG_DIR',
             'pluginsconfig', PathLocation.CONFIG, PathType.FOLDER),
202
203
204
205
            ('MY_CERT', 'localcerts', PathLocation.CONFIG, PathType.FOLDER),
        ]

        for path in unique_profile_paths:
206
            self.add(*path, unique=True)
207
208
209
210
211
212

        # These paths are only unique per profile if the commandline arg
        # `separate` is passed
        paths = [
            # Data paths
            ('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
216
217
218
            ('MY_EMOTS',
             'emoticons', PathLocation.DATA, PathType.FOLDER_OPTIONAL),
            ('MY_ICONSETS',
             'iconsets', PathLocation.DATA, PathType.FOLDER_OPTIONAL),
219
220
221
222

            # Cache paths
            ('CACHE_DB', 'cache.db', PathLocation.CACHE, PathType.FILE),
            ('AVATAR', 'avatars', PathLocation.CACHE, PathType.FOLDER),
223
            ('BOB', 'bob', PathLocation.CACHE, PathType.FOLDER),
Philipp Hörist's avatar
Philipp Hörist committed
224
225
226
227

            # Config paths
            ('MY_THEME', 'theme', PathLocation.CONFIG, PathType.FOLDER),

228
229
230
        ]

        for path in paths:
231
            self.add(*path)
232

Philipp Hörist's avatar
Philipp Hörist committed
233
234

_paths = ConfigPaths()