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