/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
7336.2.1 by Martin
Split non-ini config methods to bedding
1
# Copyright (C) 2005-2014, 2016 Canonical Ltd
2
# Copyright (C) 2019 Breezy developers
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
18
"""Functions for deriving user configuration from system environment."""
19
20
import os
21
import sys
22
23
from .lazy_import import lazy_import
24
lazy_import(globals(), """
25
from breezy import (
26
    osutils,
27
    trace,
28
    win32utils,
29
    )
30
""")
7413.8.11 by Jelmer Vernooij
Don't lazy-import errors.
31
from . import (
32
    errors,
33
    )
7336.2.1 by Martin
Split non-ini config methods to bedding
34
35
36
def ensure_config_dir_exists(path=None):
37
    """Make sure a configuration directory exists.
38
39
    This makes sure that the directory exists.
40
    On windows, since configuration directories are 2 levels deep,
41
    it makes sure both the directory and the parent directory exists.
42
    """
43
    if path is None:
44
        path = config_dir()
45
    if not os.path.isdir(path):
46
        if sys.platform == 'win32':
47
            parent_dir = os.path.dirname(path)
48
            if not os.path.isdir(parent_dir):
49
                trace.mutter(
50
                    'creating config parent directory: %r', parent_dir)
51
                os.mkdir(parent_dir)
52
        trace.mutter('creating config directory: %r', path)
53
        os.mkdir(path)
54
        osutils.copy_ownership_from_path(path)
55
56
57
def bazaar_config_dir():
58
    """Return per-user configuration directory as unicode string
59
60
    By default this is %APPDATA%/bazaar/2.0 on Windows, ~/.bazaar on Mac OS X
61
    and Linux.  On Mac OS X and Linux, if there is a $XDG_CONFIG_HOME/bazaar
62
    directory, that will be used instead
63
64
    TODO: Global option --config-dir to override this.
65
    """
66
    base = osutils.path_from_environ('BZR_HOME')
67
    if sys.platform == 'win32':
68
        if base is None:
69
            base = win32utils.get_appdata_location()
70
        if base is None:
71
            base = win32utils.get_home_location()
72
        return osutils.pathjoin(base, 'bazaar', '2.0')
73
    if base is None:
74
        xdg_dir = osutils.path_from_environ('XDG_CONFIG_HOME')
75
        if xdg_dir is None:
76
            xdg_dir = osutils.pathjoin(osutils._get_home_dir(), ".config")
77
        xdg_dir = osutils.pathjoin(xdg_dir, 'bazaar')
78
        if osutils.isdir(xdg_dir):
79
            trace.mutter(
80
                "Using configuration in XDG directory %s." % xdg_dir)
81
            return xdg_dir
82
        base = osutils._get_home_dir()
83
    return osutils.pathjoin(base, ".bazaar")
84
85
86
def _config_dir():
87
    """Return per-user configuration directory as unicode string
88
89
    By default this is %APPDATA%/breezy on Windows, $XDG_CONFIG_HOME/breezy on
90
    Mac OS X and Linux. If the breezy config directory doesn't exist but
91
    the bazaar one (see bazaar_config_dir()) does, use that instead.
92
    """
93
    # TODO: Global option --config-dir to override this.
94
    base = osutils.path_from_environ('BRZ_HOME')
95
    if sys.platform == 'win32':
96
        if base is None:
97
            base = win32utils.get_appdata_location()
98
        if base is None:
99
            base = win32utils.get_home_location()
100
    if base is None:
101
        base = osutils.path_from_environ('XDG_CONFIG_HOME')
102
        if base is None:
103
            base = osutils.pathjoin(osutils._get_home_dir(), ".config")
104
    breezy_dir = osutils.pathjoin(base, 'breezy')
105
    if osutils.isdir(breezy_dir):
106
        return (breezy_dir, 'breezy')
107
    # If the breezy directory doesn't exist, but the bazaar one does, use that:
108
    bazaar_dir = bazaar_config_dir()
109
    if osutils.isdir(bazaar_dir):
110
        trace.mutter(
111
            "Using Bazaar configuration directory (%s)", bazaar_dir)
112
        return (bazaar_dir, 'bazaar')
113
    return (breezy_dir, 'breezy')
114
115
116
def config_dir():
117
    """Return per-user configuration directory as unicode string
118
119
    By default this is %APPDATA%/breezy on Windows, $XDG_CONFIG_HOME/breezy on
120
    Mac OS X and Linux. If the breezy config directory doesn't exist but
121
    the bazaar one (see bazaar_config_dir()) does, use that instead.
122
    """
123
    return _config_dir()[0]
124
125
126
def config_path():
127
    """Return per-user configuration ini file filename."""
128
    path, kind = _config_dir()
129
    if kind == 'bazaar':
130
        return osutils.pathjoin(path, 'bazaar.conf')
131
    else:
132
        return osutils.pathjoin(path, 'breezy.conf')
133
134
135
def locations_config_path():
136
    """Return per-user configuration ini file filename."""
137
    return osutils.pathjoin(config_dir(), 'locations.conf')
