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