1
# Copyright (C) 2005, 2007, 2008 Canonical Ltd
2
# Authors: Robert Collins <robert.collins@canonical.com>
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 2 of the License, or
8
# (at your option) any later version.
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
"""Configuration that affects the behaviour of Bazaar.
21
Currently this configuration resides in ~/.bazaar/bazaar.conf
22
and ~/.bazaar/locations.conf, which is written to by bzr.
24
In bazaar.conf the following options may be set:
26
editor=name-of-program
27
email=Your Name <your@email.address>
28
check_signatures=require|ignore|check-available(default)
29
create_signatures=always|never|when-required(default)
30
gpg_signing_command=name-of-program
31
log_format=name-of-format
33
in locations.conf, you specify the url of a branch and options for it.
34
Wildcards may be used - * and ? as normal in shell completion. Options
35
set in both bazaar.conf and locations.conf are overridden by the locations.conf
37
[/home/robertc/source]
38
recurse=False|True(default)
40
check_signatures= as above
41
create_signatures= as above.
43
explanation of options
44
----------------------
45
editor - this option sets the pop up editor to use during commits.
46
email - this option sets the user id bzr will use when committing.
47
check_signatures - this option controls whether bzr will require good gpg
48
signatures, ignore them, or check them if they are
50
create_signatures - this option controls whether bzr will always create
51
gpg signatures, never create them, or create them if the
52
branch is configured to require them.
53
log_format - this option sets the default log format. Possible values are
54
long, short, line, or a plugin can register new formats.
56
In bazaar.conf you can also define aliases in the ALIASES sections, example
59
lastlog=log --line -r-10..-1
60
ll=log --line -r-10..-1
68
from bzrlib.lazy_import import lazy_import
69
lazy_import(globals(), """
71
from fnmatch import fnmatch
73
from cStringIO import StringIO
88
from bzrlib.util.configobj import configobj
104
POLICY_APPENDPATH = 2
108
POLICY_NORECURSE: 'norecurse',
109
POLICY_APPENDPATH: 'appendpath',
114
'norecurse': POLICY_NORECURSE,
115
'appendpath': POLICY_APPENDPATH,
119
STORE_LOCATION = POLICY_NONE
120
STORE_LOCATION_NORECURSE = POLICY_NORECURSE
121
STORE_LOCATION_APPENDPATH = POLICY_APPENDPATH
126
def ConfigObj(*args, **kwargs):
128
if _ConfigObj is None:
129
class ConfigObj(configobj.ConfigObj):
131
def get_bool(self, section, key):
132
return self[section].as_bool(key)
134
def get_value(self, section, name):
135
# Try [] for the old DEFAULT section.
136
if section == "DEFAULT":
141
return self[section][name]
142
_ConfigObj = ConfigObj
143
return _ConfigObj(*args, **kwargs)
146
class Config(object):
147
"""A configuration policy - what username, editor, gpg needs etc."""
150
super(Config, self).__init__()
152
def get_editor(self):
153
"""Get the users pop up editor."""
154
raise NotImplementedError
156
def get_change_editor(self, old_tree, new_tree):
157
from bzrlib import diff
158
cmd = self._get_change_editor()
161
return diff.DiffFromTool.from_string(cmd, old_tree, new_tree,
165
def get_mail_client(self):
166
"""Get a mail client to use"""
167
selected_client = self.get_user_option('mail_client')
168
_registry = mail_client.mail_client_registry
170
mail_client_class = _registry.get(selected_client)
172
raise errors.UnknownMailClient(selected_client)
173
return mail_client_class(self)
175
def _get_signature_checking(self):
176
"""Template method to override signature checking policy."""
178
def _get_signing_policy(self):
179
"""Template method to override signature creation policy."""
181
def _get_user_option(self, option_name):
182
"""Template method to provide a user option."""
185
def get_user_option(self, option_name):
186
"""Get a generic option - no special process, no default."""
187
return self._get_user_option(option_name)
189
def get_user_option_as_bool(self, option_name):
190
"""Get a generic option as a boolean - no special process, no default.
192
:return None if the option doesn't exist or its value can't be
193
interpreted as a boolean. Returns True or False ortherwise.
195
s = self._get_user_option(option_name)
196
return ui.bool_from_string(s)
198
def gpg_signing_command(self):
199
"""What program should be used to sign signatures?"""
200
result = self._gpg_signing_command()
205
def _gpg_signing_command(self):
206
"""See gpg_signing_command()."""
209
def log_format(self):
210
"""What log format should be used"""
211
result = self._log_format()
216
def _log_format(self):
217
"""See log_format()."""
220
def post_commit(self):
221
"""An ordered list of python functions to call.
223
Each function takes branch, rev_id as parameters.
225
return self._post_commit()
227
def _post_commit(self):
228
"""See Config.post_commit."""
231
def user_email(self):
232
"""Return just the email component of a username."""
233
return extract_email_address(self.username())
236
"""Return email-style username.
238
Something similar to 'Martin Pool <mbp@sourcefrog.net>'
240
$BZR_EMAIL can be set to override this (as well as the
241
deprecated $BZREMAIL), then
242
the concrete policy type is checked, and finally
244
If none is found, a reasonable default is (hopefully)
247
TODO: Check it's reasonably well-formed.
249
v = os.environ.get('BZR_EMAIL')
251
return v.decode(osutils.get_user_encoding())
253
v = self._get_user_id()
257
v = os.environ.get('EMAIL')
259
return v.decode(osutils.get_user_encoding())
261
name, email = _auto_user_id()
263
return '%s <%s>' % (name, email)
267
def signature_checking(self):
268
"""What is the current policy for signature checking?."""
269
policy = self._get_signature_checking()
270
if policy is not None:
272
return CHECK_IF_POSSIBLE
274
def signing_policy(self):
275
"""What is the current policy for signature checking?."""
276
policy = self._get_signing_policy()
277
if policy is not None:
279
return SIGN_WHEN_REQUIRED
281
def signature_needed(self):
282
"""Is a signature needed when committing ?."""
283
policy = self._get_signing_policy()
285
policy = self._get_signature_checking()
286
if policy is not None:
287
trace.warning("Please use create_signatures,"
288
" not check_signatures to set signing policy.")
289
if policy == CHECK_ALWAYS:
291
elif policy == SIGN_ALWAYS:
295
def get_alias(self, value):
296
return self._get_alias(value)
298
def _get_alias(self, value):
301
def get_nickname(self):
302
return self._get_nickname()
304
def _get_nickname(self):
307
def get_bzr_remote_path(self):
309
return os.environ['BZR_REMOTE_PATH']
311
path = self.get_user_option("bzr_remote_path")
317
class IniBasedConfig(Config):
318
"""A configuration policy that draws from ini files."""
320
def __init__(self, get_filename):
321
super(IniBasedConfig, self).__init__()
322
self._get_filename = get_filename
325
def _get_parser(self, file=None):
326
if self._parser is not None:
329
input = self._get_filename()
333
self._parser = ConfigObj(input, encoding='utf-8',
334
options={'interpolation': False})
335
except configobj.ConfigObjError, e:
336
raise errors.ParseConfigError(e.errors, e.config.filename)
339
def _get_matching_sections(self):
340
"""Return an ordered list of (section_name, extra_path) pairs.
342
If the section contains inherited configuration, extra_path is
343
a string containing the additional path components.
345
section = self._get_section()
346
if section is not None:
347
return [(section, '')]
351
def _get_section(self):
352
"""Override this to define the section used by the config."""
355
def _get_option_policy(self, section, option_name):
356
"""Return the policy for the given (section, option_name) pair."""
359
def _get_change_editor(self):
360
return self.get_user_option('change_editor')
362
def _get_signature_checking(self):
363
"""See Config._get_signature_checking."""
364
policy = self._get_user_option('check_signatures')
366
return self._string_to_signature_policy(policy)
368
def _get_signing_policy(self):
369
"""See Config._get_signing_policy"""
370
policy = self._get_user_option('create_signatures')
372
return self._string_to_signing_policy(policy)
374
def _get_user_id(self):
375
"""Get the user id from the 'email' key in the current section."""
376
return self._get_user_option('email')
378
def _get_user_option(self, option_name):
379
"""See Config._get_user_option."""
380
for (section, extra_path) in self._get_matching_sections():
382
value = self._get_parser().get_value(section, option_name)
385
policy = self._get_option_policy(section, option_name)
386
if policy == POLICY_NONE:
388
elif policy == POLICY_NORECURSE:
389
# norecurse items only apply to the exact path
394
elif policy == POLICY_APPENDPATH:
396
value = urlutils.join(value, extra_path)
399
raise AssertionError('Unexpected config policy %r' % policy)
403
def _gpg_signing_command(self):
404
"""See Config.gpg_signing_command."""
405
return self._get_user_option('gpg_signing_command')
407
def _log_format(self):
408
"""See Config.log_format."""
409
return self._get_user_option('log_format')
411
def _post_commit(self):
412
"""See Config.post_commit."""
413
return self._get_user_option('post_commit')
415
def _string_to_signature_policy(self, signature_string):
416
"""Convert a string to a signing policy."""
417
if signature_string.lower() == 'check-available':
418
return CHECK_IF_POSSIBLE
419
if signature_string.lower() == 'ignore':
421
if signature_string.lower() == 'require':
423
raise errors.BzrError("Invalid signatures policy '%s'"
426
def _string_to_signing_policy(self, signature_string):
427
"""Convert a string to a signing policy."""
428
if signature_string.lower() == 'when-required':
429
return SIGN_WHEN_REQUIRED
430
if signature_string.lower() == 'never':
432
if signature_string.lower() == 'always':
434
raise errors.BzrError("Invalid signing policy '%s'"
437
def _get_alias(self, value):
439
return self._get_parser().get_value("ALIASES",
444
def _get_nickname(self):
445
return self.get_user_option('nickname')
448
class GlobalConfig(IniBasedConfig):
449
"""The configuration that should be used for a specific location."""
451
def get_editor(self):
452
return self._get_user_option('editor')
455
super(GlobalConfig, self).__init__(config_filename)
457
def set_user_option(self, option, value):
458
"""Save option and its value in the configuration."""
459
self._set_option(option, value, 'DEFAULT')
461
def get_aliases(self):
462
"""Return the aliases section."""
463
if 'ALIASES' in self._get_parser():
464
return self._get_parser()['ALIASES']
468
def set_alias(self, alias_name, alias_command):
469
"""Save the alias in the configuration."""
470
self._set_option(alias_name, alias_command, 'ALIASES')
472
def unset_alias(self, alias_name):
473
"""Unset an existing alias."""
474
aliases = self._get_parser().get('ALIASES')
475
if not aliases or alias_name not in aliases:
476
raise errors.NoSuchAlias(alias_name)
477
del aliases[alias_name]
478
self._write_config_file()
480
def _set_option(self, option, value, section):
481
# FIXME: RBC 20051029 This should refresh the parser and also take a
482
# file lock on bazaar.conf.
483
conf_dir = os.path.dirname(self._get_filename())
484
ensure_config_dir_exists(conf_dir)
485
self._get_parser().setdefault(section, {})[option] = value
486
self._write_config_file()
488
def _write_config_file(self):
489
f = open(self._get_filename(), 'wb')
490
self._get_parser().write(f)
494
class LocationConfig(IniBasedConfig):
495
"""A configuration object that gives the policy for a location."""
497
def __init__(self, location):
498
name_generator = locations_config_filename
499
if (not os.path.exists(name_generator()) and
500
os.path.exists(branches_config_filename())):
501
if sys.platform == 'win32':
502
trace.warning('Please rename %s to %s'
503
% (branches_config_filename(),
504
locations_config_filename()))
506
trace.warning('Please rename ~/.bazaar/branches.conf'
507
' to ~/.bazaar/locations.conf')
508
name_generator = branches_config_filename
509
super(LocationConfig, self).__init__(name_generator)
510
# local file locations are looked up by local path, rather than
511
# by file url. This is because the config file is a user
512
# file, and we would rather not expose the user to file urls.
513
if location.startswith('file://'):
514
location = urlutils.local_path_from_url(location)
515
self.location = location
517
def _get_matching_sections(self):
518
"""Return an ordered list of section names matching this location."""
519
sections = self._get_parser()
520
location_names = self.location.split('/')
521
if self.location.endswith('/'):
522
del location_names[-1]
524
for section in sections:
525
# location is a local path if possible, so we need
526
# to convert 'file://' urls to local paths if necessary.
527
# This also avoids having file:///path be a more exact
528
# match than '/path'.
529
if section.startswith('file://'):
530
section_path = urlutils.local_path_from_url(section)
532
section_path = section
533
section_names = section_path.split('/')
534
if section.endswith('/'):
535
del section_names[-1]
536
names = zip(location_names, section_names)
539
if not fnmatch(name[0], name[1]):
544
# so, for the common prefix they matched.
545
# if section is longer, no match.
546
if len(section_names) > len(location_names):
548
matches.append((len(section_names), section,
549
'/'.join(location_names[len(section_names):])))
550
matches.sort(reverse=True)
552
for (length, section, extra_path) in matches:
553
sections.append((section, extra_path))
554
# should we stop looking for parent configs here?
556
if self._get_parser()[section].as_bool('ignore_parents'):
562
def _get_option_policy(self, section, option_name):
563
"""Return the policy for the given (section, option_name) pair."""
564
# check for the old 'recurse=False' flag
566
recurse = self._get_parser()[section].as_bool('recurse')
570
return POLICY_NORECURSE
572
policy_key = option_name + ':policy'
574
policy_name = self._get_parser()[section][policy_key]
578
return _policy_value[policy_name]
580
def _set_option_policy(self, section, option_name, option_policy):
581
"""Set the policy for the given option name in the given section."""
582
# The old recurse=False option affects all options in the
583
# section. To handle multiple policies in the section, we
584
# need to convert it to a policy_norecurse key.
586
recurse = self._get_parser()[section].as_bool('recurse')
590
symbol_versioning.warn(
591
'The recurse option is deprecated as of 0.14. '
592
'The section "%s" has been converted to use policies.'
595
del self._get_parser()[section]['recurse']
597
for key in self._get_parser()[section].keys():
598
if not key.endswith(':policy'):
599
self._get_parser()[section][key +
600
':policy'] = 'norecurse'
602
policy_key = option_name + ':policy'
603
policy_name = _policy_name[option_policy]
604
if policy_name is not None:
605
self._get_parser()[section][policy_key] = policy_name
607
if policy_key in self._get_parser()[section]:
608
del self._get_parser()[section][policy_key]
610
def set_user_option(self, option, value, store=STORE_LOCATION):
611
"""Save option and its value in the configuration."""
612
if store not in [STORE_LOCATION,
613
STORE_LOCATION_NORECURSE,
614
STORE_LOCATION_APPENDPATH]:
615
raise ValueError('bad storage policy %r for %r' %
617
# FIXME: RBC 20051029 This should refresh the parser and also take a
618
# file lock on locations.conf.
619
conf_dir = os.path.dirname(self._get_filename())
620
ensure_config_dir_exists(conf_dir)
621
location = self.location
622
if location.endswith('/'):
623
location = location[:-1]
624
if (not location in self._get_parser() and
625
not location + '/' in self._get_parser()):
626
self._get_parser()[location]={}
627
elif location + '/' in self._get_parser():
628
location = location + '/'
629
self._get_parser()[location][option]=value
630
# the allowed values of store match the config policies
631
self._set_option_policy(location, option, store)
632
self._get_parser().write(file(self._get_filename(), 'wb'))
635
class BranchConfig(Config):
636
"""A configuration object giving the policy for a branch."""
638
def _get_branch_data_config(self):
639
if self._branch_data_config is None:
640
self._branch_data_config = TreeConfig(self.branch)
641
return self._branch_data_config
643
def _get_location_config(self):
644
if self._location_config is None:
645
self._location_config = LocationConfig(self.branch.base)
646
return self._location_config
648
def _get_global_config(self):
649
if self._global_config is None:
650
self._global_config = GlobalConfig()
651
return self._global_config
653
def _get_best_value(self, option_name):
654
"""This returns a user option from local, tree or global config.
656
They are tried in that order. Use get_safe_value if trusted values
659
for source in self.option_sources:
660
value = getattr(source(), option_name)()
661
if value is not None:
665
def _get_safe_value(self, option_name):
666
"""This variant of get_best_value never returns untrusted values.
668
It does not return values from the branch data, because the branch may
669
not be controlled by the user.
671
We may wish to allow locations.conf to control whether branches are
672
trusted in the future.
674
for source in (self._get_location_config, self._get_global_config):
675
value = getattr(source(), option_name)()
676
if value is not None:
680
def _get_user_id(self):
681
"""Return the full user id for the branch.
683
e.g. "John Hacker <jhacker@example.com>"
684
This is looked up in the email controlfile for the branch.
687
return (self.branch._transport.get_bytes("email")
688
.decode(osutils.get_user_encoding())
690
except errors.NoSuchFile, e:
693
return self._get_best_value('_get_user_id')
695
def _get_change_editor(self):
696
return self._get_best_value('_get_change_editor')
698
def _get_signature_checking(self):
699
"""See Config._get_signature_checking."""
700
return self._get_best_value('_get_signature_checking')
702
def _get_signing_policy(self):
703
"""See Config._get_signing_policy."""
704
return self._get_best_value('_get_signing_policy')
706
def _get_user_option(self, option_name):
707
"""See Config._get_user_option."""
708
for source in self.option_sources:
709
value = source()._get_user_option(option_name)
710
if value is not None:
714
def set_user_option(self, name, value, store=STORE_BRANCH,
716
if store == STORE_BRANCH:
717
self._get_branch_data_config().set_option(value, name)
718
elif store == STORE_GLOBAL:
719
self._get_global_config().set_user_option(name, value)
721
self._get_location_config().set_user_option(name, value, store)
724
if store in (STORE_GLOBAL, STORE_BRANCH):
725
mask_value = self._get_location_config().get_user_option(name)
726
if mask_value is not None:
727
trace.warning('Value "%s" is masked by "%s" from'
728
' locations.conf', value, mask_value)
730
if store == STORE_GLOBAL:
731
branch_config = self._get_branch_data_config()
732
mask_value = branch_config.get_user_option(name)
733
if mask_value is not None:
734
trace.warning('Value "%s" is masked by "%s" from'
735
' branch.conf', value, mask_value)
737
def _gpg_signing_command(self):
738
"""See Config.gpg_signing_command."""
739
return self._get_safe_value('_gpg_signing_command')
741
def __init__(self, branch):
742
super(BranchConfig, self).__init__()
743
self._location_config = None
744
self._branch_data_config = None
745
self._global_config = None
747
self.option_sources = (self._get_location_config,
748
self._get_branch_data_config,
749
self._get_global_config)
751
def _post_commit(self):
752
"""See Config.post_commit."""
753
return self._get_safe_value('_post_commit')
755
def _get_nickname(self):
756
value = self._get_explicit_nickname()
757
if value is not None:
759
return urlutils.unescape(self.branch.base.split('/')[-2])
761
def has_explicit_nickname(self):
762
"""Return true if a nickname has been explicitly assigned."""
763
return self._get_explicit_nickname() is not None
765
def _get_explicit_nickname(self):
766
return self._get_best_value('_get_nickname')
768
def _log_format(self):
769
"""See Config.log_format."""
770
return self._get_best_value('_log_format')
773
def ensure_config_dir_exists(path=None):
774
"""Make sure a configuration directory exists.
775
This makes sure that the directory exists.
776
On windows, since configuration directories are 2 levels deep,
777
it makes sure both the directory and the parent directory exists.
781
if not os.path.isdir(path):
782
if sys.platform == 'win32':
783
parent_dir = os.path.dirname(path)
784
if not os.path.isdir(parent_dir):
785
trace.mutter('creating config parent directory: %r', parent_dir)
787
trace.mutter('creating config directory: %r', path)
792
"""Return per-user configuration directory.
794
By default this is ~/.bazaar/
796
TODO: Global option --config-dir to override this.
798
base = os.environ.get('BZR_HOME', None)
799
if sys.platform == 'win32':
801
base = win32utils.get_appdata_location_unicode()
803
base = os.environ.get('HOME', None)
805
raise errors.BzrError('You must have one of BZR_HOME, APPDATA,'
807
return osutils.pathjoin(base, 'bazaar', '2.0')
809
# cygwin, linux, and darwin all have a $HOME directory
811
base = os.path.expanduser("~")
812
return osutils.pathjoin(base, ".bazaar")
815
def config_filename():
816
"""Return per-user configuration ini file filename."""
817
return osutils.pathjoin(config_dir(), 'bazaar.conf')
820
def branches_config_filename():
821
"""Return per-user configuration ini file filename."""
822
return osutils.pathjoin(config_dir(), 'branches.conf')
825
def locations_config_filename():
826
"""Return per-user configuration ini file filename."""
827
return osutils.pathjoin(config_dir(), 'locations.conf')
830
def authentication_config_filename():
831
"""Return per-user authentication ini file filename."""
832
return osutils.pathjoin(config_dir(), 'authentication.conf')
835
def user_ignore_config_filename():
836
"""Return the user default ignore filename"""
837
return osutils.pathjoin(config_dir(), 'ignore')
841
"""Return the directory name to store crash files.
843
This doesn't implicitly create it.
845
On Windows it's in the config directory; elsewhere in the XDG cache directory.
847
if sys.platform == 'win32':
848
return osutils.pathjoin(config_dir(), 'Crash')
850
return osutils.pathjoin(xdg_cache_dir(), 'crash')
854
# See http://standards.freedesktop.org/basedir-spec/latest/ar01s03.html
855
# Possibly this should be different on Windows?
856
e = os.environ.get('XDG_CACHE_DIR', None)
860
return os.path.expanduser('~/.cache')
864
"""Calculate automatic user identification.
866
Returns (realname, email).
868
Only used when none is set in the environment or the id file.
870
This previously used the FQDN as the default domain, but that can
871
be very slow on machines where DNS is broken. So now we simply
876
if sys.platform == 'win32':
877
name = win32utils.get_user_name_unicode()
879
raise errors.BzrError("Cannot autodetect user name.\n"
880
"Please, set your name with command like:\n"
881
'bzr whoami "Your Name <name@domain.com>"')
882
host = win32utils.get_host_name_unicode()
884
host = socket.gethostname()
885
return name, (name + '@' + host)
891
w = pwd.getpwuid(uid)
893
raise errors.BzrCommandError('Unable to determine your name. '
894
'Please use "bzr whoami" to set it.')
896
# we try utf-8 first, because on many variants (like Linux),
897
# /etc/passwd "should" be in utf-8, and because it's unlikely to give
898
# false positives. (many users will have their user encoding set to
899
# latin-1, which cannot raise UnicodeError.)
901
gecos = w.pw_gecos.decode('utf-8')
905
encoding = osutils.get_user_encoding()
906
gecos = w.pw_gecos.decode(encoding)
908
raise errors.BzrCommandError('Unable to determine your name. '
909
'Use "bzr whoami" to set it.')
911
username = w.pw_name.decode(encoding)
913
raise errors.BzrCommandError('Unable to determine your name. '
914
'Use "bzr whoami" to set it.')
916
comma = gecos.find(',')
920
realname = gecos[:comma]
927
user_encoding = osutils.get_user_encoding()
928
realname = username = getpass.getuser().decode(user_encoding)
929
except UnicodeDecodeError:
930
raise errors.BzrError("Can't decode username as %s." % \
933
return realname, (username + '@' + socket.gethostname())
936
def parse_username(username):
937
"""Parse e-mail username and return a (name, address) tuple."""
938
match = re.match(r'(.*?)\s*<?([\w+.-]+@[\w+.-]+)>?', username)
940
return (username, '')
942
return (match.group(1), match.group(2))
945
def extract_email_address(e):
946
"""Return just the address part of an email string.
948
That is just the user@domain part, nothing else.
949
This part is required to contain only ascii characters.
950
If it can't be extracted, raises an error.
952
>>> extract_email_address('Jane Tester <jane@test.com>')
955
name, email = parse_username(e)
957
raise errors.NoEmailInUsername(e)
961
class TreeConfig(IniBasedConfig):
962
"""Branch configuration data associated with its contents, not location"""
964
# XXX: Really needs a better name, as this is not part of the tree! -- mbp 20080507
966
def __init__(self, branch):
967
self._config = branch._get_config()
970
def _get_parser(self, file=None):
972
return IniBasedConfig._get_parser(file)
973
return self._config._get_configobj()
975
def get_option(self, name, section=None, default=None):
976
self.branch.lock_read()
978
return self._config.get_option(name, section, default)
982
def set_option(self, value, name, section=None):
983
"""Set a per-branch configuration option"""
984
self.branch.lock_write()
986
self._config.set_option(value, name, section)
991
class AuthenticationConfig(object):
992
"""The authentication configuration file based on a ini file.
994
Implements the authentication.conf file described in
995
doc/developers/authentication-ring.txt.
998
def __init__(self, _file=None):
999
self._config = None # The ConfigObj
1001
self._filename = authentication_config_filename()
1002
self._input = self._filename = authentication_config_filename()
1004
# Tests can provide a string as _file
1005
self._filename = None
1008
def _get_config(self):
1009
if self._config is not None:
1012
# FIXME: Should we validate something here ? Includes: empty
1013
# sections are useless, at least one of
1014
# user/password/password_encoding should be defined, etc.
1016
# Note: the encoding below declares that the file itself is utf-8
1017
# encoded, but the values in the ConfigObj are always Unicode.
1018
self._config = ConfigObj(self._input, encoding='utf-8')
1019
except configobj.ConfigObjError, e:
1020
raise errors.ParseConfigError(e.errors, e.config.filename)
1024
"""Save the config file, only tests should use it for now."""
1025
conf_dir = os.path.dirname(self._filename)
1026
ensure_config_dir_exists(conf_dir)
1027
self._get_config().write(file(self._filename, 'wb'))
1029
def _set_option(self, section_name, option_name, value):
1030
"""Set an authentication configuration option"""
1031
conf = self._get_config()
1032
section = conf.get(section_name)
1035
section = conf[section]
1036
section[option_name] = value
1039
def get_credentials(self, scheme, host, port=None, user=None, path=None,
1041
"""Returns the matching credentials from authentication.conf file.
1043
:param scheme: protocol
1045
:param host: the server address
1047
:param port: the associated port (optional)
1049
:param user: login (optional)
1051
:param path: the absolute path on the server (optional)
1053
:param realm: the http authentication realm (optional)
1055
:return: A dict containing the matching credentials or None.
1057
- name: the section name of the credentials in the
1058
authentication.conf file,
1059
- user: can't be different from the provided user if any,
1060
- scheme: the server protocol,
1061
- host: the server address,
1062
- port: the server port (can be None),
1063
- path: the absolute server path (can be None),
1064
- realm: the http specific authentication realm (can be None),
1065
- password: the decoded password, could be None if the credential
1066
defines only the user
1067
- verify_certificates: https specific, True if the server
1068
certificate should be verified, False otherwise.
1071
for auth_def_name, auth_def in self._get_config().items():
1072
if type(auth_def) is not configobj.Section:
1073
raise ValueError("%s defined outside a section" % auth_def_name)
1075
a_scheme, a_host, a_user, a_path = map(
1076
auth_def.get, ['scheme', 'host', 'user', 'path'])
1079
a_port = auth_def.as_int('port')
1083
raise ValueError("'port' not numeric in %s" % auth_def_name)
1085
a_verify_certificates = auth_def.as_bool('verify_certificates')
1087
a_verify_certificates = True
1090
"'verify_certificates' not boolean in %s" % auth_def_name)
1093
if a_scheme is not None and scheme != a_scheme:
1095
if a_host is not None:
1096
if not (host == a_host
1097
or (a_host.startswith('.') and host.endswith(a_host))):
1099
if a_port is not None and port != a_port:
1101
if (a_path is not None and path is not None
1102
and not path.startswith(a_path)):
1104
if (a_user is not None and user is not None
1105
and a_user != user):
1106
# Never contradict the caller about the user to be used
1111
# Prepare a credentials dictionary with additional keys
1112
# for the credential providers
1113
credentials = dict(name=auth_def_name,
1120
password=auth_def.get('password', None),
1121
verify_certificates=a_verify_certificates)
1122
# Decode the password in the credentials (or get one)
1123
self.decode_password(credentials,
1124
auth_def.get('password_encoding', None))
1125
if 'auth' in debug.debug_flags:
1126
trace.mutter("Using authentication section: %r", auth_def_name)
1129
if credentials is None:
1130
# No credentials were found in authentication.conf, try the fallback
1131
# credentials stores.
1132
credentials = credential_store_registry.get_fallback_credentials(
1133
scheme, host, port, user, path, realm)
1137
def set_credentials(self, name, host, user, scheme=None, password=None,
1138
port=None, path=None, verify_certificates=None,
1140
"""Set authentication credentials for a host.
1142
Any existing credentials with matching scheme, host, port and path
1143
will be deleted, regardless of name.
1145
:param name: An arbitrary name to describe this set of credentials.
1146
:param host: Name of the host that accepts these credentials.
1147
:param user: The username portion of these credentials.
1148
:param scheme: The URL scheme (e.g. ssh, http) the credentials apply
1150
:param password: Password portion of these credentials.
1151
:param port: The IP port on the host that these credentials apply to.
1152
:param path: A filesystem path on the host that these credentials
1154
:param verify_certificates: On https, verify server certificates if
1156
:param realm: The http authentication realm (optional).
1158
values = {'host': host, 'user': user}
1159
if password is not None:
1160
values['password'] = password
1161
if scheme is not None:
1162
values['scheme'] = scheme
1163
if port is not None:
1164
values['port'] = '%d' % port
1165
if path is not None:
1166
values['path'] = path
1167
if verify_certificates is not None:
1168
values['verify_certificates'] = str(verify_certificates)
1169
if realm is not None:
1170
values['realm'] = realm
1171
config = self._get_config()
1173
for section, existing_values in config.items():
1174
for key in ('scheme', 'host', 'port', 'path', 'realm'):
1175
if existing_values.get(key) != values.get(key):
1179
config.update({name: values})
1182
def get_user(self, scheme, host, port=None, realm=None, path=None,
1183
prompt=None, ask=False, default=None):
1184
"""Get a user from authentication file.
1186
:param scheme: protocol
1188
:param host: the server address
1190
:param port: the associated port (optional)
1192
:param realm: the realm sent by the server (optional)
1194
:param path: the absolute path on the server (optional)
1196
:param ask: Ask the user if there is no explicitly configured username
1199
:param default: The username returned if none is defined (optional).
1201
:return: The found user.
1203
credentials = self.get_credentials(scheme, host, port, user=None,
1204
path=path, realm=realm)
1205
if credentials is not None:
1206
user = credentials['user']
1212
# Create a default prompt suitable for most cases
1213
prompt = scheme.upper() + ' %(host)s username'
1214
# Special handling for optional fields in the prompt
1215
if port is not None:
1216
prompt_host = '%s:%d' % (host, port)
1219
user = ui.ui_factory.get_username(prompt, host=prompt_host)
1224
def get_password(self, scheme, host, user, port=None,
1225
realm=None, path=None, prompt=None):
1226
"""Get a password from authentication file or prompt the user for one.
1228
:param scheme: protocol
1230
:param host: the server address
1232
:param port: the associated port (optional)
1236
:param realm: the realm sent by the server (optional)
1238
:param path: the absolute path on the server (optional)
1240
:return: The found password or the one entered by the user.
1242
credentials = self.get_credentials(scheme, host, port, user, path,
1244
if credentials is not None:
1245
password = credentials['password']
1246
if password is not None and scheme is 'ssh':
1247
trace.warning('password ignored in section [%s],'
1248
' use an ssh agent instead'
1249
% credentials['name'])
1253
# Prompt user only if we could't find a password
1254
if password is None:
1256
# Create a default prompt suitable for most cases
1257
prompt = '%s' % scheme.upper() + ' %(user)s@%(host)s password'
1258
# Special handling for optional fields in the prompt
1259
if port is not None:
1260
prompt_host = '%s:%d' % (host, port)
1263
password = ui.ui_factory.get_password(prompt,
1264
host=prompt_host, user=user)
1267
def decode_password(self, credentials, encoding):
1269
cs = credential_store_registry.get_credential_store(encoding)
1271
raise ValueError('%r is not a known password_encoding' % encoding)
1272
credentials['password'] = cs.decode_password(credentials)
1276
class CredentialStoreRegistry(registry.Registry):
1277
"""A class that registers credential stores.
1279
A credential store provides access to credentials via the password_encoding
1280
field in authentication.conf sections.
1282
Except for stores provided by bzr itself, most stores are expected to be
1283
provided by plugins that will therefore use
1284
register_lazy(password_encoding, module_name, member_name, help=help,
1285
fallback=fallback) to install themselves.
1287
A fallback credential store is one that is queried if no credentials can be
1288
found via authentication.conf.
1291
def get_credential_store(self, encoding=None):
1292
cs = self.get(encoding)
1297
def is_fallback(self, name):
1298
"""Check if the named credentials store should be used as fallback."""
1299
return self.get_info(name)
1301
def get_fallback_credentials(self, scheme, host, port=None, user=None,
1302
path=None, realm=None):
1303
"""Request credentials from all fallback credentials stores.
1305
The first credentials store that can provide credentials wins.
1308
for name in self.keys():
1309
if not self.is_fallback(name):
1311
cs = self.get_credential_store(name)
1312
credentials = cs.get_credentials(scheme, host, port, user,
1314
if credentials is not None:
1315
# We found some credentials
1319
def register(self, key, obj, help=None, override_existing=False,
1321
"""Register a new object to a name.
1323
:param key: This is the key to use to request the object later.
1324
:param obj: The object to register.
1325
:param help: Help text for this entry. This may be a string or
1326
a callable. If it is a callable, it should take two
1327
parameters (registry, key): this registry and the key that
1328
the help was registered under.
1329
:param override_existing: Raise KeyErorr if False and something has
1330
already been registered for that key. If True, ignore if there
1331
is an existing key (always register the new value).
1332
:param fallback: Whether this credential store should be
1335
return super(CredentialStoreRegistry,
1336
self).register(key, obj, help, info=fallback,
1337
override_existing=override_existing)
1339
def register_lazy(self, key, module_name, member_name,
1340
help=None, override_existing=False,
1342
"""Register a new credential store to be loaded on request.
1344
:param module_name: The python path to the module. Such as 'os.path'.
1345
:param member_name: The member of the module to return. If empty or
1346
None, get() will return the module itself.
1347
:param help: Help text for this entry. This may be a string or
1349
:param override_existing: If True, replace the existing object
1350
with the new one. If False, if there is already something
1351
registered with the same key, raise a KeyError
1352
:param fallback: Whether this credential store should be
1355
return super(CredentialStoreRegistry, self).register_lazy(
1356
key, module_name, member_name, help,
1357
info=fallback, override_existing=override_existing)
1360
credential_store_registry = CredentialStoreRegistry()
1363
class CredentialStore(object):
1364
"""An abstract class to implement storage for credentials"""
1366
def decode_password(self, credentials):
1367
"""Returns a clear text password for the provided credentials."""
1368
raise NotImplementedError(self.decode_password)
1370
def get_credentials(self, scheme, host, port=None, user=None, path=None,
1372
"""Return the matching credentials from this credential store.
1374
This method is only called on fallback credential stores.
1376
raise NotImplementedError(self.get_credentials)
1380
class PlainTextCredentialStore(CredentialStore):
1381
"""Plain text credential store for the authentication.conf file."""
1383
def decode_password(self, credentials):
1384
"""See CredentialStore.decode_password."""
1385
return credentials['password']
1388
credential_store_registry.register('plain', PlainTextCredentialStore,
1389
help=PlainTextCredentialStore.__doc__)
1390
credential_store_registry.default_key = 'plain'
1393
class BzrDirConfig(object):
1395
def __init__(self, bzrdir):
1396
self._bzrdir = bzrdir
1397
self._config = bzrdir._get_config()
1399
def set_default_stack_on(self, value):
1400
"""Set the default stacking location.
1402
It may be set to a location, or None.
1404
This policy affects all branches contained by this bzrdir, except for
1405
those under repositories.
1407
if self._config is None:
1408
raise errors.BzrError("Cannot set configuration in %s" % self._bzrdir)
1410
self._config.set_option('', 'default_stack_on')
1412
self._config.set_option(value, 'default_stack_on')
1414
def get_default_stack_on(self):
1415
"""Return the default stacking location.
1417
This will either be a location, or None.
1419
This policy affects all branches contained by this bzrdir, except for
1420
those under repositories.
1422
if self._config is None:
1424
value = self._config.get_option('default_stack_on')
1430
class TransportConfig(object):
1431
"""A Config that reads/writes a config file on a Transport.
1433
It is a low-level object that considers config data to be name/value pairs
1434
that may be associated with a section. Assigning meaning to the these
1435
values is done at higher levels like TreeConfig.
1438
def __init__(self, transport, filename):
1439
self._transport = transport
1440
self._filename = filename
1442
def get_option(self, name, section=None, default=None):
1443
"""Return the value associated with a named option.
1445
:param name: The name of the value
1446
:param section: The section the option is in (if any)
1447
:param default: The value to return if the value is not set
1448
:return: The value or default value
1450
configobj = self._get_configobj()
1452
section_obj = configobj
1455
section_obj = configobj[section]
1458
return section_obj.get(name, default)
1460
def set_option(self, value, name, section=None):
1461
"""Set the value associated with a named option.
1463
:param value: The value to set
1464
:param name: The name of the value to set
1465
:param section: The section the option is in (if any)
1467
configobj = self._get_configobj()
1469
configobj[name] = value
1471
configobj.setdefault(section, {})[name] = value
1472
self._set_configobj(configobj)
1474
def _get_config_file(self):
1476
return self._transport.get(self._filename)
1477
except errors.NoSuchFile:
1480
def _get_configobj(self):
1481
return ConfigObj(self._get_config_file(), encoding='utf-8')
1483
def _set_configobj(self, configobj):
1484
out_file = StringIO()
1485
configobj.write(out_file)
1487
self._transport.put_file(self._filename, out_file)