138
139
140
def authentication_config_path():
141
    """Return per-user authentication ini file filename."""
142
    return osutils.pathjoin(config_dir(), 'authentication.conf')
143
144
145
def user_ignore_config_path():
146
    """Return per-user authentication ini file filename."""
147
    return osutils.pathjoin(config_dir(), 'ignore')
148
149
150
def crash_dir():
151
    """Return the directory name to store crash files.
152
153
    This doesn't implicitly create it.
154
155
    On Windows it's in the config directory; elsewhere it's /var/crash
156
    which may be monitored by apport.  It can be overridden by
157
    $APPORT_CRASH_DIR.
158
    """
159
    if sys.platform == 'win32':
160
        return osutils.pathjoin(config_dir(), 'Crash')
161
    else:
162
        # XXX: hardcoded in apport_python_hook.py; therefore here too -- mbp
163
        # 2010-01-31
164
        return os.environ.get('APPORT_CRASH_DIR', '/var/crash')
165
166
167
def cache_dir():
168
    """Return the cache directory to use."""
7344.2.1 by Martin
Relocate the bzr log file out of $HOME
169
    base = osutils.path_from_environ('BRZ_HOME')
7336.2.1 by Martin
Split non-ini config methods to bedding
170
    if sys.platform in "win32":
7344.2.1 by Martin
Relocate the bzr log file out of $HOME
171
        if base is None:
172
            base = win32utils.get_local_appdata_location()
7336.2.1 by Martin
Split non-ini config methods to bedding
173
        if base is None:
174
            base = win32utils.get_home_location()
175
    else:
176
        base = osutils.path_from_environ('XDG_CACHE_HOME')
177
        if base is None:
178
            base = osutils.pathjoin(osutils._get_home_dir(), ".cache")
179
180
    cache_dir = osutils.pathjoin(base, "breezy")
181
182
    # GZ 2019-06-15: Move responsibility for ensuring dir exists elsewhere?
183
    if not os.path.exists(cache_dir):
184
        os.makedirs(cache_dir)
185
186
    return cache_dir
187
188
189
def _get_default_mail_domain(mailname_file='/etc/mailname'):
190
    """If possible, return the assumed default email domain.
191
192
    :returns: string mail domain, or None.
193
    """
194
    if sys.platform == 'win32':
195
        # No implementation yet; patches welcome
196
        return None
197
    try:
198
        f = open(mailname_file)
199
    except (IOError, OSError):
200
        return None
201
    try:
202
        domain = f.readline().strip()
203
        return domain
204
    finally:
205
        f.close()
206
207
208
def default_email():
209
    v = os.environ.get('BRZ_EMAIL')
210
    if v:
211
        return v
212
    v = os.environ.get('EMAIL')
213
    if v:
214
        return v
215
    name, email = _auto_user_id()
216
    if name and email:
217
        return u'%s <%s>' % (name, email)
218
    elif email:
219
        return email
220
    raise errors.NoWhoami()
221
222
223
def _auto_user_id():
224
    """Calculate automatic user identification.
225
226
    :returns: (realname, email), either of which may be None if they can't be
227
    determined.
228
229
    Only used when none is set in the environment or the id file.
230
231
    This only returns an email address if we can be fairly sure the
232
    address is reasonable, ie if /etc/mailname is set on unix.
233
234
    This doesn't use the FQDN as the default domain because that may be
235
    slow, and it doesn't use the hostname alone because that's not normally
236
    a reasonable address.
237
    """
238
    if sys.platform == 'win32':
239
        # No implementation to reliably determine Windows default mail
240
        # address; please add one.
241
        return None, None
242
243
    default_mail_domain = _get_default_mail_domain()
244
    if not default_mail_domain:
245
        return None, None
246
247
    import pwd
248
    uid = os.getuid()
249
    try:
250
        w = pwd.getpwuid(uid)
251
    except KeyError:
252
        trace.mutter('no passwd entry for uid %d?' % uid)
253
        return None, None
254
255
    # we try utf-8 first, because on many variants (like Linux),
256
    # /etc/passwd "should" be in utf-8, and because it's unlikely to give
257
    # false positives.  (many users will have their user encoding set to
258
    # latin-1, which cannot raise UnicodeError.)
259
    gecos = w.pw_gecos
260
    if isinstance(gecos, bytes):
261
        try:
262
            gecos = gecos.decode('utf-8')
263
            encoding = 'utf-8'
264
        except UnicodeError:
265
            try:
266
                encoding = osutils.get_user_encoding()
267
                gecos = gecos.decode(encoding)
268
            except UnicodeError:
269
                trace.mutter("cannot decode passwd entry %s" % w)
270
                return None, None
271
272
    username = w.pw_name
273
    if isinstance(username, bytes):
274
        try:
275
            username = username.decode(encoding)
276
        except UnicodeError:
277
            trace.mutter("cannot decode passwd entry %s" % w)
278
            return None, None
279
280
    comma = gecos.find(',')
281
    if comma == -1:
282
        realname = gecos
283
    else:
284
        realname = gecos[:comma]
285
286
    return realname, (username + '@' + default_mail_domain)